如何使用 FusedLocation API 每 5 分钟接收一次位置更新
How to receive location updates every 5 minutes using the FusedLocation API
我目前正在开发一款必须每五分钟检查一次用户位置并将坐标发送到服务器的应用程序。我决定使用 Google Play Services 中的 FusedLocation API 而不是普通的旧 LocationManager API,主要是因为我注意到 LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY 优先级别,它声称可以提供 100 米的精度级别和合理的电池使用,这正是我所需要的。
在我的例子中,我有一个 Activity 其继承结构是:
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener
并实现相关回调(onConnected、onConnectionFailed、onConnectionSuspended、onLocationChanged)。我还得到了 GoogleApiClient 的一个实例,按照官方文档的建议,使用这种方法:
protected synchronized GoogleApiClient buildGoogleApiClient() {
return new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
在 onConnected 中,我使用
开始位置更新
LocationServices.FusedLocationApi.requestLocationUpdates(mApiClient,
mLocationRequest, this);
...并在 onLocationChanged() 中捕捉变化。
但是,我很快发现位置更新似乎在一段时间后停止了。也许是因为此方法与 Activity 生命周期相关,我不确定。无论如何,我试图通过创建一个扩展 IntentService 并由 AlarmManager 启动它的内部 class 来解决这个问题。所以在 onConnected 中,我最终这样做了:
AlarmManager alarmMan = (AlarmManager) this
.getSystemService(Context.ALARM_SERVICE);
Intent updateIntent = new Intent(this, LocUpService.class);
PendingIntent pIntent = PendingIntent.getService(this, 0, updateIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmMan.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0,
1000 * 60 * 5, pIntent);
LocUpService class 看起来像这样:
public static class LocUpService extends IntentService {
public LocUpService() {
super("LocUpService");
}
@Override
protected void onHandleIntent(Intent intent) {
Coords coords = LocationUpdater.getLastKnownLocation(mApiClient);
}
}
LocationUpdater是另一个class,里面包含静态方法getLastKnownLocation,就是这个:
public static Coords getLastKnownLocation(GoogleApiClient apiClient) {
Coords coords = new Coords();
Location location = LocationServices.FusedLocationApi
.getLastLocation(apiClient);
if (location != null) {
coords.setLatitude(location.getLatitude());
coords.setLongitude(location.getLongitude());
Log.e("lat ", location.getLatitude() + " degrees");
Log.e("lon ", location.getLongitude() + " degrees");
}
return coords;
}
但是惊喜!!当我清楚地将引用传递给静态方法时,我得到了 "IllegalArgumentException: GoogleApiClient parameter is required",我再次猜测这一定与 GoogleApiClient 实例有关,该实例与 Activity 的生命周期有关,并且将实例传递到 IntentService 时出现问题。
所以我在想:如何在不发疯的情况下每五分钟定期更新一次位置信息?我是否扩展服务,在该组件上实现所有接口回调,在其中构建 GoogleApiClient 实例并将其 运行 保留在后台?我是否让 AlarmManager 启动一项服务,该服务每五分钟扩展一次 IntentService 以完成工作,再次在 IntentService 中构建所有相关回调和 GoogleApiClient?我是否继续做我现在正在做的事情,但将 GoogleApiClient 构造为单例,并期望它会有所作为?你会怎么做?
感谢并抱歉这么长篇大论。
I am currently working on an app that has to check the user's location every five minutes and send the coordinates to a server. I decided to go with the FusedLocation API in Google Play Services instead of the plain old LocationManager API
我们的应用程序具有完全相同的要求,我在几天前实现了它,这就是我的实现方式。
在启动 activity 或任何您想启动的地方,使用 AlarmManager 将 LocationTracker 配置为每 5 分钟 运行。
private void startLocationTracker() {
// Configure the LocationTracker's broadcast receiver to run every 5 minutes.
Intent intent = new Intent(this, LocationTracker.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis(),
LocationProvider.FIVE_MINUTES, pendingIntent);
}
LocationTracker.java
public class LocationTracker extends BroadcastReceiver {
private PowerManager.WakeLock wakeLock;
@Override
public void onReceive(Context context, Intent intent) {
PowerManager pow = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pow.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
wakeLock.acquire();
Location currentLocation = LocationProvider.getInstance().getCurrentLocation();
// Send new location to backend. // this will be different for you
UserService.registerLocation(context, new Handlers.OnRegisterLocationRequestCompleteHandler() {
@Override
public void onSuccess() {
Log.d("success", "UserService.RegisterLocation() succeeded");
wakeLock.release();
}
@Override
public void onFailure(int statusCode, String errorMessage) {
Log.d("error", "UserService.RegisterLocation() failed");
Log.d("error", errorMessage);
wakeLock.release();
}
}, currentLocation);
}
}
LocationProvider.java
public class LocationProvider {
private static LocationProvider instance = null;
private static Context context;
public static final int ONE_MINUTE = 1000 * 60;
public static final int FIVE_MINUTES = ONE_MINUTE * 5;
private static Location currentLocation;
private LocationProvider() {
}
public static LocationProvider getInstance() {
if (instance == null) {
instance = new LocationProvider();
}
return instance;
}
public void configureIfNeeded(Context ctx) {
if (context == null) {
context = ctx;
configureLocationUpdates();
}
}
private void configureLocationUpdates() {
final LocationRequest locationRequest = createLocationRequest();
final GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build();
googleApiClient.registerConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
startLocationUpdates(googleApiClient, locationRequest);
}
@Override
public void onConnectionSuspended(int i) {
}
});
googleApiClient.registerConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
});
googleApiClient.connect();
}
private static LocationRequest createLocationRequest() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(FIVE_MINUTES);
return locationRequest;
}
private static void startLocationUpdates(GoogleApiClient client, LocationRequest request) {
LocationServices.FusedLocationApi.requestLocationUpdates(client, request, new com.google.android.gms.location.LocationListener() {
@Override
public void onLocationChanged(Location location) {
currentLocation = location;
}
});
}
public Location getCurrentLocation() {
return currentLocation;
}
}
我首先在扩展应用程序的 class 中创建 LocationProvider 的实例,在应用程序启动时创建实例:
MyApp.java
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
LocationProvider locationProvider = LocationProvider.getInstance();
locationProvider.configureIfNeeded(this);
}
}
LocationProvider 被实例化并配置为仅一次位置更新,因为它是单例。每 5 分钟它会更新它的 currentLocation
值,我们可以使用
从任何我们需要的地方检索它
Location loc = LocationProvider.getInstance().getCurrentLocation();
运行 不需要任何类型的后台服务。 AlarmManager 将每 5 分钟向 LocationTracker.onReceive() 广播一次,部分唤醒锁将确保代码将完成 运行ning,即使设备处于待机状态。这也是节能的。
请注意,您需要以下权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- For keeping the LocationTracker alive while it is doing networking -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
别忘了注册接收者:
<receiver android:name=".LocationTracker" />
关于您使用 Activity 请求位置更新的第一种方法,除非您在 activity 的 onPause() 方法中断开位置客户端,否则它们不应停止。因此,只要您的 activity 在 background/foreground 中,您就应该继续接收位置更新。但是如果 activity 被破坏了那么你当然不会得到更新。
检查您是否在 activity 生命周期中断开位置客户端。
我目前正在开发一款必须每五分钟检查一次用户位置并将坐标发送到服务器的应用程序。我决定使用 Google Play Services 中的 FusedLocation API 而不是普通的旧 LocationManager API,主要是因为我注意到 LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY 优先级别,它声称可以提供 100 米的精度级别和合理的电池使用,这正是我所需要的。
在我的例子中,我有一个 Activity 其继承结构是:
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener
并实现相关回调(onConnected、onConnectionFailed、onConnectionSuspended、onLocationChanged)。我还得到了 GoogleApiClient 的一个实例,按照官方文档的建议,使用这种方法:
protected synchronized GoogleApiClient buildGoogleApiClient() {
return new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
在 onConnected 中,我使用
开始位置更新LocationServices.FusedLocationApi.requestLocationUpdates(mApiClient,
mLocationRequest, this);
...并在 onLocationChanged() 中捕捉变化。
但是,我很快发现位置更新似乎在一段时间后停止了。也许是因为此方法与 Activity 生命周期相关,我不确定。无论如何,我试图通过创建一个扩展 IntentService 并由 AlarmManager 启动它的内部 class 来解决这个问题。所以在 onConnected 中,我最终这样做了:
AlarmManager alarmMan = (AlarmManager) this
.getSystemService(Context.ALARM_SERVICE);
Intent updateIntent = new Intent(this, LocUpService.class);
PendingIntent pIntent = PendingIntent.getService(this, 0, updateIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmMan.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0,
1000 * 60 * 5, pIntent);
LocUpService class 看起来像这样:
public static class LocUpService extends IntentService {
public LocUpService() {
super("LocUpService");
}
@Override
protected void onHandleIntent(Intent intent) {
Coords coords = LocationUpdater.getLastKnownLocation(mApiClient);
}
}
LocationUpdater是另一个class,里面包含静态方法getLastKnownLocation,就是这个:
public static Coords getLastKnownLocation(GoogleApiClient apiClient) {
Coords coords = new Coords();
Location location = LocationServices.FusedLocationApi
.getLastLocation(apiClient);
if (location != null) {
coords.setLatitude(location.getLatitude());
coords.setLongitude(location.getLongitude());
Log.e("lat ", location.getLatitude() + " degrees");
Log.e("lon ", location.getLongitude() + " degrees");
}
return coords;
}
但是惊喜!!当我清楚地将引用传递给静态方法时,我得到了 "IllegalArgumentException: GoogleApiClient parameter is required",我再次猜测这一定与 GoogleApiClient 实例有关,该实例与 Activity 的生命周期有关,并且将实例传递到 IntentService 时出现问题。
所以我在想:如何在不发疯的情况下每五分钟定期更新一次位置信息?我是否扩展服务,在该组件上实现所有接口回调,在其中构建 GoogleApiClient 实例并将其 运行 保留在后台?我是否让 AlarmManager 启动一项服务,该服务每五分钟扩展一次 IntentService 以完成工作,再次在 IntentService 中构建所有相关回调和 GoogleApiClient?我是否继续做我现在正在做的事情,但将 GoogleApiClient 构造为单例,并期望它会有所作为?你会怎么做?
感谢并抱歉这么长篇大论。
I am currently working on an app that has to check the user's location every five minutes and send the coordinates to a server. I decided to go with the FusedLocation API in Google Play Services instead of the plain old LocationManager API
我们的应用程序具有完全相同的要求,我在几天前实现了它,这就是我的实现方式。
在启动 activity 或任何您想启动的地方,使用 AlarmManager 将 LocationTracker 配置为每 5 分钟 运行。
private void startLocationTracker() {
// Configure the LocationTracker's broadcast receiver to run every 5 minutes.
Intent intent = new Intent(this, LocationTracker.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis(),
LocationProvider.FIVE_MINUTES, pendingIntent);
}
LocationTracker.java
public class LocationTracker extends BroadcastReceiver {
private PowerManager.WakeLock wakeLock;
@Override
public void onReceive(Context context, Intent intent) {
PowerManager pow = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pow.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
wakeLock.acquire();
Location currentLocation = LocationProvider.getInstance().getCurrentLocation();
// Send new location to backend. // this will be different for you
UserService.registerLocation(context, new Handlers.OnRegisterLocationRequestCompleteHandler() {
@Override
public void onSuccess() {
Log.d("success", "UserService.RegisterLocation() succeeded");
wakeLock.release();
}
@Override
public void onFailure(int statusCode, String errorMessage) {
Log.d("error", "UserService.RegisterLocation() failed");
Log.d("error", errorMessage);
wakeLock.release();
}
}, currentLocation);
}
}
LocationProvider.java
public class LocationProvider {
private static LocationProvider instance = null;
private static Context context;
public static final int ONE_MINUTE = 1000 * 60;
public static final int FIVE_MINUTES = ONE_MINUTE * 5;
private static Location currentLocation;
private LocationProvider() {
}
public static LocationProvider getInstance() {
if (instance == null) {
instance = new LocationProvider();
}
return instance;
}
public void configureIfNeeded(Context ctx) {
if (context == null) {
context = ctx;
configureLocationUpdates();
}
}
private void configureLocationUpdates() {
final LocationRequest locationRequest = createLocationRequest();
final GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build();
googleApiClient.registerConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
startLocationUpdates(googleApiClient, locationRequest);
}
@Override
public void onConnectionSuspended(int i) {
}
});
googleApiClient.registerConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
});
googleApiClient.connect();
}
private static LocationRequest createLocationRequest() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(FIVE_MINUTES);
return locationRequest;
}
private static void startLocationUpdates(GoogleApiClient client, LocationRequest request) {
LocationServices.FusedLocationApi.requestLocationUpdates(client, request, new com.google.android.gms.location.LocationListener() {
@Override
public void onLocationChanged(Location location) {
currentLocation = location;
}
});
}
public Location getCurrentLocation() {
return currentLocation;
}
}
我首先在扩展应用程序的 class 中创建 LocationProvider 的实例,在应用程序启动时创建实例:
MyApp.java
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
LocationProvider locationProvider = LocationProvider.getInstance();
locationProvider.configureIfNeeded(this);
}
}
LocationProvider 被实例化并配置为仅一次位置更新,因为它是单例。每 5 分钟它会更新它的 currentLocation
值,我们可以使用
Location loc = LocationProvider.getInstance().getCurrentLocation();
运行 不需要任何类型的后台服务。 AlarmManager 将每 5 分钟向 LocationTracker.onReceive() 广播一次,部分唤醒锁将确保代码将完成 运行ning,即使设备处于待机状态。这也是节能的。
请注意,您需要以下权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- For keeping the LocationTracker alive while it is doing networking -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
别忘了注册接收者:
<receiver android:name=".LocationTracker" />
关于您使用 Activity 请求位置更新的第一种方法,除非您在 activity 的 onPause() 方法中断开位置客户端,否则它们不应停止。因此,只要您的 activity 在 background/foreground 中,您就应该继续接收位置更新。但是如果 activity 被破坏了那么你当然不会得到更新。
检查您是否在 activity 生命周期中断开位置客户端。