locationListener 在前台服务 30 秒后不起作用

Ira*_*dis 5 android android-location foreground-service android-10.0

我创建了一个服务,用于查找用户的坐标并将其存储在 SQLite 数据库中。

public class GPS_Service extends Service {

DatabaseHelper myDb;

private LocationListener locationListener;
private LocationManager locationManager;

private String latitude, longitude;

@Override
public void onCreate() {
    super.onCreate();

    myDb = new DatabaseHelper(this);

}

@SuppressLint("MissingPermission")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Service")
            .setContentText("Coordinates Location Running")
            .setContentIntent(pendingIntent)
            .build();

    startForeground(1, notification);

    locationListener = new LocationListener() {

        @Override
        public void onLocationChanged(Location location) {

            Log.d("myTag", "Hello");

            latitude = String.valueOf(location.getLatitude());
            longitude = String.valueOf(location.getLongitude());

            insertCoordinates(latitude, longitude);

            Intent i = new Intent("location_update");
            i.putExtra("latitude", latitude);
            i.putExtra("longitude",longitude);

            sendBroadcast(i);

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

            Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);

        }

    };

    locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locationListener);

    return START_NOT_STICKY;

}

@Override
public void onDestroy() {

    super.onDestroy();

    if(locationManager != null)
        locationManager.removeUpdates(locationListener);

}

private void insertCoordinates(String latitude, String longitude) {

    boolean inserted = myDb.insertData(latitude, longitude); //Insert coordinates

    //Check if insertion is completed
    if(inserted)
        Toast.makeText(GPS_Service.this, "Coordinates Inserted", Toast.LENGTH_SHORT).show();
    else
        Toast.makeText(GPS_Service.this, "Coordinates Not Inserted", Toast.LENGTH_SHORT).show();

}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

}
Run Code Online (Sandbox Code Playgroud)

我可以像这样从 MainActivity 启动或停止服务

private void enable_buttons() {

    buttonStartService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent serviceIntent = new Intent(getApplicationContext(), GPS_Service.class);

            //Checks if the SDK version is higher than 26 to act accordingly
            ContextCompat.startForegroundService(MainActivity.this, serviceIntent);

        }
    });

    buttonStopService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent serviceIntent = new Intent(MainActivity.this, GPS_Service.class);
            stopService(serviceIntent);

        }
    });

}
Run Code Online (Sandbox Code Playgroud)

问题是,当我启动此服务时,如果我完全关闭应用程序或将其留在后台,则 locationListener 将工作 30 秒然后停止。如果我重新打开该应用程序,该服务将从停止的地方继续工作。如果服务正在运行,我还检查了开发人员选项,即使 locationListener 没有输出预期的结果,它也确实如此。有任何想法吗?

gre*_*e31 5

特尔;博士:

添加android:foregroundServiceType="location"到您的Service's清单条目。

解释

这种针对 Android 10 的新行为与您所描述的完全一样:即使您可能正在使用前台服务,在您的应用离开屏幕 30 秒后,位置更新也会停止。

您可能已经注意到,Android 10 设备在授予位置权限时向用户提供了两个新选择(对于旧版 (API < 29) 应用或声明ACCESS_BACKGROUND_LOCATION权限的应用):

  • “一直允许”
  • “仅在使用此应用程序时允许”

“仅在使用此应用程序时允许”实际上意味着“在应用程序在屏幕上可见时允许”。这是因为用户现在可以根据该标准有选择地删除位置访问权限——甚至是前台服务。用户可以更改此设置在任何时候,即使你的应用程序正在运行。

Android的文档说明,解决方案,android:foregroundServiceType="location",旨在为您精确的用例:“谷歌地图”般的应用程序,其中有一个前台服务,但预计将继续处理位置数据,如果用户切换到另一个应用程序。该技术称为“继续用户启动的操作”,即使在您的应用程序置于“后台”之后,它也允许您获取位置更新。

(文档似乎在这里扩展了术语“后台”的定义。过去,如果您有前台服务,则您的应用程序被视为“在前台”——至少出于任务优先级的目的,打盹,等等。现在看来,就位置访问而言,如果某个应用在过去 30 秒内未出现在屏幕上,则它似乎被视为“在后台”。)

我不确定当您设置特定的foregroundServiceType. 无论如何,在我看来,用户不太可能反对。

适用于 Android 10 设备的其他解决方案

或者,您可以声明 atargetSdkVersion为 28 或更少,这将使您的应用程序在位置“兼容模式”下运行。

您还可以选择获得ACCESS_BACKGROUND_LOCATION许可,但文档警告不要这样做

如果您的应用在后台运行时不需要位置访问权限,强烈建议您不要请求ACCESS_BACKGROUND_LOCATION...

对于您的用例,这种方法不是必需的,因为您Activity用于启动您的Service; 在Service开始获取后台位置更新之前,您可以保证您的应用程序至少在屏幕上出现过一次。(至少,我认为这就是操作系统确定“用户启动的操作”开始的foregroundServiceType方式。据推测,如果您Service从 aJobScheduler或 aBroadcastReceiver或其他东西开始,该方法将不起作用。)

PS:坚持那个WakeLock代码。如果您想以 10 秒的速度持续获取更新,您将需要保持设备处于唤醒状态。