应用在后台时获取位置更新

时间:2018-04-24 11:50:37

标签: android location fusedlocationproviderapi

我正在尝试创建一项服务,该应用程序在应用程序处于专用于Android 8及更高版本的后台时提供位置更新,因为它们已优化后台服务。我在stackOverflow和Google API doc上阅读了一些答案,他们提到将PendingIntent传递给onRequestLocationUpdates即使在app处于后台时也会给出位置。正如doc中提到的那样。这是我的服务类:

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时,它向我抛出一个错误的GoogleAPIClient未连接。

以下是我得到的错误

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)

2 个答案:

答案 0 :(得分:0)

我可以看到您已在GoogleApiClient中设置了onStartCommand()个实例,并且正如服务生命周期所说,每次启动服务时都会调用onStartCommand()。因此,每次实例化您的GoogleApiClient实例。 我认为这会在您的代码中产生冲突。让我们考虑一个理解问题的场景。

  1. 您已经开始服务一次,一秒钟之后又重新开始服务。因此,它会同时启动mLocationClient,因为您已在onStartCommand()方法中启动它。假设第一个实例的值为mLocationClient1,而另一个实例的值为mLocationClient2。(这个值只是为了简化说明)。

  2. 成功连接mLocationClient1后,它会触发onConnected()方法,并在其中使用mLocationClient,它由尚未连接的新值mLocationClient2初始化。因此,它返回了googleapiclient未连接的错误。

  3. 如果您认为自己只启动了一次服务,那么它为什么多次调用onStartCommand()是一个答案。每当您从最近的应用程序列表中删除您的应用程序时,将停止您的服务,但您已创建START_STICKY服务,因此,重新启动您的服务并再次调用onStartCommand()方法。

    因此,我建议您在mLocationClient方法而不是onCreate()中设置onStartCommand(),因为只有在第一次创建服务后才会调用onCreate()方法&#39被毁坏了。

答案 1 :(得分:-1)

我遇到了同样的问题,尝试在Android Studio模拟器中运行代码,而不是其他模拟器。因此,您需要在设备中安装Google Play服务。