手机进入待机状态(屏幕关闭状态)时,活动识别无法继续接收更新。

9
我在活动识别方面遇到了一些问题。我已经将其实现在一个应用程序中,当设备的屏幕开启时它可以正常工作。我在我的活动识别意图服务类中有一个日志条目,我可以看到它何时更新。所以,我知道它在屏幕开启时可以正常工作。
但是,在手机待机后(屏幕关闭),它停止检测用户的活动。我检查了DetectionRequester类中的onDisconnected()方法是否被调用,使用了一个日志帖子进行了验证。
我的问题是:为什么我的应用程序在设备进入待机模式后停止跟踪用户的活动?如何使其不停止检测用户的活动?
如果您需要查看任何代码或需要更多关于我的应用程序的详细信息,请告诉我。
以下是MainActivity类中相关的代码片段。这是应用程序开始请求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;
    }
 }

我的DetectionRequester类:

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

意图服务:

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
我刚在Nexus 7上测试了一下,即使屏幕打开,它也会调用我的IntentService,因此我认为当屏幕关闭时活动识别不会停止(我以前在不同设备上进行的各种测试中也从未观察到这种情况)。请发布您的IntentService代码以及如何初始化它,包括传递给ActivityRecognitionClient的两个侦听器。 - Emanuel Moecklin
1
你的代码还缺少几个方法(requestDisconnection、setRequestPendingIntent等),但我认为问题的原因是PendingIntent是使用FLAG_CANCEL_CURRENT而不是FLAG_UPDATE_CURRENT创建的。再加上你可能在使用不同的实例变量(mActivityRecognitionClient、mActivityRecognitionPendingIntent等)而不是静态变量,意味着PendingIntent可能被创建了多次,因此位置服务无法再将PendingIntent与服务匹配。 - Emanuel Moecklin
1
请使用FLAG_UPDATE_CURRENT,如果这不起作用,请发布完整的代码,包括onCreate、onStart等Activity生命周期方法中缺失的所有与活动识别相关的方法以及所使用变量的定义(mActivityRecognitionClient...)。您没有收到onDisconnect()调用的事实意味着那里也有问题(当您通过调用requestDisconnection()从ActivityRecognitionClient断开连接时,它应该被调用)。 - Emanuel Moecklin
1
我按照原样测试了您的代码,即使屏幕关闭也可以正常工作。我在Moto G和Nexus 7上进行了测试。您使用什么测试设备?您不会碰巧使用启用了Stamina模式的Sony设备吧?此外,您的设备是否启用了位置服务?是否有其他应用程序会在设备进入睡眠状态时关闭位置服务? - Emanuel Moecklin
2
@MartinPfeffer:这只是在一些低端设备上存在的问题。API 在 90% 的设备上即使屏幕关闭也能很好地工作。我们已经在我们的应用中实现了这个功能,对结果非常满意。(应用程序链接:google.com/+drvrapp)另外,我强烈建议不要阅读这方面的评论和帖子,它们都是垃圾。 - DrkStr
显示剩余22条评论
2个回答

2
为了避免耗尽电池,Android设备长时间未操作会快速进入睡眠状态,这可能导致您的服务停止。您可以在此处阅读有关如何处理此问题的更多信息:保持设备唤醒。此外,还可以查看cwac-wakeful库中的WakefulIntentService。看起来它正是您所需要的。

1

请看“重复闹钟”:

它们在应用程序之外运行,因此即使您的应用程序未运行,甚至设备处于睡眠状态,您也可以使用它们触发事件或动作。

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

或者在“WakeLock”方面:

使用唤醒锁的一个合理情况可能是需要在屏幕关闭时保持CPU运行以完成工作的后台服务。然而,这种做法应该尽量减少,因为它对电池寿命的影响。

特别地,“WakefulBroadcastReceiver”:

使用广播接收器与服务结合使用可以管理后台任务的生命周期。

WakefulBroadcastReceiver是一种特殊类型的广播接收器,负责创建和管理应用程序的PARTIAL_WAKE_LOCK。 WakefulBroadcastReceiver将工作传递给服务(通常是IntentService),同时确保设备在过渡期间不会返回睡眠状态。如果在将工作传递到服务时不持有唤醒锁,则有效地允许设备在工作完成之前返回睡眠状态。结果是,应用程序可能无法在未来的某个任意时间完成工作,这不是您想要的。

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

如果您考虑使用第二种方法,请注意不要耗尽电池...


2
位置 API 应该自行唤醒设备以提供活动更新。 - DrkStr
@DrkStr:你找到解决方案了吗? - decades
不,一些低端手机在屏幕关闭时,活动识别意图服务会停止更新。我不确定如何区分这些手机,以便可以使用Android清单中的“用户特征”元素将它们过滤掉。 - DrkStr
@DrkStr:我也有同样令人沮丧的经历。在我的 Nexus 5 上,即使屏幕锁定,它也能正常工作。但在 Android 手表 4.2.2 上就会停止做任何事情,直到重新启动。另外,我尝试了两种方法,包括使用 Google Play 服务的活动识别,但效果都不理想... - decades
1
当它工作时,它表现得很好。但是当它不工作时,就没有办法知道为什么,也没有其他选择 :( - DrkStr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接