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

时间:2019-11-07 09:59:59

标签: 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;
}

}

我可以这样从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);

        }
    });

}

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

2 个答案:

答案 0 :(得分:1)

TL; DR:

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

EXPLANATION

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

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

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

“仅在使用此应用程序时允许”实际上表示“在屏幕上可见应用程序时允许”。这是因为用户现在可以根据该标准选择性地删除位置访问权限,甚至是对前台服务的访问权限。即使您的应用正在运行,用户也可以随时 更改此设置。

Android docs解释说,解决方案android:foregroundServiceType="location"适用于您的确切用例:类似“ Google Maps”的应用程序,具有前台服务,但有望继续处理位置数据如果用户切换到另一个应用程序。该技术称为“继续用户发起的操作”,即使您的应用程序放置在“背景”中,它也允许您获取位置更新。

(文档似乎在这里扩展了“背景”一词的定义。过去,如果您拥有前台服务,则您的应用被视为“前台”-至少出于任务目的优先级,Doze等。现在,如果最近30秒钟内未在屏幕上显示某个应用程序,则该应用程序在位置访问方面就被视为“在后台”。

我不确定在设置特定的foregroundServiceType时会发生什么UI更改(例如在Google Play商店中)。无论如何,在我看来,用户不太可能反对。

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

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

您还可以选择获得ACCESS_BACKGROUND_LOCATION权限,但是docs对此请谨慎行事:

  

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

对于您的用例来说,不需要这种方法,因为您的Activity用于启动Service;您可以保证在Service开始获取后台位置更新之前,您的应用已至少出现在屏幕上一次。 (至少,我假定,这是操作系统确定“用户发起的操作”的开始的方式。如果您正在启动foregroundServiceType方法,大概是行不通的。 ServiceJobScheduler等中的BroadcastReceiver

PS::继续使用该WakeLock代码。如果您想保持10秒钟的稳定更新速度,就需要使设备保持唤醒状态。

答案 1 :(得分:-1)

我在代码中看不到任何问题,但是我对START_NOT_STICKY有点怀疑。尝试使用START_STICKY

START_STICKY

  

如果此服务的进程在启动时被杀死(之后   从onStartCommand(Intent,int,int))返回,然后将其保留在   起始状态,但不要保留此交付意图。后来的系统   将尝试重新创建服务。因为它处于启动状态,   它将保证在之后调用onStartCommand(Intent,int,int)   创建新的服务实例;如果没有任何挂起的开始   传递给服务的命令,它将以null调用   意向对象,因此您必须小心检查这一点。

START_NOT_STICKY

  

如果此服务的进程在启动时被杀死(之后   从onStartCommand(Intent,int,int))返回,并且没有新的   开始交付意图,然后将服务从   处于开始状态,直到以后明确调用才重新创建   Context.startService(Intent)。该服务将不会收到   用空Intent调用onStartCommand(Intent,int,int),因为它   如果没有待发送的意向书,将不会重新启动。

因此,当您返回START_NOT_STICKY时,如果进程被终止,则不会再次调用onStartCommand(),这是您初始化侦听器和locationManager的地方。