我刚刚开始使用Gluon Mobile,我正在开发一个小型iOS应用程序。我设法使用ponistService更新UI标签上的用户位置。现在,即使该应用在后台模式下,我也想获取位置更新。由于Apple开发人员文档,这应该通过在应用程序plist
中添加以下键来工作。<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
在iPhone上部署该应用程序时,只要应用程序活动,我就可以看到更新。当进入主屏幕时,显示"停止更新位置"消息的更新停止和终端(gradle-> lainingiosdevice)。知道为什么我在后台模式下没有获得位置更新?
在此相关代码:
Services.get(PositionService.class).ifPresent(service -> {
service.positionProperty().addListener((obs, oldPos, newPos) -> {
posLbl.setText(String.format(" %.7f %.7fnLast update: " + LocalDateTime.now().toString(),
newPos.getLatitude(), newPos.getLongitude()));
handleData();
});
});
这是相关的PLIST条目:
<key>NSLocationAlwaysUsageDescription</key>
<string>A good reason.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>An even better reason.</string>
当应用程序进入后台模式时,位置服务不起作用的原因:
Services.get(LifecycleService.class).ifPresent(l -> {
l.addListener(LifecycleEvent.PAUSE, IOSPositionService::stopObserver);
l.addListener(LifecycleEvent.RESUME, IOSPositionService::startObserver);
});
startObserver();
生命周期服务旨在防止当应用在背景中以节省电池时进行不必要的事情。默认情况下,许多服务(包括位置或电池)使用它。
到目前为止,没有简单的方法可以删除听众,因为没有API可以启用或禁用其使用。如果您认为应该添加这一点,则可以在此处提出问题。
您可以使用自己的快照,然后使用自己的快照来分配符合魅力的存储库,删除相关代码,然后再次构建它,但这当然不是一个很好的长期解决方案。
目前,我唯一能想到的不修改的方法是避免包含iOS的生命周期服务实施。
这样做,一旦打开应用程序并实例化了位置服务,startObserver
将被调用并且永远不会停止(直到您关闭应用程序)。
在您的build.gradle
文件中,而不是使用downConfig
块来包含position
插件,而是在dependencies
块中执行,然后删除遍历依赖性到生命周期的OIOS:
dependencies {
compile 'com.gluonhq:charm:4.3.7'
compile 'com.gluonhq:charm-down-plugin-position:3.6.0'
iosRuntime('com.gluonhq:charm-down-plugin-position-ios:3.6.0') {
exclude group: 'com.gluonhq', module: 'charm-down-plugin-lifecycle-ios'
}
}
jfxmobile {
downConfig {
version = '3.6.0'
plugins 'display', 'statusbar', 'storage'
}
现在将其部署到您的iOS设备,并检查位置服务是否在后台模式下工作。
编辑
正如指出的那样,删除阻止观察者的生命周期听众还不够:该位置未在后台模式下更新。
解决此工作的解决方案需要修改iOS的位置服务,并构建本地快照。
这些是步骤(仅适用于Mac):
1。克隆/叉子魅力
Charm Down是一个可以在此处找到的开源库。
2。编辑iOS
的位置服务我们需要从IOSPositionService
(链接)发表评论或删除生命周期听众:
public IOSPositionService() {
position = new ReadOnlyObjectWrapper<>();
startObserver();
}
(尽管一种更好的方法将添加API以允许背景模式,并基于它安装听众。还应需要停止观察者的方法)
现在,我们必须在Position.m
(链接)上修改本机实现:
- (void)startObserver
{
...
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
// try to save battery by using GPS only when app is used:
[self.locationManager requestWhenInUseAuthorization];
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0)
{
// allow background mode
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
NSLog(@"Start updating location");
...
}
(同样,应该基于背景模式API设置)
3。构建和安装
在魅力下的根部,然后使用Mac运行以下操作:
./gradlew clean install
(如果未安装Android SDK,则可以在settings.gradle
上评论Android服务)。
这将安装魅力下降服务的快照(当前3.7.0-snapshot)。
4。更新Gluon移动项目
编辑build.gradle
文件,并设置mavenLocal()
存储库,然后快照版本:
repositories {
mavenLocal()
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
dependencies {
compile 'com.gluonhq:charm:4.3.7'
}
jfxmobile {
downConfig {
version = '3.7.0-SNAPSHOT'
plugins 'display', 'lifecycle', 'position', 'statusbar', 'storage'
}
保存并重新加载项目。
5。在后台模式下使用位置服务
正如评论中指出的那样,在背景模式下运行时,iOS不允许在UI中进行更改。
这意味着每当从服务中检索新位置时,我们都必须使用缓冲区存储它,并且只有在用户恢复应用程序时,我们将使用所有这些缓冲位置对UI进行必要的更新。
这是一个简单的用例:使用生命周期服务,我们知道我们是在前景还是背景上,并且在应用程序在前景上运行时仅更新ListView
控件(UI),或者它只是从后台恢复。/p>
private final BooleanProperty foreground = new SimpleBooleanProperty(true);
private final Map<String, String> map = new LinkedHashMap<>();
private final ObservableList<String> positions = FXCollections.observableArrayList();
public BasicView(String name) {
super(name);
Services.get(LifecycleService.class).ifPresent(l -> {
l.addListener(LifecycleEvent.PAUSE, () -> foreground.set(false));
l.addListener(LifecycleEvent.RESUME, () -> foreground.set(true));
});
ListView<String> listView = new ListView<>(positions);
Button button = new Button("Start GPS");
button.setGraphic(new Icon(MaterialDesignIcon.GPS_FIXED));
button.setOnAction(e -> {
Services.get(PositionService.class).ifPresent(service -> {
foreground.addListener((obs, ov, nv) -> {
if (nv) {
positions.addAll(map.values());
}
});
service.positionProperty().addListener((obs, oldPos, newPos) -> {
if (foreground.get()) {
positions.add(addPosition(newPos));
} else {
map.put(LocalDateTime.now().toString(), addPosition(newPos));
}
});
});
});
VBox controls = new VBox(15.0, button, listView);
controls.setAlignment(Pos.CENTER);
setCenter(controls);
}
private String addPosition(Position position) {
return LocalDateTime.now().toString() + " :: " +
String.format("%.7f, %.7f", position.getLatitude(), position.getLongitude()) +
" :: " + (foreground.get() ? "F" : "B");
}
最后,正如OP所指出的那样,请确保将所需的键添加到PLIST:
<key>NSLocationAlwaysUsageDescription</key>
<string>A good reason.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>An even better reason.</string>
6。部署和运行
允许使用位置使用,启动位置服务以及进入背景模式时,iOS设备上的蓝色状态栏将显示该应用程序正在使用位置。
请注意,这可能会很快排出电池。