如何知道用户使用我的应用程序时是否已经过了一定时间?

How to know if a certain amount of time has passed while user is using my app?

如果用户在我的应用上花费至少 20 秒,我想向 firebase 发送一个事件。这看起来很简单,因为我只需要创建一个计时器。这是我到目前为止所做的:

Subscription sessionEvent = Observable.timer(20, TimeUnit.SECONDS)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(aLong -> Log.v("tag", "hello"));

我在 activity 的 onResume 中订阅,而在 onPause 中取消订阅。 然而,这是一个问题,因为如果我的 activity 启动另一个 activity,onPause 也会被调用。这意味着即使用户仍在我的应用程序上,计时器也会停止。

我要查找的是用户关闭应用程序的时间,即计时器停止的时间。我已经尝试使用 onUserLeaveHint,不幸的是,启动另一个 activity 也会调用该方法。

如果您有多个活动,我认为没有直接的方法可以检测您的应用程序是在前台还是后台。但是,您可以使用 OnResume 和 OnPause 来跟踪每个 activity 的状态。使用布尔共享首选项跟踪每个 activity 的状态,并定期 运行 使用重复警报的后台服务来简单地检查屏幕上是否至少有 activity。 StartTime-StartTime = duration.

您可以使用 ActivityLifecycleCallbacks to monitor the state of all activities in your app. You can register one with Application.registerActivityLifecycleCallbacks(),它会在每个 Activity 经历其生命周期方法时被调用。当您启动的活动总数为 0 时,您可以确定用户没有在查看您的应用,但他们也完全有可能随时从任务切换器切换回来。

下面是一个示例Application implementation that uses the ActivityLifeCycleCallbacks mentioned in Doug Stevenson's answer. The onTrimMemory()方法用于检测应用程序何时后台运行:

public class MyApplication extends Application
        implements Application.ActivityLifecycleCallbacks {

    private static final String TAG = "MyApplication";

    private Handler handler;
    private final Runnable generateEvent = new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "User engagement is 20 secs");
            // Generate analytics event
        }
    };

    private boolean mAppInForeground;

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

        registerActivityLifecycleCallbacks(this);
        handler = new Handler(getMainLooper());
        mAppInForeground = false;
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            Log.i(TAG, "onTrimMemory: App in background");
            handler.removeCallbacks(generateEvent);
            mAppInForeground = false;
        }
    }

    @Override
    public void onActivityStarted(Activity activity) {
        // Called when any activity is started
        Log.d(TAG, "onActivityStarted: Was foreground=" + mAppInForeground);
        if (!mAppInForeground) {
            mAppInForeground = true;
            handler.postDelayed(generateEvent, 20 * 1000);
        }
    }

    // Remaining ActivityLifecycleCallbacks are not used
    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
    @Override
    public void onActivityResumed(Activity activity) {}
    @Override
    public void onActivityPaused(Activity activity) {}
    @Override
    public void onActivityStopped(Activity activity) {}
    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
    @Override
    public void onActivityDestroyed(Activity activity) {}
}

我最后使用 <uses-permission android:name="android.permission.GET_TASKS"/> 检查应用程序是否在前台。这是我使用的方法:

public boolean isAppForeground(Context context) {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

    if (am == null) return false;

    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
        List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();

        if (processInfos != null && !processInfos.isEmpty()) {
            ActivityManager.RunningAppProcessInfo info = processInfos.get(0);
            if (info == null || info.processName == null || !info.processName.contentEquals(context.getPackageName()))
                return false;
        } else {
            return false;
        }
    }
    else{
        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);

        if (tasks != null && !tasks.isEmpty()) {
            ComponentName topActivity = tasks.get(0).topActivity;
            if (topActivity == null || topActivity.getPackageName() == null || !topActivity.getPackageName().equals(context.getPackageName())) {
                return false;
            }
        } else {
            return false;
        }
    }

    return true;
}

该方法位于我的自定义 Application class 中。我使用 registerActivityLifecycleCallbacks 检查 onActivityPaused 如果应用程序在前台,我不会停止计时器,但如果不是,我会停止计时器。