向requestLocationUpdates发送额外的intentService打破位置更新 [英] sending extra to requestLocationUpdates intentService breaks location updates

本文介绍了向requestLocationUpdates发送额外的intentService打破位置更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法发送额外的字符串,并将 PendingIntent 传递给 LocationServices.FusedLocationApi.requestLocationUpdates(GoogleApiClient客户端,LocationRequest请求, 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,在intent中发现的键:+ TextUtils.join(,,extras.keySet()));

字符串用户名= intent.getStringExtra(username);

if(username!= null){
Log.d(TAG,username:+ username);
} else {
Log.d(TAG,username:null); $!

$ b if(!intent.hasExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED)){
Log.d(TAG,intent does not have 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

主要活动类:

 <$ c 
$ b布尔mLocationUpdatesEnabled = false;

保护void createLocationRequest(){
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(LOCATION_UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(LOCATION_UPDATE_FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);


保护无效startLocationUpdates(){

Log.d(标签,startng位置更新...);

mLocationUpdatesEnabled = true;

if(mLocationRequest == null){
createLocationRequest();
}

//创建使用WebViewActivity处理结果的Intent
Intent mRequestLocationUpdatesIntent = new Intent(this,LocationUpdateService.class);

//创建一个PendingIntent
mRequestLocationUpdatesPendingIntent = PendingIntent.getService(getApplicationContext(),0,
mRequestLocationUpdatesIntent,
PendingIntent.FLAG_CANCEL_CURRENT);

//请求位置更新
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest,
mRequestLocationUpdatesPendingIntent);

Log.d(TAG,位置更新开始);


保护无效stopLocationUpdates(){

Log.d(标记,停止位置更新...);

mLocationUpdatesEnabled = false;

LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient,
mRequestLocationUpdatesPendingIntent);

Log.d(TAG,停止位置更新);
}

这一切都很好,当用户按下按钮时,调用 toggleLocationUpdates ,调用 LocationServices.FusedLocationApi.requestLocationUpdates ,它调用我的 LocationUpdateService 我可以获取位置。

当我试图在我的 Intent 使用Intent.putExtra(String,String):

主要活动类:

  ... 
保护无效startLocationUpdates(字符串用户名){
....

//创建使用WebViewActivity处理结果的意图
Intent mRequestLocationUpdatesIntent = new Intent(this,LocationUpdateService.class);

////////////////////////////////////////// ////////////////////////
//
//当我把这个额外的,IntentService看到我的用户名额外
//但是parcelableExtra`location` == null :(
//
////////////////////////// ////////////////////

mRequestLocationUpdatesIntent。 putExtra(username,username);
...
}
...

编辑 我已经开始下一个句子,而不是一个问题:我正在使用...



我是否使用正确的方法将一些额外的数据发送到此位置更新处理 IntentService 或者有更多的方法去处理这是什么?



这是一个错误还是只是糟糕的文档?

解决方案

将IntentService与FusedLocationProviderAPI结合使用会带来问题。从标题为 Receipt的开发人员文档位置更新


根据请求的形式,融合位置提供程序
或者调用 LocationListener.onLocationChanged()回调
方法并将它传递给一个Location对象,或者发出一个 PendingIntent
包含扩展数据中的位置。更新的准确性和频率
受到您
请求的位置权限以及您在位置请求对象中设置的选项的影响


此外, PendingIntent 用于扩展另一段代码的权限( FusedLocationProviderAPI 在谷歌播放服务)在您的apk内执行他们的代码。一个 IntentService 用于启动在您的apk范围内定义的服务。

因此,该方法需要为前台更新实现 LocationListener ,或者 PendingIntent 用于与广播接收器配合使用的后台更新。

这是一些用于从 PendingIntent 请求位置更新以及额外值的方法的示例。注意: LocalStorage.java 是存储局部变量的工具类,它不是Android API



GPSPlotter

  / ** 
*使用
* LocationServices Api初始化Google Api客户端的私人帮助程序方法并构建它以供使用。
* /
private void initializeGoogleApiClient(){
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();

}

/ **
*用于确定GooglePlayServices
*是否安装在本地系统上的专用辅助方法。
*
* @return服务已安装。
* /
private boolean googlePlayServicesInstalled(){
int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
返回结果== ConnectionResult.SUCCESS;
}

/ **
*使用LocationServices API构建Api客户端的私有方法。
* /
私有同步void buildApiClient(){
Log.w(TAG,构建Google Api客户端...);
initializeGoogleApiClient();
}

/ **
*用于将ApiClient连接到Google为
托管的API的私有方法*访问位置。
* /
private void connectClient(){
mGoogleApiClient.connect();
}

/ **
*用户在请求的间隔轮询时间内以秒为单位传递一个
*整数。
*
* @param theAccount是对用于更新视图的父活动的引用。
* /
public void beginManagedLocationRequests(MyAccount theAccount){
if(mAccount == null)
mAccount = theAccount;

startBackgroundUpdates();

}

/ **
*公共方法结束托管的位置请求。
* /
public void endManagedLocationRequests(){
endBackgroundUpdates();

}

/ **
*此方法通过停止然后再次启动
*背景udpates来处理轮询速率的切换,依次在调用堆栈中的另一个方法中设置间隔。
* @param theInterval期望的间隔轮询率
* /
public void changeRequestIntervals(int theInterval){
mIntentInterval = theInterval;
if(LocalStorage.getRequestingBackgroundStatus(mContext)){
endBackgroundUpdates();
startBackgroundUpdates();
}



}

/ **
*建立Intent的私人助手方法将与待处理意图使用
*发出后台位置请求。
*
* @return theIntent
* /
private Intent buildBackgroundRequestIntent(){
Intent intent = new Intent(mContext,BackgroundLocationReceiver.class);
intent.setAction(BACKGROUND_ACTION);
intent.putExtra(User.USER_ID,mUserID);
返回意图;
}
$ b $ ** ** b $ b *用于在用户请求后台服务
*时在FusedLocationApi内生成PendingIntent的专用辅助方法,直到间隔更改。
*
* @return pendingIntent
* /
private PendingIntent buildRequestPendingIntent(Intent theIntent){
Log.w(TAG,building pending intent);
返回PendingIntent.getBroadcast(mContext,0,theIntent,0);
}


/ **
*在后台使用FusedLocation API启动位置更新的私有方法。
* /
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 void endBackgroundUpdates(){
Log.w(TAG,Ending background updates);
LocalStorage.putBackgroundRequestStatus(false,mContext);
LocalStorage.putLocationRequestStatus(false,mContext);
unregisterAlarmManager();
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,buildRequestPendingIntent(buildBackgroundRequestIntent()));
}

BackgroundLocationReceiver $ b

  public class BackgroundLocationReceiver extends BroadcastReceiver {
private static final String TAG =BLocRec:;
private static final String UPLOAD_ERROR_MESSAGE =上传坐标后台服务失败。;
private static final String UPLOAD_MESSAGE =坐标批处理推送到数据库。

public BackgroundLocationReceiver(){
//默认,无参数构造函数
}
$ b $ / **
*此方法处理任何在应用程序不再焦点时收到的位置更新。坐标是
*存储在本地数据库中并且每小时上传一次。
* @param context应用程序上下文
* @param intent是待处理的意图
* /
@Override
public void onReceive(Context context,Intent intent){如果(intent.getAction()。matches(GPSPlotter.BACKGROUND_ACTION)){
Log.w(TAG,BLR Received-background);
位置位置= intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
storeLocation(location,context,intent.getStringExtra(User.USER_ID));





$编辑

下面的方法创建了调用 requestLocationUpdates()方法所必需的LocationRequest

  / ** 
*用于生成LocationRequest的私有助手方法,该方法将用于处理FusedLocationApi中的所有位置更新
*,直到间隔改变。
*
* @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(标签,建筑物位置请求);
返回locationRequest;
}

编辑
After在与Catherine聊天的长时间讨论中,我们得出的结论是,Google Play服务库7.5有一个错误,当其他附加内容放入Intent时,它不处理从FusedLocationProviderAPI传递的Parcelable Extra Location。但是,7.0确实提供了这种功能。她表示她会提交一个错误,我们会看到Android团队需要多长时间才能解决问题。


I'm having trouble sending a string extra with my PendingIntent that I pass to LocationServices.FusedLocationApi.requestLocationUpdates(GoogleApiClient client, LocationRequest request, PendingIntent callbackIntent).

It appears that the username extra i'm putting onto the Intent is mangling the location that requestLocationUpdates is trying to hand off to my IntentService as intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED) returns null.

EDIT

I've tried making a User class that implements Parcelable and putting it as an extra:

mRequestLocationUpdatesIntent.putExtra("username", new User(username));

and I've also tried to put the Parcelable User inside a Bundle as suggested via comment in this bug report https://code.google.com/p/android/issues/detail?id=81812:

Bundle userBundle = new Bundle();
userBundle.putParcelable("user", new User(username));
mRequestLocationUpdatesIntent.putExtra("user", userBundle);

in my service:

Bundle userBundle = intent.getBundleExtra("user");
User user = userBundle.getParcelable("user");
String username = user.getUsername();

However neither of these approaches has made any difference. Whenever I put any extra onto my intent, the location is never added to the intent when the updates occur.

I setup this IntentService to handle location updates:

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()));

        ...

    }


}

When the user clicks a button, the startLocationUpdates is called in my main activity:

main activity class:

...

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");
}

This all works well and good; When the user presses the button, toggleLocationUpdates is called, which calls LocationServices.FusedLocationApi.requestLocationUpdates which calls my LocationUpdateService where I'm able to get the location.

The trouble comes when I tried to put a string extra onto my Intent using Intent.putExtra(String, String):

main activity class:

...
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);
    ...
}
...

EDIT I had started the next sentence as a statement rather than a question: "I am using..."

Am I using the correct approach to sending some extra data to this location update handling IntentService or is there a more-sane way to go about this?

Is this a bug or just poor documentation?

解决方案

Using the IntentService coupled with the FusedLocationProviderAPI will present issues. From the Developer Docs titled Receiving Location Updates:

Depending on the form of the request, the fused location provider either invokes the LocationListener.onLocationChanged() callback method and passes it a Location object, or issues a PendingIntent that contains the location in its extended data. The accuracy and frequency of the updates are affected by the location permissions you've requested and the options you set in the location request object

Further, a PendingIntent is used for extending permissions for another piece of code (FusedLocationProviderAPI in Google Play Services) to execute their code within your apk. An IntentService is used to start a Service defined within the scope of your apk.

So, the method requires an implementation of LocationListener for foreground updates, or a PendingIntent for background updates coupled with a Broadcast Receiver.

This is a working example of some methods used to request location updates from a PendingIntent coupled with extra values.

Note: LocalStorage.java is a utility class for storing local variables, it is not part of the Android API

GPSPlotter

/**
 * 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));

    }

EDIT The method below builds a LocationRequest necessary for invoking the requestLocationUpdates() method

/**
 * 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;
}

EDIT After a long discussion in chat with Catherine, we came to the conclusion that google play services library 7.5 has a bug that does not process the Parcelable Extra Location passed from FusedLocationProviderAPI when other extras are put into the Intent. However, 7.0 does provide this capability. She said that she will submit a bug and we'll see how long it takes the Android team to resolve

这篇关于向requestLocationUpdates发送额外的intentService打破位置更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆