使用'后台位置服务'在iOS上使用gluon mobile

时间:2017-09-16 20:47:11

标签: gluon gluon-mobile

我刚开始使用胶子手机,我正在开发一个小型iOS应用程序。我设法使用PositionService来更新UI中标签上的用户位置。现在,即使应用程序处于后台模式,我也希望获得位置更新。由于Apple开发人员的文档,这应该通过将以下密钥添加到应用程序plist

来实现
@DataJpaTest

在iPhone上部署应用程序时,只要应用程序处于活动状态,我就可以看到更新。当进入主屏幕时,更新停止并在终端(gradle - > launchIOSDevice)中消息"停止更新位置"显示。知道为什么我不能在后台模式下获得位置更新吗?

这里有相关代码:

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>

以下是相关的plist条目:

    Services.get(PositionService.class).ifPresent(service -> {
        service.positionProperty().addListener((obs, oldPos, newPos) -> {
            posLbl.setText(String.format(" %.7f %.7f\nLast update: " + LocalDateTime.now().toString(),
                    newPos.getLatitude(), newPos.getLongitude()));
            handleData();
        });
    });

1 个答案:

答案 0 :(得分:0)

可以在应用进入后台模式时找到位置服务无效的原因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服务实现。

这样做,一旦您打开应用程序并实例化位置服务,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是一个可以找到here的开源库。

<强> 2。编辑iOS的职位服务

我们需要从IOSPositionServicelink)注释掉或删除生命周期监听器:

public IOSPositionService() {
    position = new ReadOnlyObjectWrapper<>();

    startObserver();
}

(虽然更好的方法是添加API以允许后台模式,并根据它安装监听器。还应该要求停止观察者的方法)

现在我们必须在Position.mlink)修改原生实现:

- (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。在后台模式下使用位置服务

正如评论中所指出的,当在后台模式下运行时,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设备上的蓝色状态栏将显示该应用正在使用位置。

请注意,这可能会非常快地耗尽电池