要在后台检索fused location,我创建了一个与Commonsguy创建的cwac-locpoll库非常相似的库。
在PollerThread内部,我正在尝试使用LocationClient
连接,请求和检索位置。
我可以通过接收onConnected
方法的回调来连接,但我无法在onLocationChanged
方法上获得回调。因此我的onTimeout
线程按照确定的时间间隔执行。< / p>
注意: 只有当屏幕指示灯熄灭时才会出现此问题。否则它会完全正常工作。
我怀疑新的位置Api可能存在错误。
以下是我的PollerThread
,
private class PollerThread extends WakefulThread implements GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,LocationListener{
private static final String TAG = "PollerThread";
//context
private Context mContext=null;
private LocationClient mLocationClient=null;
private LocationRequest mLocationRequest=null;
private LocationManager locMgr=null;
private Intent intentTemplate=null;
private Handler handler=new Handler();
private Runnable onTimeout = new Runnable() {
@Override
public void run() {
Log.e(TAG, "onTimeout");
//prepare broadcast intent
Intent toBroadcast=new Intent(intentTemplate);
toBroadcast.putExtra(FusedPoller.EXTRA_ERROR, "Timeout!");
toBroadcast.putExtra(
FusedPoller.EXTRA_ERROR_PROVIDER_DISABLED, false);
toBroadcast.putExtra(FusedPoller.EXTRA_LASTKNOWN,
mLocationClient.getLastLocation());
sendBroadcast(toBroadcast);
//stop the thread
quit();
}
};
PollerThread(Context mContext,LocationRequest mLocationRequest,PowerManager.WakeLock lock, LocationManager locMgr,
Intent intentTemplate) {
super(lock, "LocationPoller-PollerThread");
Log.e(TAG, "PollerThread");
this.mContext=mContext;
this.mLocationRequest=mLocationRequest;
this.locMgr=locMgr;
this.intentTemplate=intentTemplate;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.e(TAG, "onPreExecute");
//setup timeout
setTimeoutAlarm();
//initiate connection
initiateConnection();
}
@Override
protected void onPostExecute() {
super.onPostExecute();
Log.e(TAG, "onPostExecute");
//remove timeout
removeTimeoutAlarm();
//disconnect
initiateDisconnection();
}
/**
* Called when the WakeLock is completely unlocked.
* Stops the service, so everything shuts down.
*/
@Override
protected void onUnlocked() {
Log.e(TAG, "onUnlocked");
stopSelf();
}
private void setTimeoutAlarm() {
Log.e(TAG, "setTimeoutAlarm");
handler.postDelayed(onTimeout, FusedLocationUtils.DEFAULT_TIMEOUT);
}
private void removeTimeoutAlarm()
{
Log.e(TAG, "removeTimeoutAlarm");
handler.removeCallbacks(onTimeout);
}
private void initiateConnection()
{
Log.e(TAG, "initiateConnection");
mLocationClient = new LocationClient(this.mContext, this, this);
mLocationClient.connect();
}
private void initiateDisconnection()
{
Log.e(TAG, "initiateDisconnection");
if(mLocationClient.isConnected())
{
mLocationClient.disconnect();
}
}
@Override
public void onConnected(Bundle arg0) {
Log.e(TAG, "onConnected");
Log.e(TAG, "provider: GPS-"+locMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)+" NETWORK-"+locMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER));
if (!(locMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)) && !(locMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) {
Log.e(TAG, "both disabled");
//get last location and broadcast it
getLastLocationAndBroadcast();
//stop the thread
quit();
}
else
{
Log.e(TAG, "provider enabled");
//get latest location and broadcast it
getLatestLocationAndBroadcast();
//don't quit from here,quit from onLocationChanged
}
}
@Override
public void onDisconnected() {
Log.e(TAG, "onDisconnected");
// TODO Auto-generated method stub
}
@Override
public void onConnectionFailed(ConnectionResult arg0) {
Log.e(TAG, "onConnectionFailed");
// TODO Auto-generated method stub
}
@Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged");
//prepare broadcast intent
Intent toBroadcast=new Intent(intentTemplate);
toBroadcast.putExtra(FusedPoller.EXTRA_LOCATION, location);
sendBroadcast(toBroadcast);
//stop further updates
stopUpdates();
//stop the thread
quit();
}
private void getLatestLocationAndBroadcast() {
Log.e(TAG, "getLatestLocationAndBroadcast");
if(mLocationClient.isConnected() && servicesConnected())
{
Log.e(TAG, "going to request updates");
Log.e(TAG, "lockStatic.isHeld(): "+lockStatic.isHeld());
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
else
{
Log.e(TAG, "not going to request updates");
}
}
private void stopUpdates() {
Log.e(TAG, "stopUpdates");
if(servicesConnected())
{
Log.e(TAG,getString(R.string.location_updates_stopped));
mLocationClient.removeLocationUpdates(this);
}
else
{
Log.e(TAG,"can't do:"+getString(R.string.location_updates_stopped));
}
}
private void getLastLocationAndBroadcast() {
Log.e(TAG, "getLastLocationAndBroadcast");
if(mLocationClient.isConnected() && servicesConnected())
{
Log.e(TAG, "going to get last location: "+mLocationClient.getLastLocation());
Intent toBroadcast = new Intent(intentTemplate);
toBroadcast.putExtra(FusedPoller.EXTRA_ERROR,
"Location Provider disabled!");
toBroadcast.putExtra(
FusedPoller.EXTRA_ERROR_PROVIDER_DISABLED, true);
toBroadcast.putExtra(FusedPoller.EXTRA_LASTKNOWN,
mLocationClient.getLastLocation());
sendBroadcast(toBroadcast);
}
else
{
Log.e(TAG, "not going to get last location");
}
}
}
和servicesConnected
方法实现,
/**
* Verify that Google Play services is available before making a request.
*
* @return true if Google Play services is available, otherwise false
*/
private boolean servicesConnected() {
Log.e(TAG, "servicesConnected");
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d(FusedLocationUtils.APPTAG, getString(R.string.play_services_available));
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Display an error dialog
Log.d(FusedLocationUtils.APPTAG, getString(R.string.play_services_unavailable));
Toast.makeText(this, getString(R.string.play_services_unavailable), Toast.LENGTH_SHORT).show();
return false;
}
}
答案 0 :(得分:18)
如果您想在后台收听频繁的位置更新(例如,每秒),您应该在Service
内运行代码:
http://developer.android.com/reference/android/app/Service.html
Android平台可以在不在前台的任何时间点结束活动。
使用服务时,我建议让服务直接实现LocationListener,而不是服务中的Thread。例如,使用:
public class LocListener extends Service implements com.google.android.gms.location.LocationListener, ...{
我已经使用这种设计在我的GPS Benchmark app中使用LocationClient
和融合位置提供程序直接在服务上实现LocationListener,我可以确认即使屏幕关闭并且应用程序正在后台运行。
如果您想使用融合位置提供程序在后台(例如,每分钟)收听偶尔的位置更新,则更好的设计是使用PendingIntents,使用LocationClient.requestLocationUpdates(Location Request, PendingIntent callbackIntent)
方法:
来自上面的Android文档:
此方法适用于后台用例,更具体地说,适用于接收位置更新,即使应用程序已被系统杀死也是如此。为此,请对已启动的服务使用PendingIntent。对于前台用例,建议使用该方法的LocationListener版本,请参阅requestLocationUpdates(LocationRequest,LocationListener)。
此PendingIntent上注册的任何先前LocationRequests都将被替换。
使用KEY_LOCATION_CHANGED键和意图上的位置值发送位置更新。
有关在后台运行时使用PendingIntents
获取更新的更详细说明,请参阅活动识别示例:
https://developer.android.com/training/location/activity-recognition.html
以下是本文档的修改摘录,由我更改为特定于位置更新。
首先声明意图:
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
...
/*
* Store the PendingIntent used to send location updates
* back to the app
*/
private PendingIntent mLocationPendingIntent;
// Store the current location client
private LocationClient mLocationClient;
...
}
按照您目前的要求更新请求,但这次要传递待处理的意图:
/*
* Create the PendingIntent that Location Services uses
* to send location updates back to this app.
*/
Intent intent = new Intent(
mContext, LocationIntentService.class);
...
//Set up LocationRequest with desired parameter here
...
/*
* Request a PendingIntent that starts the IntentService.
*/
mLocationPendingIntent =
PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
/*
* Request location updates
*/
mLocationClient.requestLocationUpdates(mLocationRequest, callbackIntent);
处理位置更新
要处理位置服务为每个更新间隔发送的Intent,请在onHandleIntent()上定义IntentService及其所需方法。位置服务使用您在调用requestLocationUpdates()时提供的PendingIntent以Intent对象的形式发送...更新。由于您为PendingIntent提供了明确的意图,因此接收意图的唯一组件是您正在定义的IntentService。
在onHandleIntent()上定义类和所需方法:
/**
* Service that receives Location updates. It receives
* updates in the background, even if the main Activity is not visible.
*/
public class LocationIntentService extends IntentService {
...
/**
* Called when a new location update is available.
*/
@Override
protected void onHandleIntent(Intent intent) {
Bundle b = intent.getExtras();
Location loc = (Location) b.get(LocationClient.KEY_LOCATION_CHANGED);
Log.d(TAG, "Updated location: " + loc.toString());
}
...
}
重要 - 为了尽可能高效,onHandleIntent()
中的代码应尽快返回,以允许IntentService关闭。来自IntentService docs:
在工作线程上调用此方法并处理请求。一次只处理一个Intent,但处理发生在独立于其他应用程序逻辑运行的工作线程上。因此,如果此代码需要很长时间,它将阻止对同一个IntentService的其他请求,但它不会阻止其他任何内容。处理完所有请求后,IntentService会自行停止,因此不应调用stopSelf()。
我对IntentService
设计的理解是,您可以在onHandleIntent()
中生成线程,以避免通过对onHandleIntent()
的平台调用阻止其他位置更新,请注意该服务将继续运行直到所有正在运行的线程终止。
答案 1 :(得分:0)
我花了几天的时间尝试在Nexus 6上使用Android 6.0锁定屏幕上的WiFi和基于单元格的位置。 看起来像原生的android位置服务简单不允许这样做。 一旦设备被锁定,它仍然会收集位置更新事件10-15分钟,然后停止提供任何位置更新。
在我的情况下,解决方案是从原生Android位置服务切换到名为com.google.android.gms.location的Google Play服务包装:https://developers.google.com/android/reference/com/google/android/gms/location/package-summary
是的,我知道有些Android设备缺少GMS,但对于我的应用程序来说,这是唯一可以执行的解决方案。
即使在后台和设备屏幕被锁定,它也不会停止发送位置更新。
我个人更喜欢RxJava库将此服务包装成一个流(包括示例):https://github.com/mcharmas/Android-ReactiveLocation