我创建了一个服务,该服务可以查找用户的坐标,然后将其存储在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没有输出预期的结果,它的确是这样。有什么想法吗?
答案 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
方法,大概是行不通的。 Service
或JobScheduler
等中的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的地方。