Android 如何在应用程序处于后台时保持GPS接收器位置更新(Google Play Service location API)

Android 如何在应用程序处于后台时保持GPS接收器位置更新(Google Play Service location API),android,gps,background-service,android-doze,power-saving,Android,Gps,Background Service,Android Doze,Power Saving,抱歉,这是一个很长的问题,但我希望你们当中的一位专家能够帮助一个正在悄悄发疯的新手 我有一个Android应用程序,它使用后台服务(使用Google Play服务)每隔20秒获得GPS定位。它将纬度和经度与列表中的纬度和经度进行比较,如果找到匹配项,它会向接收器发送广播,从而触发前台活动以提醒用户 我使用后台服务,因为用户警报之间通常有2到20分钟的时间间隔,在这段时间内,没有用户交互。应用程序使用前台活动让用户选择他想要的选项,但随后关闭所有前台活动,只留下后台活动运行 这在我的旧设备安卓4.

抱歉,这是一个很长的问题,但我希望你们当中的一位专家能够帮助一个正在悄悄发疯的新手

我有一个Android应用程序,它使用后台服务(使用Google Play服务)每隔20秒获得GPS定位。它将纬度和经度与列表中的纬度和经度进行比较,如果找到匹配项,它会向接收器发送广播,从而触发前台活动以提醒用户

我使用后台服务,因为用户警报之间通常有2到20分钟的时间间隔,在这段时间内,没有用户交互。应用程序使用前台活动让用户选择他想要的选项,但随后关闭所有前台活动,只留下后台活动运行

这在我的旧设备安卓4.3上运行得很好,但我现在正在安卓8(Oreo)上更新它(在Sony Xperia XZ1Compact上测试)。我补充说

<uses-permission 
android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/
只要该应用程序的某个前台活动打开且可见,它就可以正常工作。使用logcat进行跟踪显示后台服务正在获得GPS修复,onLocationChanged下的代码(请参阅上面的代码)每20秒运行一次

如果我允许我的应用程序关闭所有前台活动并只运行上面的后台服务,只要另一个(完全无关的)调用GPS位置的应用程序打开且可见,该应用程序仍然可以正常工作,onLocationChanged方法仍然每20秒运行一次

但是,如果屏幕上没有任何使用GPS的应用程序,GPS跟踪就会停止;onLocationChanged方法不再运行,我也不再获得GPS定位

如果我打开我的应用程序的一个前台活动,GPS跟踪会再次启动,显示后台服务没有被终止

我假设这与为实现省电/打瞌睡模式所做的更改有关,我只是不太理解它。然而,我所能找到的所有建议似乎都表明,只要在设备白名单中允许作为例外,后台服务将继续工作,但这显然没有发生

我更喜欢让后台服务工作,因为这意味着编写新代码的工作量最少!。然而,一些对类似问题的回答建议使用带有通知的前台服务。这能满足我的要求吗?大部分时间都没有可见的用户界面(除了通知),只是每隔几分钟提示用户做一些事情,同时让他或她自由地做其他事情?它在旧版本的Android中也能正常工作吗


谁能比我更了解这一点(可能比我更了解这一点并不难!)请帮忙?

从Android棉花糖开始,谷歌推出了一种新的电池优化方法 像

  • 打瞌睡模式
  • 应用程序待机模式

  • 您可以通过以下方式了解更多信息: 及 在这些概念中,在android Oreo之前一直存在争议。当谷歌宣布android Oreo时,他们严重依赖电池优化方式,他们引入了新的API来处理后台操作,如工作管理器和作业调度程序 但他们也进入打瞌睡和待机模式。并在打瞌睡模式维护窗口中工作。
    经过长时间的探索和实践,找到了最好的方法 要保持每20秒跟踪一次用户位置,您需要启动具有粘性前台通知的前台服务。这种方式不会因打瞌睡或待机模式而终止,它会根据时间间隔更新位置。 下面是对

    的解释和示例

    被列入白名单的应用程序可以在打瞌睡和应用程序待机期间使用网络并持有部分唤醒锁。然而,其他限制仍然适用于白名单上的应用程序,就像它们适用于其他应用程序一样。例如,白名单上的应用程序的作业和同步被延迟(在API级别23及以下),其常规AlarmManager警报不会触发

    您的用例似乎属于“其他限制仍然适用”


    您的应用程序在不使用时会耗尽电池电量,这正是应用程序待机和打瞌睡的目的

    非常感谢你。很好用!非常感谢你。很好用!对于任何感兴趣的人,我调用了在使用startForegroundService而不是StarService之前使用的相同服务。在服务中(在onCreate和onStartCommand中),我创建了一个通知并称为startForeground。然后,我将通过接收器触发前台活动的广播替换为直接启动前台活动的简单意图。我想我需要一个单独的版本的服务,旧的安卓版本。
    package com.barney.trackgps;
    
    import android.Manifest;
    import android.app.Service;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.location.Location;
    import android.location.LocationListener;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.support.v4.app.ActivityCompat;
    import android.util.Log;
    
    import com.google.android.gms.common.ConnectionResult;
    import com.google.android.gms.common.api.GoogleApiClient;
    import com.google.android.gms.location.LocationListener;
    import com.google.android.gms.location.LocationRequest;
    import com.google.android.gms.location.LocationServices;
    
    
    
    public class ApiTrackService extends Service implements
        GoogleApiClient.ConnectionCallbacks, 
    GoogleApiClient.OnConnectionFailedListener,
        LocationListener {
    
    GoogleApiClient mLocationClient;
    LocationRequest mLocationRequest = new LocationRequest();
    public static final String ACTION_LOCATION_BROADCAST = 
    ApiTrackService.class.getName() + "LocationBroadcast";
    public static final String EXTRA_LATITUDE = "extra_latitude";
    public static final String EXTRA_LONGITUDE = "extra_longitude";
    
    int interval=20; //time between fixes in seconds
    
    
    @Override
    public void onDestroy(){
        super.onDestroy();
        stopSelf();
    }
    
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v("sTag","Got to apitrack");
        mLocationClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mLocationRequest.setInterval(interval*1000);
        mLocationRequest.setFastestInterval(1000);
        int priority = LocationRequest.PRIORITY_HIGH_ACCURACY;
        mLocationRequest.setPriority(priority);
        mLocationClient.connect();
        return START_STICKY;
    }
    
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    
    @Override
    public void onConnected(Bundle dataBundle) {
        if (ActivityCompat.checkSelfPermission(this, 
    Manifest.permission.ACCESS_FINE_LOCATION) != 
    PackageManager.PERMISSION_GRANTED && 
    ActivityCompat.checkSelfPermission(this, 
    Manifest.permission.ACCESS_COARSE_LOCATION) != 
    PackageManager.PERMISSION_GRANTED) {
            return;
        }
    
    LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, 
    mLocationRequest, this);
    }
    
    
    @Override
    public void onConnectionSuspended(int i) {
    }
    
    
    //to get the location change
    @Override
    public void onLocationChanged(Location location) {
        boolean locFound=false;
        Log.v("sTag","Got a fix");
            /*does stuff to compare latitude and longitude with places in a list 
            and sets locFound=true if it finds a match*/
        if (location != null) {
            GPSLog=GPSLog+"Found";
            if (locFound) {
                Log.v("sTag", "Sending broadcast");
                Intent intent = new Intent();
                intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                intent.setAction("com.AboutMyJourney.posBroadcast");
                sendBroadcast(intent);
            }
        }
    }
    
    
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        //Log.d(TAG, "Failed to connect to Google API");
    }
    
    }