我想实现一个跟踪应用程序,该应用程序每3分钟请求一次当前用户位置。该应用程序应在后台运行(也应在该应用程序关闭时)。目前,我正在尝试使用WorkManager。不幸的是,当应用关闭时,我没有得到GPS位置(Toast Message)。
我的代码:
public class LocationWorker extends Worker {
private FusedLocationProviderClient client;
public LocationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
requestLocationUpdates();
return null;
}
private void requestLocationUpdates() {
LocationRequest request = new LocationRequest();
request.setInterval(5 * 1000);
request.setFastestInterval(5 * 1000);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
client = LocationServices.getFusedLocationProviderClient(getApplicationContext());
int permission = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION);
if (permission == PackageManager.PERMISSION_GRANTED) {
final LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Toast.makeText(getApplicationContext(),"TEST",Toast.LENGTH_LONG).show();
Location location = locationResult.getLastLocation();
if (location != null) {
Log.e("LONG", "location update " + location.getLongitude());
}
}
};
client.requestLocationUpdates(request, locationCallback,Looper.getMainLooper());
}
}
您知道在关闭应用程序后应如何在后台接收位置更新吗?我应该使用WorkManager还是其他更好的解决方案?我还尝试了PeriodicWorkRequest,但它的间隔最小(15分钟)。
答案 0 :(得分:0)
我正在尝试自己解决这个问题,到目前为止,我发现在您的情况下,发生了几件事。
首先,从API 26开始,限制了您的应用在后台(或为此关闭)时发生的工作。具体位置请参见此处-> Android Background Limits,也请参见此处-> Android Background Location Limits。
这意味着无论如何,您在解决方案中添加的间隔都将不受尊重。
我认为正在发生的第二件事是FusedLocationProviderClient.requestLocationUpdates(request, locationCallback, looper)打算按照文档在前台模式下使用。对于后台模式,建议使用FusedLocationProviderClient.requestLocationUpdates(request, pendingIntent)。
这就是我目前对使用FusedLocationProviderClient(检索位置的推荐方法)和WorkManager(在后台执行工作的推荐方法)一起理解的地方。因为WorkManager并非旨在与Intent互操作。
答案 1 :(得分:0)
我遇到了同样的问题。我已经通过使用CoroutineWorker并注册了BroadcastReceiver运行时来解决了这个问题。这是我的解决方案,希望对您有所帮助。
class LocationUploadWorker(
private val context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
companion object {
private const val ACTION = "myapp.location"
private const val TIMEOUT = 60_000L // 1 min
private const val REQUEST = 1000
}
override suspend fun doWork(): Result {
val location = obtain() ?: return Result.retry()
val result = upload(location)
return when(result) {
SuccessResponse -> Result.success()
ErrorResponse -> Result.failure()
}
}
@RequiresPermission(allOf = [ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION])
private suspend fun obtain(): Location? =
// coroutines which will cancel all child coroutines if time out reached
withTimeoutOrNull(TIMEOUT) {
// this block will suspend until continuation won't be invoked
suspendCancellableCoroutine { continuation ->
// location client and request
val client = LocationServices.getFusedLocationProviderClient(context)
val request = LocationRequest().apply { priority = LocationRequest.PRIORITY_HIGH_ACCURACY }
// init intent and receiver
val intent = PendingIntent.getBroadcast(context, REQUEST, Intent(ACTION), 0)
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, data: Intent) {
val location = LocationResult.extractResult(data)?.lastLocation
// stop listening
client.removeLocationUpdates(intent)
context.unregisterReceiver(this)
// resume suspended continuation which basically means return location as function
continuation.resume(location)
}
}
// start listening
context.registerReceiver(receiver, IntentFilter(ACTION))
client.requestLocationUpdates(request, intent)
// stop listening if cancelled by timeout or other reason
continuation.invokeOnCancellation {
client.removeLocationUpdates(intent)
context.unregisterReceiver(receiver)
}
}
}
private suspend fun upload(location: Location) {
// upload your location here
}
}
fun Context.startUploadingLocation() {
if (checkLocationPermissions().not()) return
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val request = PeriodicWorkRequestBuilder<LocationUploadWorker>(15, TimeUnit.MINUTES)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.LINEAR, 1, TimeUnit.MINUTES)
.addTag(LocationWorkTag)
.build()
WorkManager
.getInstance(this)
.enqueue(request)
}
fun Context.checkLocationPermissions() =
ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED