Activity Recognition stops receiving updates when

2019-03-11 15:29发布

问题:

I am having some trouble with activity recognition. I have implemented it in an app and it works fine when the device's screen is on. I have a log entry in my Activity recognition intent service class and I can see when it gets an update.So, I know it is working fine when the screen is on.

But then after the phone is put to standby(the screen is turned off) it stops detecting the uses activity.

The onDisconnected() in the DetectionRequester class is not getting called, I checked using a log post.

My question: Why does my app stop tracking the uses activity after the device goes to standby mode? And how do I make it not stop detecting the users activity?

Let me know if you need to see any of the code or if you need any more details on my app.

Relevant bits of code from my MainActivity class. This is where the app starts the request for ActivityRecognition.

public class MainActivity extends FragmentActivity  {

// The activity recognition update request object
private DetectionRequester mDetectionRequester;

// The activity recognition update removal object
private DetectionRemover mDetectionRemover;


// Store the current request type (ADD or REMOVE)
private REQUEST_TYPE mRequestType;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);     
    //Crashlytics.start(this);
    setContentView(R.layout.activity_main);

    mDetectionRequester = new DetectionRequester(this);
    mDetectionRemover = new DetectionRemover(this);

    // Check for Google Play services
     if (servicesConnected()) 
     {
        /*
         *Set the request type. If a connection error occurs, and Google Play services can
         * handle it, then onActivityResult will use the request type to retry the request
         */

          mRequestType = ActivityUtils.REQUEST_TYPE.ADD;

         // Pass the update request to the requester object
          mDetectionRequester.requestUpdates();

      }
}

 private boolean servicesConnected() {

    Log.wtf("Rakshak", "Service connected method");

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

    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) 
    {
        Log.wtf("Rakshak", "Service connected method: connection result success");
        // Continue
        return true;

    // Google Play services was not available for some reason
    } else {

        Log.wtf("Rakshak", "Service connected method: connection result failure");

        // Display an error dialog
        GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();
        return false;
    }
}        


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {

    // Choose what to do based on the request code
    switch (requestCode) {

        // If the request code matches the code sent in onConnectionFailed
        case ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST :

            switch (resultCode) {
                // If Google Play services resolved the problem
                case Activity.RESULT_OK:

                    // If the request was to start activity recognition updates
                    if (ActivityUtils.REQUEST_TYPE.ADD == mRequestType) {

                        // Restart the process of requesting activity recognition updates

                        mDetectionRequester.requestUpdates(); 

                    // If the request was to remove activity recognition updates
                    } else if (ActivityUtils.REQUEST_TYPE.REMOVE == mRequestType ){

                            /*
                             * Restart the removal of all activity recognition updates for the 
                             * PendingIntent.
                            */
                           // mDetectionRemover.removeUpdates(
                              //  mDetectionRequester.getRequestPendingIntent()); 

                    }
                break;

                // If any other result was returned by Google Play services
                default:

                    // Report that Google Play services was unable to resolve the problem.
                    Log.d(ActivityUtils.APPTAG, "unable to resolve Google play services problems");
            }

        // If any other request code was received
        default:
           // Report that this Activity received an unknown requestCode
           Log.d(ActivityUtils.APPTAG,
                   "received an unknown request code");

           break;
    }
 }

My DetectionRequester class:

public class DetectionRequester
    implements ConnectionCallbacks, OnConnectionFailedListener {

// Storage for a context from the calling client
private Context mContext;

// Stores the PendingIntent used to send activity recognition events back to the app
private PendingIntent mActivityRecognitionPendingIntent;

// Stores the current instantiation of the activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;

public DetectionRequester(Context context) {
    // Save the context
    mContext = context;

    // Initialize the globals to null
    mActivityRecognitionPendingIntent = null;
    mActivityRecognitionClient = null;

}
/**
 * Returns the current PendingIntent to the caller.
 *
 * @return The PendingIntent used to request activity recognition updates
 */
public PendingIntent getRequestPendingIntent() {
    return mActivityRecognitionPendingIntent;
}

/**
 * Sets the PendingIntent used to make activity recognition update requests
 * @param intent The PendingIntent
 */
public void setRequestPendingIntent(PendingIntent intent) {
    mActivityRecognitionPendingIntent = intent;
}

/**
 * Start the activity recognition update request process by
 * getting a connection.
 */
public void requestUpdates() {
    requestConnection();
}

/**
 * Make the actual update request. This is called from onConnected().
 */
private void continueRequestActivityUpdates() {
    /*
     * Request updates, using the default detection interval.
     * The PendingIntent sends updates to ActivityRecognitionIntentService
     */
    getActivityRecognitionClient().requestActivityUpdates(
            ActivityUtils.DETECTION_INTERVAL_MILLISECONDS,
            createRequestPendingIntent());

    // Disconnect the client
    requestDisconnection();
}

/**
 * Request a connection to Location Services. This call returns immediately,
 * but the request is not complete until onConnected() or onConnectionFailure() is called.
 */
private void requestConnection() {
    getActivityRecognitionClient().connect();
}

/**
 * Get the current activity recognition client, or create a new one if necessary.
 * This method facilitates multiple requests for a client, even if a previous
 * request wasn't finished. Since only one client object exists while a connection
 * is underway, no memory leaks occur.
 *
 * @return An ActivityRecognitionClient object
 */
private ActivityRecognitionClient getActivityRecognitionClient() {
    if (mActivityRecognitionClient == null) {

        mActivityRecognitionClient =
                new ActivityRecognitionClient(mContext, this, this);
    }
    return mActivityRecognitionClient;
}

/**
 * Get the current activity recognition client and disconnect from Location Services
 */
private void requestDisconnection() {
    getActivityRecognitionClient().disconnect();
}

/*
 * Called by Location Services once the activity recognition client is connected.
 *
 * Continue by requesting activity updates.
 */
@Override
public void onConnected(Bundle arg0) {
    // If debugging, log the connection
    Log.w("Rakshak", "Locatin client connected");

    // Continue the process of requesting activity recognition updates
    continueRequestActivityUpdates();
}

/*
 * Called by Location Services once the activity recognition client is disconnected.
 */
@Override
public void onDisconnected() {
    // In debug mode, log the disconnection
     Log.w("Rakshak", "Locatin client dis-connected");

    // Destroy the current activity recognition client
    mActivityRecognitionClient = null;

}

/**
 * Get a PendingIntent to send with the request to get activity recognition updates. Location
 * Services issues the Intent inside this PendingIntent whenever a activity recognition update
 * occurs.
 *
 * @return A PendingIntent for the IntentService that handles activity recognition updates.
 */
private PendingIntent createRequestPendingIntent() {

    // If the PendingIntent already exists
    if (null != getRequestPendingIntent()) {

        // Return the existing intent
        return mActivityRecognitionPendingIntent;

    // If no PendingIntent exists
    } else {
        // Create an Intent pointing to the IntentService
        Intent intent = new Intent(mContext, ActivityRecognitionIntentService.class);

        /*
         * Return a PendingIntent to start the IntentService.
         * Always create a PendingIntent sent to Location Services
         * with FLAG_UPDATE_CURRENT, so that sending the PendingIntent
         * again updates the original. Otherwise, Location Services
         * can't match the PendingIntent to requests made with it.
         */
        PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        setRequestPendingIntent(pendingIntent);
        return pendingIntent;
    }

}

/*
 * Implementation of OnConnectionFailedListener.onConnectionFailed
 * If a connection or disconnection request fails, report the error
 * connectionResult is passed in from Location Services
 */
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    /*
     * 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()) {

        try {
            connectionResult.startResolutionForResult((Activity) mContext,
                ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

        /*
         * Thrown if Google Play services canceled the original
         * PendingIntent
         */
        } catch (SendIntentException e) {
           // display an error or log it here.
        }

    /*
     * If no resolution is available, display Google
     * Play service error dialog. This may direct the
     * user to Google Play Store if Google Play services
     * is out of date.
     */
    } else {
        Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
                        connectionResult.getErrorCode(),
                        (Activity) mContext,
                        ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
        if (dialog != null) {
            dialog.show();
        }
    }
}

}

The intent service:

public class ActivityRecognitionIntentService extends IntentService {   

public ActivityRecognitionIntentService() {
    // Set the label for the service's background thread
    super("ActivityRecognitionIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {

    Log.w("Rakshak", "the on handel intent called"); // I see this only when the devices screen is on 

    // do some fun stuff

  }

回答1:

To avoid draining the battery, an Android device that is left idle quickly falls asleep. It's the reason of your service stopping. You can read more about how it can be handled here: Keeping the Device Awake. Also take a look on WakefulIntentService from cwac-wakeful library. Looks like it's what you are looking for.



回答2:

Take a look at "Repeating Alarms":

They operate outside of your application, so you can use them to trigger events or actions even when your app is not running, and even if the device itself is asleep.

https://developer.android.com/training/scheduling/alarms.html

Or at "WakeLock":

One legitimate case for using a wake lock might be a background service that needs to grab a wake lock to keep the CPU running to do work while the screen is off. Again, though, this practice should be minimized because of its impact on battery life.

Specially "WakefulBroadcastReceiver":

Using a broadcast receiver in conjunction with a service lets you manage the life cycle of a background task.

A WakefulBroadcastReceiver is a special type of broadcast receiver that takes care of creating and managing a PARTIAL_WAKE_LOCK for your app. A WakefulBroadcastReceiver passes off the work to a Service (typically an IntentService), while ensuring that the device does not go back to sleep in the transition. If you don't hold a wake lock while transitioning the work to a service, you are effectively allowing the device to go back to sleep before the work completes. The net result is that the app might not finish doing the work until some arbitrary point in the future, which is not what you want.

https://developer.android.com/training/scheduling/wakelock.html#cpu

If you consider to use the second approach be careful about draining the battery...