向 requestLocationUpdates intentService 发送额外信息会中断位置更新
Posted
技术标签:
【中文标题】向 requestLocationUpdates intentService 发送额外信息会中断位置更新【英文标题】:sending extra to requestLocationUpdates intentService breaks location updates 【发布时间】:2015-08-31 15:24:09 【问题描述】:我无法使用我的PendingIntent
发送额外的字符串,我将其传递给LocationServices.FusedLocationApi.requestLocationUpdates(GoogleApiClient client, LocationRequest request, PendingIntent callbackIntent)
。
我在Intent
上添加的用户名似乎正在破坏requestLocationUpdates
试图移交给我的IntentService
的位置,因为intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED)
返回null
。
编辑
我尝试创建一个实现Parcelable
的User
类并将其作为额外的:
mRequestLocationUpdatesIntent.putExtra("username", new User(username));
我还尝试将Parcelable User
放入Bundle
中,正如此错误报告https://code.google.com/p/android/issues/detail?id=81812 中的评论所建议的那样:
Bundle userBundle = new Bundle();
userBundle.putParcelable("user", new User(username));
mRequestLocationUpdatesIntent.putExtra("user", userBundle);
为我服务:
Bundle userBundle = intent.getBundleExtra("user");
User user = userBundle.getParcelable("user");
String username = user.getUsername();
但是,这些方法都没有任何区别。每当我在意图中添加任何额外内容时,更新发生时该位置永远不会添加到意图中。
我设置了这个IntentService
来处理位置更新:
public class LocationUpdateService extends IntentService
private final String TAG = "LocationUpdateService";
public LocationUpdateService()
super("LocationUpdateService");
@Override
protected void onHandleIntent(Intent intent)
Log.d(TAG, "onHandleIntent");
Bundle extras = intent.getExtras();
Log.d(TAG, "keys found inside intent: " + TextUtils.join(", ", extras.keySet()));
String username = intent.getStringExtra("username");
if (username != null)
Log.d(TAG, "username: " + username);
else
Log.d(TAG, "username: null");
if (!intent.hasExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED))
Log.d(TAG, "intent does not have location :(");
Location location = intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
if (location == null)
Log.d(TAG, "location == null :(");
Log.d(TAG, "latitude " + String.valueOf(location.getLatitude()));
Log.d(TAG, "longitude " + String.valueOf(location.getLongitude()));
...
当用户点击一个按钮时,startLocationUpdates
在我的主要活动中被调用:
主要活动类:
...
Boolean mLocationUpdatesEnabled = false;
protected void createLocationRequest()
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(LOCATION_UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(LOCATION_UPDATE_FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
protected void startLocationUpdates()
Log.d(TAG, "startng location updates...");
mLocationUpdatesEnabled = true;
if (mLocationRequest == null)
createLocationRequest();
// create the Intent to use WebViewActivity to handle results
Intent mRequestLocationUpdatesIntent = new Intent(this, LocationUpdateService.class);
// create a PendingIntent
mRequestLocationUpdatesPendingIntent = PendingIntent.getService(getApplicationContext(), 0,
mRequestLocationUpdatesIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
// request location updates
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest,
mRequestLocationUpdatesPendingIntent);
Log.d(TAG, "location updates started");
protected void stopLocationUpdates()
Log.d(TAG, "stopping location updates...");
mLocationUpdatesEnabled = false;
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient,
mRequestLocationUpdatesPendingIntent);
Log.d(TAG, "location updates stopped");
这一切都很好,很好;当用户按下按钮时,toggleLocationUpdates
会被调用,LocationServices.FusedLocationApi.requestLocationUpdates
会调用我的LocationUpdateService
,我可以在其中获取位置。
当我尝试使用 Intent.putExtra(String, String) 将额外的字符串添加到我的 Intent
时,问题就来了:
主要活动类:
...
protected void startLocationUpdates(String username)
....
// create the Intent to use WebViewActivity to handle results
Intent mRequestLocationUpdatesIntent = new Intent(this, LocationUpdateService.class);
//////////////////////////////////////////////////////////////////
//
// When I put this extra, IntentService sees my username extra
// but the parcelableExtra `location` == null :(
//
//////////////////////////////////////////////////////////////////
mRequestLocationUpdatesIntent.putExtra("username", username);
...
...
编辑 我将下一个句子作为陈述而不是问题开始:“我正在使用......”
我是否使用正确的方法向此位置更新处理 IntentService
发送一些额外的数据,还是有更理智的方法来解决这个问题?
这是一个错误还是只是糟糕的文档?
【问题讨论】:
@BladeCoder 为我提供了一些指导,以回应我在 Android 开发 Google+ 社区中的帖子:plus.google.com/117366723702848823459/posts/6QAmns2pQCT 一旦我弄清楚了我会发布我的答案 一个澄清的问题,它打印出"location == null :("
- 即hasExtra()
返回false
,还是intent.getParcelableExtra()
只是返回null?
如果我删除 return 语句,我会得到 java.lang.NullPointerException: Attempt to invoke virtual method 'double android.location.Location.getLatitude()' on a null object reference
我编辑了 IntentService 以提供详细的日志记录,显示 Intent.hasExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED
返回 false,location == null
,并引发空对象引用异常。
我也得到了这个日志输出keys found inside intent: username
;但是,当我没有将“用户名”放在意图上时,我会得到以下输出:keys found inside intent: com.google.android.location.LOCATION, com.google.android.gms.location.EXTRA_LOCATION_RESULT
Location Client request location updates with parcelable extras in PendingIntent的可能重复
【参考方案1】:
将 IntentService 与 FusedLocationProviderAPI 结合使用会出现问题。来自标题为接收位置更新的开发者文档:
根据请求的形式,融合的位置提供者 要么调用
LocationListener.onLocationChanged()
回调 方法并传递给它一个 Location 对象,或者发出一个PendingIntent
在其扩展数据中包含该位置。精度和频率 的更新受您拥有的位置权限的影响 请求和您在位置请求对象中设置的选项
此外,PendingIntent
用于扩展另一段代码(Google Play 服务中的FusedLocationProviderAPI
)的权限,以在您的 apk 中执行其代码。 IntentService
用于启动在您的 apk 范围内定义的服务。
因此,该方法需要实现LocationListener
用于前台更新,或PendingIntent
用于后台更新以及广播接收器。
这是用于从PendingIntent
请求位置更新以及额外值的一些方法的工作示例。
注意:LocalStorage.java
是一个用于存储局部变量的实用程序类,它不是 Android API 的一部分
GPS绘图仪
/**
* Private helper method to initialize the Google Api Client with the
* LocationServices Api and Build it for use.
*/
private void initializeGoogleApiClient()
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
/**
* Private helper method to determine whether or not GooglePlayServices
* are installed on the local system.
*
* @return services are installed.
*/
private boolean googlePlayServicesInstalled()
int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
return result == ConnectionResult.SUCCESS;
/**
* Private method to build the Api Client for use with the LocationServices API.
*/
private synchronized void buildApiClient()
Log.w(TAG, "Building Google Api Client...");
initializeGoogleApiClient();
/**
* Private method used to connect the ApiClient to the Api hosted by Google for
* Accessing Locations.
*/
private void connectClient()
mGoogleApiClient.connect();
/**
* User passes in a requested interval polling time in seconds as an
* integer.
*
* @param theAccount is a reference to the parent activity used for updating views.
*/
public void beginManagedLocationRequests(MyAccount theAccount)
if (mAccount == null)
mAccount = theAccount;
startBackgroundUpdates();
/**
* Public method to end the managed Location Requests.
*/
public void endManagedLocationRequests()
endBackgroundUpdates();
/**
* This method handles the switch in polling rates by stopping and then starting once more the
* background udpates, which in turn sets the interval in another method in the call stack.
* @param theInterval the desired interval polling rate
*/
public void changeRequestIntervals(int theInterval)
mIntentInterval = theInterval;
if (LocalStorage.getRequestingBackgroundStatus(mContext))
endBackgroundUpdates();
startBackgroundUpdates();
/**
* Private helper method to build an Intent that will be couple with a pending intent uses
* for issuing background Location requests.
*
* @return theIntent
*/
private Intent buildBackgroundRequestIntent()
Intent intent = new Intent(mContext, BackgroundLocationReceiver.class);
intent.setAction(BACKGROUND_ACTION);
intent.putExtra(User.USER_ID, mUserID);
return intent;
/**
* Private helper method used to generate a PendingIntent for use when the User requests background service
* within the FusedLocationApi until the Interval is changed.
*
* @return pendingIntent
*/
private PendingIntent buildRequestPendingIntent(Intent theIntent)
Log.w(TAG, "building pending intent");
return PendingIntent.getBroadcast(mContext, 0, theIntent, 0);
/**
* Private method to start the Location Updates using the FusedLocation API in the background.
*/
private void startBackgroundUpdates()
Log.w(TAG, "Starting background updates");
if (googlePlayServicesInstalled())
LocalStorage.putBackgroundRequestStatus(true, mContext);
LocalStorage.putLocationRequestStatus(true, mContext);
registerAlarmManager();
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, buildLocationRequest(), buildRequestPendingIntent(buildBackgroundRequestIntent()));
/**
* Private method to end background updates.
*/
private void endBackgroundUpdates()
Log.w(TAG, "Ending background updates");
LocalStorage.putBackgroundRequestStatus(false, mContext);
LocalStorage.putLocationRequestStatus(false, mContext);
unregisterAlarmManager();
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, buildRequestPendingIntent(buildBackgroundRequestIntent()));
BackgroundLocationReceiver
public class BackgroundLocationReceiver extends BroadcastReceiver
private static final String TAG = "BLocRec: ";
private static final String UPLOAD_ERROR_MESSAGE = "Background Service to Upload Coordinates Failed.";
private static final String UPLOAD_MESSAGE = "Coordinate Batch Pushed to Database.";
public BackgroundLocationReceiver()
//Default, no-arg constructor
/**
* This method handles any location updates received when the app is no longer in focus. Coordinates are
* stored in the local database and uploaded once every hour.
* @param context the application context
* @param intent is the pending intent
*/
@Override
public void onReceive(Context context, Intent intent)
if (intent.getAction().matches(GPSPlotter.BACKGROUND_ACTION))
Log.w(TAG, "BLR Received-background");
Location location = intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
storeLocation(location, context, intent.getStringExtra(User.USER_ID));
编辑
下面的方法构建了调用 requestLocationUpdates()
方法所必需的 LocationRequest
/**
* Private helper method used to generate a LocationRequest which will be used to handle all location updates
* within the FusedLocationApi until the Interval is changed.
*
* @return locationRequest
*/
private LocationRequest buildLocationRequest()
int dateConversion = 1000;
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(mIntentInterval * dateConversion);
locationRequest.setFastestInterval((mIntentInterval / 2) * dateConversion);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
Log.w(TAG, "Building location request");
return locationRequest;
编辑 经过与 Catherine 的长时间讨论后,我们得出的结论是 google play services library 7.5 存在一个 bug,即当其他 extras 放入 Intent 时,不会处理从 FusedLocationProviderAPI 传递的 Parcelable Extra Location。但是,7.0 确实提供了这种能力。她说她会提交一个bug,我们看看Android团队需要多长时间才能解决
【讨论】:
> 将 IntentService 与 FusedLocationProviderAPI 结合使用会出现问题。你从哪里得到这些信息的?我查看并查看了融合位置提供程序和 IntentService 的 Android 文档,但都没有说它们不兼容或它们的组合可能会造成麻烦。也许 Google 的文档 do 很糟糕......:' ( > 因此,该方法需要实现 LocationListener 用于前台更新,或 PendingIntent 用于后台更新与广播接收器耦合。同样,我从未在文档中看到任何说需要广播接收器的内容。请您解释一下原因,或者给我一个解释为什么必须使用广播接收器的文档。 @Catherine,我忘记在发布的第一个答案中包含创建 LocationRequest 的示例。这可能会有所帮助。我唯一能想到的另一件事是设备上的 GPS 可能没有“开启”? @andrewdleach GPS 绝对开启。当我在意图中包含额外内容时,我不会得到位置,如果我取出额外内容,我就会得到位置。 即使在 10.x 版本中问题仍然存在以上是关于向 requestLocationUpdates intentService 发送额外信息会中断位置更新的主要内容,如果未能解决你的问题,请参考以下文章
requestLocationUpdates() 花费太多时间或根本不起作用
为啥 requestLocationUpdates 会在接收时立即触发?
如何等待 requestLocationUpdates() 回调意图完成?
当两个 locationManager 对象调用 requestLocationUpdates(...) 时?