在后台更新位置并检查 LocationSettings

Update location in background and check LocationSettings

我想每隔几分钟在后台更新一次用户的位置。
我使用了这个示例代码googlesamples/android-play-location
但将其更改为与 Service

一起使用
public class MyService extends Service implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener, ResultCallback<LocationSettingsResult>

但我无法检查位置设置

    protected void checkLocationSettings() {
        PendingResult<LocationSettingsResult> result =
                LocationServices.SettingsApi.checkLocationSettings(
                        mGoogleApiClient,
                        mLocationSettingsRequest
                );
        result.setResultCallback(this);
    }

    @Override
    public void onResult(LocationSettingsResult locationSettingsResult) {
        final Status status = locationSettingsResult.getStatus();
        switch (status.getStatusCode()) {
            case LocationSettingsStatusCodes.SUCCESS:
                Log.i(TAG, "All location settings are satisfied.");
                startLocationUpdates();
                break;
            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                Log.i(TAG, "Location settings are not satisfied. Show the user a dialog to" +
                        "upgrade location settings ");

                try {
                    // Show the dialog by calling startResolutionForResult(), and check the result
                    // in onActivityResult().
                    status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS);
                } catch (IntentSender.SendIntentException e) {
                    Log.i(TAG, "PendingIntent unable to execute request.");
                }
                break;
            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                Log.i(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog " +
                        "not created.");
                break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            // Check for the integer request code originally supplied to startResolutionForResult().
            case REQUEST_CHECK_SETTINGS:
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        Log.i(TAG, "User agreed to make required location settings changes.");
                        startLocationUpdates();
                        break;
                    case Activity.RESULT_CANCELED:
                        Log.i(TAG, "User chose not to make required location settings changes.");
                        break;
                }
                break;
        }
    }

因为这个方法需要Activity

status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS);

如果我将之前的代码放在 MainActvity 中,我将不会有对 mGoogleApiClient 和 mLocationSettingsRequest 的引用

LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, mLocationSettingsRequest);

即使 activity 被破坏,我也想升级并在后台保存位置。
使用服务是正确的做法吗?
以及如何检查位置设置?

[编辑]
我尝试使用 PendingIntentIntentService

LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
                    mLocationRequest, mPendingIntent);

This method is suited for the background use cases, more specifically for receiving location updates, even when the app has been killed by the system.

ServiceLocationListner版本相比有什么不同?

您可以使用位置变化侦听器

  public class LocationService extends Service 
    {
           public static final String BROADCAST_ACTION = "Hello World";
           private static final int TWO_MINUTES = 1000 * 60 * 2;
           public LocationManager locationManager;
           public MyLocationListener listener;
           public Location previousBestLocation = null;

           Intent intent;
           int counter = 0;

        @Override
        public void onCreate() 
        {
            super.onCreate();
            intent = new Intent(BROADCAST_ACTION);      
        }

        @Override
        public void onStart(Intent intent, int startId) 
        {      
            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
            listener = new MyLocationListener();        
            locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 4000, 0, listener);
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 4000, 0, listener);
        }

        @Override
        public IBinder onBind(Intent intent) 
        {
            return null;
        }

        protected boolean isBetterLocation(Location location, Location currentBestLocation) {
            if (currentBestLocation == null) {
                // A new location is always better than no location
                return true;
            }

            // Check whether the new location fix is newer or older
            long timeDelta = location.getTime() - currentBestLocation.getTime();
            boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
            boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
            boolean isNewer = timeDelta > 0;

            // If it's been more than two minutes since the current location, use the new location
            // because the user has likely moved
            if (isSignificantlyNewer) {
                return true;
            // If the new location is more than two minutes older, it must be worse
            } else if (isSignificantlyOlder) {
                return false;
            }

            // Check whether the new location fix is more or less accurate
            int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
            boolean isLessAccurate = accuracyDelta > 0;
            boolean isMoreAccurate = accuracyDelta < 0;
            boolean isSignificantlyLessAccurate = accuracyDelta > 200;

            // Check if the old and new location are from the same provider
            boolean isFromSameProvider = isSameProvider(location.getProvider(),
                    currentBestLocation.getProvider());

            // Determine location quality using a combination of timeliness and accuracy
            if (isMoreAccurate) {
                return true;
            } else if (isNewer && !isLessAccurate) {
                return true;
            } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
                return true;
            }
            return false;
        }



    /** Checks whether two providers are the same */
        private boolean isSameProvider(String provider1, String provider2) {
            if (provider1 == null) {
              return provider2 == null;
            }
            return provider1.equals(provider2);
        }



    @Override
        public void onDestroy() {       
           // handler.removeCallbacks(sendUpdatesToUI);     
            super.onDestroy();
            Log.v("STOP_SERVICE", "DONE");
            locationManager.removeUpdates(listener);        
        }   

        public static Thread performOnBackgroundThread(final Runnable runnable) {
            final Thread t = new Thread() {
                @Override
                public void run() {
                    try {
                        runnable.run();
                    } finally {

                    }
                }
            };
            t.start();
            return t;
        }




    public class MyLocationListener implements LocationListener
        {

            public void onLocationChanged(final Location loc)
            {
                Log.i("**************************************", "Location changed");
                if(isBetterLocation(loc, previousBestLocation)) {
                    loc.getLatitude();
                    loc.getLongitude();             
                    intent.putExtra("Latitude", loc.getLatitude());
                    intent.putExtra("Longitude", loc.getLongitude());     
                    intent.putExtra("Provider", loc.getProvider());                 
                    sendBroadcast(intent);          

                }                               
            }

            public void onProviderDisabled(String provider)
            {
                Toast.makeText( getApplicationContext(), "Gps Disabled", Toast.LENGTH_SHORT ).show();
            }


            public void onProviderEnabled(String provider)
            {
                Toast.makeText( getApplicationContext(), "Gps Enabled", Toast.LENGTH_SHORT).show();
            }


            public void onStatusChanged(String provider, int status, Bundle extras)
            {

            }

        }
    }

[解决方案]
我找到了解决问题的方法。
要访问 Main Activity 我使用了方法

MainActivity.getInstance()

主要Activity

public class MainActivity extends AppCompatActivity {
    private static MainActivity instance;

    public static MainActivity getInstance() {
        Log.i("MainActivity", "getInstance");
        return instance;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Instance of the MainActivity
        instance = this;
    }
    ...

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.i(TAG, "onActivityResult");
    switch (requestCode) {
        // Check for the integer request code originally supplied to startResolutionForResult().
        case REQUEST_CHECK_SETTINGS:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    Log.i(TAG, "User agreed to make required location settings changes.");
                    startService(intent);
                    break;
                case Activity.RESULT_CANCELED:
                    Log.i(TAG, "User chose not to make required location settings changes.");
                    break;
            }
            break;
        }
    }
}

服务

@Override
public void onResult(LocationSettingsResult locationSettingsResult) {
    final Status status = locationSettingsResult.getStatus();
    switch (status.getStatusCode()) {
        case LocationSettingsStatusCodes.SUCCESS:
            // All location settings are satisfied. The client can
            // initialize location requests here.
            Log.i(TAG, "All location settings are satisfied.");
            startLocationUpdates();
            break;
        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
            // Location settings are not satisfied, but this can be fixed
            // by showing the user a dialog.
            Log.i(TAG, "Location settings are not satisfied. Show the user a dialog to " +
                    "upgrade location settings ");
            try {
                // Show the dialog by calling startResolutionForResult(),
                // and check the result in onActivityResult().
                status.startResolutionForResult(MainActivity.getInstance(), REQUEST_CHECK_SETTINGS);
            } catch (IntentSender.SendIntentException e) {
                // Ignore the error.
                Log.i(TAG, "PendingIntent unable to execute request.");
            }
            break;
        // The status will never be SETTINGS_CHANGE_UNAVAILABLE if use builder.setAlwaysShow(true);
        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
            // Location settings are not satisfied. However, we have no way
            // to fix the settings so we won't show the dialog.
            Log.i(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog " +
                    "not created.");
            break;
    }
}