应用进入后台时Position服务不工作的原因可以找到here:
Services.get(LifecycleService.class).ifPresent(l -> {
l.addListener(LifecycleEvent.PAUSE, IOSPositionService::stopObserver);
l.addListener(LifecycleEvent.RESUME, IOSPositionService::startObserver);
});
startObserver();
生命周期服务旨在防止在应用处于后台时执行不必要的操作,主要是为了节省电量。许多服务,包括位置或电池,默认使用它。
到目前为止,还没有简单的方法可以删除侦听器,因为没有 API 可以启用或禁用它的使用。如果您认为应该添加此内容,您可以提交问题here。
您可以分叉 Charm Down 存储库,删除相关代码,然后使用您自己的快照重新构建它,但这当然不是一个好的长期解决方案。
目前,我能想到的唯一方法是在不修改 Down 的情况下避免包含 iOS 的 Lifecycle 服务实现。
这样做后,一旦您打开应用并实例化 Position 服务,startObserver 将被调用并且永远不会停止(直到您关闭应用)。
在您的build.gradle 文件中,不要使用downConfig 块来包含position 插件,而是在dependencies 块中执行此操作,并删除对生命周期-ios 的遍历依赖:
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 的 Position 服务,并构建本地快照。
这些是步骤(仅适用于 Mac):
1.克隆/分叉 Charm Down
Charm Down 是一个开源库,可以在 here 找到。
2。编辑 iOS 的位置服务
我们需要从IOSPositionService (link) 中注释掉或移除 Lifecycle 监听器:
public IOSPositionService() {
position = new ReadOnlyObjectWrapper<>();
startObserver();
}
(虽然更好的方法是添加 API 以允许后台模式,并基于它安装侦听器。还需要一种停止观察者的方法)
现在我们必须修改 Position.m (link) 的原生实现:
- (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.构建和安装
在 Charm Down 的根目录下,使用 Mac,运行:
./gradlew clean install
(如果没有安装Android sdk,可以在settings.gradle注释掉android服务)。
这将安装 Charm Down 服务的快照(当前为 3.7.0-SNAPSHOT)。
4.更新 Gluon Mobile 项目
编辑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.在后台模式下使用定位服务
正如 cmets 中所指出的,在后台模式下运行时,iOS 不允许在 UI 中进行更改。
这意味着每当从服务中检索到新位置时,我们都必须使用缓冲区来存储它,并且只有当用户恢复应用时,我们才会使用所有这些缓冲位置对 UI 进行必要的更新.
这是一个简单的用例:通过 Lifecycle 服务,我们知道我们是在前台还是后台,并且我们仅在应用程序在前台运行或刚刚从后台恢复时更新 ListView 控件 (UI) .
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设备上的蓝色状态栏会显示App正在使用定位。
请注意,这可能会很快耗尽电池。