是否要求在Android上从后台获取位置更新?

时间:2019-03-31 09:27:38

标签: android service background gps location

我想实现一个跟踪应用程序,该应用程序每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分钟)。

2 个答案:

答案 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