当应用程序处于后台时获取位置更新
Get Location Update when app is in background
我正在尝试创建一项服务,当应用程序在后台时提供位置更新,专门针对 Android 8 及更高版本,因为他们已经优化了后台服务。我在 Whosebug 和 Google API 文档上阅读了一些答案,他们提到将 PendingIntent 传递给 onRequestLocationUpdates 会提供位置,即使应用程序在后台也是如此。正如 doc 中提到的那样。这是我的服务 class:
public class LocationMonitoringService extends Service implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String TAG = LocationMonitoringService.class.getSimpleName();
GoogleApiClient mLocationClient;
LocationRequest mLocationRequest = new LocationRequest();
public static final String ACTION_LOCATION_BROADCAST = LocationMonitoringService.class.getName() + "LocationBroadcast";
public static final String EXTRA_LATITUDE = "extra_latitude";
public static final String EXTRA_LONGITUDE = "extra_longitude";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mLocationClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest.setInterval(Constants.LOCATION_INTERVAL);
mLocationRequest.setFastestInterval(Constants.FASTEST_LOCATION_INTERVAL);
int priority = LocationRequest.PRIORITY_HIGH_ACCURACY; //by default
//PRIORITY_BALANCED_POWER_ACCURACY, PRIORITY_LOW_POWER, PRIORITY_NO_POWER are the other priority modes
mLocationRequest.setPriority(priority);
mLocationClient.connect();
//Make it stick to the notification panel so it is less prone to get cancelled by the Operating System.
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/*
* LOCATION CALLBACKS
*/
@Override
public void onConnected(Bundle dataBundle) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Log.d(TAG, "== Error On onConnected() Permission not granted");
//Permission not granted by user so cancel the further execution.
return;
}
Intent intent = new Intent(this, LocationMonitoringService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, mLocationRequest, pendingIntent);
Log.d(TAG, "Connected to Google API");
}
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
@Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "Connection suspended");
}
//to get the location change
@Override
public void onLocationChanged(Location location) {
Log.d(TAG, "Location changed");
if (location != null) {
Log.d(TAG, "== location != null");
//Send result to activities
sendMessageToUI(String.valueOf(location.getLatitude()), String.valueOf(location.getLongitude()));
}
}
private void sendMessageToUI(String lat, String lng) {
Log.d(TAG, "Sending info...");
Intent intent = new Intent(ACTION_LOCATION_BROADCAST);
intent.putExtra(EXTRA_LATITUDE, lat);
intent.putExtra(EXTRA_LONGITUDE, lng);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "Failed to connect to Google API");
}
}
在上面的代码中,当我将 PendingIntent 传递给 requestLocationUpdates 时,它向我抛出一个错误 GoogleAPI客户端未连接。
下面是我遇到的错误
java.lang.IllegalStateException: GoogleApiClient is not connected yet.
at com.google.android.gms.internal.zzbcd.zze(Unknown Source)
at com.google.android.gms.internal.zzbcx.zze(Unknown Source)
at com.google.android.gms.internal.zzbcp.zze(Unknown Source)
at com.google.android.gms.internal.zzccb.requestLocationUpdates(Unknown Source)
at com.locationtracker.LocationMonitoringService.onConnected(LocationMonitoringService.java:93)
at com.google.android.gms.common.internal.zzac.zzn(Unknown Source)
at com.google.android.gms.internal.zzbcp.zzm(Unknown Source)
at com.google.android.gms.internal.zzbcd.zzpY(Unknown Source)
at com.google.android.gms.internal.zzbcd.onConnected(Unknown Source)
at com.google.android.gms.internal.zzbcx.onConnected(Unknown Source)
at com.google.android.gms.internal.zzbbi.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzaa.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzn.zzrj(Unknown Source)
at com.google.android.gms.common.internal.zze.zzs(Unknown Source)
at com.google.android.gms.common.internal.zzi.zzrk(Unknown Source)
at com.google.android.gms.common.internal.zzh.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
我遇到了同样的问题,请在 Android Studio 模拟器中尝试 运行 您的代码,而不是其他模拟器。原因是您需要在设备中安装 Google Play 服务。
我看到您在 onStartCommand()
中实例化了 GoogleApiClient
实例,正如服务生命周期所说,每次启动服务时都会调用 onStartCommand()
。因此,每次它实例化您的 GoogleApiClient 实例时。
我认为这会在您的代码中造成冲突。让我们考虑一个场景来理解问题。
您已经启动了一次服务,过一会又启动了。因此,它两次都启动了 mLocationClient
,因为您已经在 onStartCommand() 方法中启动了它。 suppose 第一个实例的值是 mLocationClient1,另一个是 mLocationClient2。(这个值只是为了解释简单)。
mLocationClient1
成功连接后,它会触发 onConnected() 方法,并且在其中您使用了 mLocationClient
,它由一个新值 mLocationClient2
初始化,但不是已连接。因此,googleapiclient 未连接 returns 错误。
如果您认为您只启动了一次服务,那么它多次调用 onStartCommand() 的原因就是答案。每当您从最近的应用程序列表中删除您的应用程序时,都会停止您的服务,但您已经创建了 START_STICKY
服务,因此,os 重新启动您的服务并再次调用 onStartCommand() 方法。
所以,我建议你在 onCreate()
方法中实例化 mLocationClient
而不是 onStartCommand()
因为 onCreate()
方法只会在服务创建后第一次创建时被调用被毁。
我正在尝试创建一项服务,当应用程序在后台时提供位置更新,专门针对 Android 8 及更高版本,因为他们已经优化了后台服务。我在 Whosebug 和 Google API 文档上阅读了一些答案,他们提到将 PendingIntent 传递给 onRequestLocationUpdates 会提供位置,即使应用程序在后台也是如此。正如 doc 中提到的那样。这是我的服务 class:
public class LocationMonitoringService extends Service implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String TAG = LocationMonitoringService.class.getSimpleName();
GoogleApiClient mLocationClient;
LocationRequest mLocationRequest = new LocationRequest();
public static final String ACTION_LOCATION_BROADCAST = LocationMonitoringService.class.getName() + "LocationBroadcast";
public static final String EXTRA_LATITUDE = "extra_latitude";
public static final String EXTRA_LONGITUDE = "extra_longitude";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mLocationClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest.setInterval(Constants.LOCATION_INTERVAL);
mLocationRequest.setFastestInterval(Constants.FASTEST_LOCATION_INTERVAL);
int priority = LocationRequest.PRIORITY_HIGH_ACCURACY; //by default
//PRIORITY_BALANCED_POWER_ACCURACY, PRIORITY_LOW_POWER, PRIORITY_NO_POWER are the other priority modes
mLocationRequest.setPriority(priority);
mLocationClient.connect();
//Make it stick to the notification panel so it is less prone to get cancelled by the Operating System.
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/*
* LOCATION CALLBACKS
*/
@Override
public void onConnected(Bundle dataBundle) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Log.d(TAG, "== Error On onConnected() Permission not granted");
//Permission not granted by user so cancel the further execution.
return;
}
Intent intent = new Intent(this, LocationMonitoringService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, mLocationRequest, pendingIntent);
Log.d(TAG, "Connected to Google API");
}
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
@Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "Connection suspended");
}
//to get the location change
@Override
public void onLocationChanged(Location location) {
Log.d(TAG, "Location changed");
if (location != null) {
Log.d(TAG, "== location != null");
//Send result to activities
sendMessageToUI(String.valueOf(location.getLatitude()), String.valueOf(location.getLongitude()));
}
}
private void sendMessageToUI(String lat, String lng) {
Log.d(TAG, "Sending info...");
Intent intent = new Intent(ACTION_LOCATION_BROADCAST);
intent.putExtra(EXTRA_LATITUDE, lat);
intent.putExtra(EXTRA_LONGITUDE, lng);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "Failed to connect to Google API");
}
}
在上面的代码中,当我将 PendingIntent 传递给 requestLocationUpdates 时,它向我抛出一个错误 GoogleAPI客户端未连接。
下面是我遇到的错误
java.lang.IllegalStateException: GoogleApiClient is not connected yet.
at com.google.android.gms.internal.zzbcd.zze(Unknown Source)
at com.google.android.gms.internal.zzbcx.zze(Unknown Source)
at com.google.android.gms.internal.zzbcp.zze(Unknown Source)
at com.google.android.gms.internal.zzccb.requestLocationUpdates(Unknown Source)
at com.locationtracker.LocationMonitoringService.onConnected(LocationMonitoringService.java:93)
at com.google.android.gms.common.internal.zzac.zzn(Unknown Source)
at com.google.android.gms.internal.zzbcp.zzm(Unknown Source)
at com.google.android.gms.internal.zzbcd.zzpY(Unknown Source)
at com.google.android.gms.internal.zzbcd.onConnected(Unknown Source)
at com.google.android.gms.internal.zzbcx.onConnected(Unknown Source)
at com.google.android.gms.internal.zzbbi.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzaa.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzn.zzrj(Unknown Source)
at com.google.android.gms.common.internal.zze.zzs(Unknown Source)
at com.google.android.gms.common.internal.zzi.zzrk(Unknown Source)
at com.google.android.gms.common.internal.zzh.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
我遇到了同样的问题,请在 Android Studio 模拟器中尝试 运行 您的代码,而不是其他模拟器。原因是您需要在设备中安装 Google Play 服务。
我看到您在 onStartCommand()
中实例化了 GoogleApiClient
实例,正如服务生命周期所说,每次启动服务时都会调用 onStartCommand()
。因此,每次它实例化您的 GoogleApiClient 实例时。
我认为这会在您的代码中造成冲突。让我们考虑一个场景来理解问题。
您已经启动了一次服务,过一会又启动了。因此,它两次都启动了
mLocationClient
,因为您已经在 onStartCommand() 方法中启动了它。 suppose 第一个实例的值是 mLocationClient1,另一个是 mLocationClient2。(这个值只是为了解释简单)。mLocationClient1
成功连接后,它会触发 onConnected() 方法,并且在其中您使用了mLocationClient
,它由一个新值mLocationClient2
初始化,但不是已连接。因此,googleapiclient 未连接 returns 错误。
如果您认为您只启动了一次服务,那么它多次调用 onStartCommand() 的原因就是答案。每当您从最近的应用程序列表中删除您的应用程序时,都会停止您的服务,但您已经创建了 START_STICKY
服务,因此,os 重新启动您的服务并再次调用 onStartCommand() 方法。
所以,我建议你在 onCreate()
方法中实例化 mLocationClient
而不是 onStartCommand()
因为 onCreate()
方法只会在服务创建后第一次创建时被调用被毁。