首页 > 解决方案 > 当应用程序移至后台时,在前台服务中获取位置更新不起作用

问题描述

我正在使用下面的应用程序代码每 5 秒从 GPS 接收位置更新,并将输出打印到日志中。只要应用程序在前台,这对我来说就很好,但是当它移动到后台时更新会停止,直到它被带回前台。我正在使用galaxy s20(android 10)来运行和调试,并且应用程序设置为永不休眠(android设置)。这是完整的过程描述:

我在清单上有这些权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.android.hardware.location.gps"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

这是启用 GPS 并创建服务的主要活动:

public class MainActivity extends AppCompatActivity {

    private LocationManager _locManager;
    private Intent _scanServiceIntent;

    public void onStart()
    {
        super.onStart();
        boolean gps_enabled = _locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (gps_enabled)
        {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET}, 1);
            _scanServiceIntent = new Intent(this, ScanService.class);
            startService(_scanServiceIntent);
        }       
    }
}

这是服务类-我正在创建一个通知以使服务始终运行-即使应用程序进入后台:

public class ScanService extends Service {

    private LocationListener _locListener;
    private LocationManager _locManager;


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        String NOTIFICATION_CHANNEL_ID = "com.tandenkore.mychannel";

        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MAX)
                .setCategory(Notification.CATEGORY_SERVICE)
                .setContentIntent(pendingIntent)
                .build();
        startForeground(300, notification);
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        _locListener = new MyLocationListener();
        _locManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        _locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, _locListener);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true);
        _locManager.removeUpdates(_locListener);
    }

    class MyLocationListener implements LocationListener {
        @Override
        public void onLocationChanged(Location location)
        {
            Log.v(TAG, "location changes");
        }
    }
}

当我在设备上调试它并且应用程序在前台时,我每 5 秒在 logcat 中得到一个“位置更改”行的输出。这是我正在寻找的行为。当应用程序进入后台时会出现问题,然后我在 logcat 中收到以下内容:

2021-04-11 00:00:26.648 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:31.657 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:36.656 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:41.656 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:46.653 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:50.170 7241-7307/? E/RequestManager_FLP: [LocationManager] Paused by AppOps 7e14624(Listener) gps interval=5000 from com.tandenkore.application (10367)
2021-04-11 00:00:50.173 7241-7307/? I/RequestManager_FLP: onOpChanged, op=0 / packageName=com.tandenkore.application
2021-04-11 00:00:50.174 7241-7307/? I/PackageInfoManager_FLP: checkLocationAccess for com.tandenkore.application[gps,5000,100], result is false

但是在这最后一条消息之后 - 在我将应用程序带回前台之前不再进行更新。我希望它一直保持运行 - 为此还需要做什么?

标签: javaandroidservicenotificationsbackground

解决方案


根据官方文档:

当您应用中的某个功能在运行 Android 10(API 级别 29)的设备上请求后台位置时,系统权限对话框会包含一个名为“始终允许”的选项。如果用户选择此选项,您应用中的功能将获得后台位置访问权限。

但是,在 Android 11(API 级别 30)及更高版本上,系统对话框不包含“始终允许”选项。相反,用户必须在设置页面上启用后台定位,如图 3 所示。

因此,您需要在应用程序的“设置”页面中始终看到名为“允许”的选项

在此处输入图像描述

为此,您必须在清单中拥有此权限:

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

在 API 30 设备及以下设备上,以便在对话框中显示“允许所有时间”"Manifest.permission.ACCESS_BACKGROUND_LOCATION"选项,当您要求用户授予他们权限时,您必须添加。如下

ActivityCompat.requestPermissions(MainActivity.this,
                new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION ,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION},
                1);

如您所见,对话框始终显示“允许”

在此处输入图像描述

但在 Android Api 30 及更高版本上,您必须邀请用户针对 API < 30手动检查此选项

所以如果你想加载设置页面,你可以使用这个方法:

   // load the permission setting screen
    private void loadPermissionPage(Activity context) {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", context.getPackageName(), null);
        intent.setData(uri);
        context.startActivityForResult(intent, 0);
    }

最后,如果您选中Allow all time选项,您的应用程序将能够在后台监听位置变化

更新 :

对于 Android 30 及更高版本,您可以通过直接使用以下代码行打开位置权限页面来要求用户保证后台位置:

requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, REQUEST_CODE);

有关更多信息,请参阅请求后台位置


推荐阅读