Android, get the location when the screen is off

2019-07-09 14:57发布

问题:

I use a started service with the fused api, and implement the location listener directly on it. The Location keeps updating even when the screen is locked, But it stops if the screen goes off.

So, is there any way to make sure that the location will keep updating when the screen is off?

I read a lot of other questions and I don't really know what i'm missing.

public class CLocationService extends Service implements GoogleApiClient.ConnectionCallbacks, LocationListener,
    GoogleApiClient.OnConnectionFailedListener {

private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mLocationRequest;
// Flag that indicates if a request is underway.
private boolean mInProgress;

private Boolean servicesAvailable = false;

private boolean isStarted;

public static final int LOCATION_SERVICE_NOTIFICATION_ID = 4567654;

private void showNotification() {

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

    Notification notification = new Notification.Builder(this)
            .setContentTitle(getText(R.string.app_name))
            .setContentText("")
            .setSmallIcon(R.mipmap.ic_notification)
            .setContentIntent(pendingIntent)
            .setTicker("")
            .build();

    startForeground(LOCATION_SERVICE_NOTIFICATION_ID, notification);
}

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

@Override
public void onCreate() {
    super.onCreate();

    /*
     * Create a new location client, using the enclosing class to
     * handle callbacks.
     */
    setUpLocationClientIfNeeded();

    startLocationServices();
}

/*
 * Create a new location client, using the enclosing class to
 * handle callbacks.
 */
protected synchronized void buildGoogleApiClient() {
    this.mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
}

private boolean servicesConnected() {

    // Check that Google Play services is available
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {

        return true;
    } else {

        return false;
    }
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);

    /*
    WakeLock is reference counted so we don't want to create multiple WakeLocks. So do a check before initializing and acquiring.
    This will fix the "java.lang.Exception: WakeLock finalized while still held: MyWakeLock" error that you may find.
    */
    if (this.mWakeLock == null) { //**Added this
        this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
    }

    if (!this.mWakeLock.isHeld()) { //**Added this
        this.mWakeLock.acquire();
    }

    if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
        return START_STICKY;

    setUpLocationClientIfNeeded();
    if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
        mInProgress = true;
        mGoogleApiClient.connect();
    }

    return START_STICKY;
}

private void setUpLocationClientIfNeeded() {
    if (mGoogleApiClient == null)
        buildGoogleApiClient();
}

@Override
public void onDestroy() {

    stopLocationServices();

    super.onDestroy();
}

private void startLocationServices() {

    mInProgress = false;
    // Create the LocationRequest object
    mLocationRequest = LocationRequest.create();
    // Use high accuracy
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    // Set the update interval to 5 seconds
    mLocationRequest.setInterval(5000);
    // Set the fastest update interval to 1 second
    mLocationRequest.setFastestInterval(1000);
    mLocationRequest.setSmallestDisplacement(0);

    servicesAvailable = servicesConnected();
}

private void stopLocationServices() {

    // Turn off the request flag
    this.mInProgress = false;

    if (this.servicesAvailable && this.mGoogleApiClient != null) {
        this.mGoogleApiClient.unregisterConnectionCallbacks(this);
        this.mGoogleApiClient.unregisterConnectionFailedListener(this);
        this.mGoogleApiClient.disconnect();
        // Destroy the current location client
        this.mGoogleApiClient = null;
    }
    // Display the connection status
    // Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ":
    // Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();

    if (this.mWakeLock != null) {
        this.mWakeLock.release();
        this.mWakeLock = null;
    }
}

private void cancelNotification() {
    NotificationManager nMgr = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
    nMgr.cancel(LOCATION_SERVICE_NOTIFICATION_ID);
}

@Override
public void onLocationChanged(Location location) {
    // log the new location
}

@Override
public void onConnected(@Nullable Bundle bundle) {

    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.
        return;
    }
    LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
            mLocationRequest, this); // This is the changed line.
}

@Override
public void onConnectionSuspended(int i) {

    // Turn off the request flag
    mInProgress = false;
    // Destroy the current location client
    mGoogleApiClient = null;
}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    mInProgress = false;

    /*
     * Google Play services can resolve some errors it detects.
     * If the error has a resolution, try sending an Intent to
     * start a Google Play services activity that can resolve
     * error.
     */
    if (connectionResult.hasResolution()) {

        // If no resolution is available, display an error dialog
    } else {

    }
}

}

回答1:

I'm not sure I have your answer but since you're 9 days in with nothing I'll give some suggestions.

My app is doing what you would like to do. I use a long running started Service to keep location updated even when the phone is off.

The difference most likely to cause different behavior between your code & mine is the return from onStartCommand(). You are returning START_STICKY. This is the recommended return for something like this:

This mode makes sense for things that will be explicitly started and stopped to run for arbitrary periods of time, such as a service performing background music playback.

However, I'm sending info in the Intent that I needed to have redelivered so I'm returning START_REDELIVER_INTENT. Try this (even if you have no need to redeliver any data) to see if it fixes your problem.

Also, I didn't need WakeLock in my implementation. Maybe your implementation needs this though. Have you tried without it?

Edit: Lastly, what kind of device are you using? link