如何在Android中检测用户长时间不活动且存在多个活动

8
我知道有人已经提出了相同的问题并得到了答案,这个问题在这里。但是我对那个答案有疑问。
我的要求是,如果用户长时间不活动,比如5分钟,那么用户将自动注销应用程序,并上传登录和登出日志到服务器。
我尝试了上面链接中的所有答案,但是那些答案只适用于单个Activity,而我想要跟踪多个Activity。
为此,我创建了一个抽象类。
public abstract class SessionTimeOutActivity extends BaseActivity {

    public static final long DISCONNECT_TIMEOUT = 1000 * 60; // 5 min = 5 * 60 * 1000 ms

    private static Handler disconnectHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Log.d("SessionTimeOutActivity", "disconnectHandler");

            return false;
        }
    });

    private Runnable disconnectCallback = new Runnable() {
        @Override
        public void run() {
            // Perform any required operation on disconnect
            Log.d("SessionTimeOutActivity", "disconnectCallback");


            Toast.makeText(getApplicationContext(), "Session time out", Toast.LENGTH_LONG).show();
            Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
            startActivity(intent);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

            finish();

        }
    };

    public void resetDisconnectTimer() {
        disconnectHandler.removeCallbacks(disconnectCallback);
        disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
    }

    public void stopDisconnectTimer() {
        disconnectHandler.removeCallbacks(disconnectCallback);
    }

    @Override
    public void onUserInteraction() {
        Log.d("SessionTimeOutActivity", "onUserInteraction");
        resetDisconnectTimer();
    }

    @Override
    public void onResume() {
        super.onResume();
//        resetDisconnectTimer();
    }

    @Override
    public void onStop() {
        super.onStop();
//        stopDisconnectTimer();
    }


}

应用中的其他活动

    public class MenuActivtyNav extends SessionTimeOutActivity{
.....
}

菜单活动

 public class MenuActivty extends SessionTimeOutActivity{
....
}

问题是

1) 在锁屏状态下,自动注销功能不起作用,它没有调用断开回调函数。

2) 在使用应用程序时,会显示提示信息 "会话超时"。

2个回答

11

让我们从理解代码不能按照预期执行的原因开始。

问题1。在Android应用中,你应该避免在活动(activity)中运行长时间运行的任务,特别是当它们不可见时,因为系统可能会杀死你的活动或应用程序进程,所以代码将无法运行。

问题2。每个活动都会向处理程序(handler)发布“断开连接(disconnect)”回调。在用户与第二个活动交互时,第一个活动的回调不会被重置并且将触发。

解决方案。

现在考虑一种解决方法的方法。最好有一个地方来跟踪非活动状态。它可以是单例模式,也可以使用应用程序对象。并将两种情况分别处理:第一种情况是如果你的用户在应用程序内部而没有使用它,另一种情况是当你的用户退出应用程序。

对于第一个目标,你可以采用类似当前方法的方法,但在应用程序中创建共享的disconnectCallback。将处理程序、回调和重置回调逻辑移至Application类中,并使

@Override
public void onUserInteraction()

在应用程序上调用resetCallback。这将使所有活动使用相同的回调并解决问题2。

对于第二个目标,您可以利用 Activity生命周期回调。每次暂停活动时保存时间戳。然后,每次恢复活动时,请将此时间戳与恢复时间进行比较,如果大于5分钟,则注销用户并路由到登录屏幕。这将解决问题1。您需要持久化此时间戳,因为系统可能会杀死应用程序,并清除内存中共享的所有内容。例如,可以使用共享偏好设置。

最后一件事,当应用程序进入后台时,您需要取消回调。您可以在活动回调的onActivityPaused(Activity activity)中执行此操作,在您将触发第二个情况的同一位置。


谢谢回复,我该如何处理第二种情况?因为每次我启动一个新的活动后,一段时间后我的上一个活动就会进入暂停模式并调用onActivityPaused方法。 - MJM
你的新活动将在此之后调用onActivityResumed方法,在该方法中,您将检查自上次onActivityPaused以来经过的时间是否大于5分钟。如果小于5分钟,则不执行任何操作并清除暂停时间戳。 - TpoM6oH

2

即使您可以通过此答案做出一些努力来管理您的需求。

但是,我在考虑一些不需要定时器和处理程序的解决方案。我已经有了一个良好管理的计时器解决方案。但是,我已经成功实现了无定时器和处理程序的解决方案。

首先,我告诉您如果使用定时器或处理程序,您需要管理什么

  • 如果用户或优化器终止应用程序,则所有回调都将被销毁,因此您的应用程序永远不会自动注销。(需要管理一些Alarm Manager或Service?
  • 在每个基类中都使用计时器是否好?您正在为仅调用注销过程而创建许多线程。(在应用程序级别上管理静态处理程序或计时器?
  • 如果用户处于后台状态,则您的处理程序将启动登录活动,如果用户在应用程序外执行其他工作。(管理应用程序前台或后台?
  • 如果屏幕自动关闭怎么办。(管理广播接收器上的屏幕关闭?

最后,我实现了一个解决方案

  1. 您不必使用处理程序或计时器。
  2. 您不必使用Alarm Manager。
  3. 您不必使用应用程序生命周期。
  4. 您不必使用ACTION_SCREEN_ON/ACTION_SCREEN_OFF广播接收器。

解决方案

我们将不会通过定时器观察用户的非活动状态,而是在用户活动时检查最后一次活动时间。因此,当用户下次与应用程序交互时,我会检查最后一次交互时间。

这里是您将从每个活动类中扩展的BaseActivity.class

import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class BaseActivity extends AppCompatActivity {
    public static final long TIMEOUT_IN_MILLI = 1000 * 20;
    public static final String PREF_FILE = "App_Pref";
    public static final String KEY_SP_LAST_INTERACTION_TIME = "KEY_SP_LAST_INTERACTION_TIME";

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        if (isValidLogin())
            getSharedPreference().edit().putLong(KEY_SP_LAST_INTERACTION_TIME, System.currentTimeMillis()).apply();
        else logout();
    }

    public SharedPreferences getSharedPreference() {
        return getSharedPreferences(PREF_FILE, MODE_PRIVATE);
    }

    public boolean isValidLogin() {
        long last_edit_time = getSharedPreference().getLong(KEY_SP_LAST_INTERACTION_TIME, 0);
        return last_edit_time == 0 || System.currentTimeMillis() - last_edit_time < TIMEOUT_IN_MILLI;
    }

    public void logout() {
        Intent intent = new Intent(this, LoginActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
        finish();
        Toast.makeText(this, "User logout due to inactivity", Toast.LENGTH_SHORT).show();
        getSharedPreference().edit().remove(KEY_SP_LAST_INTERACTION_TIME).apply(); // make shared preference null.
    }
}

如果我滑出应用程序或从最近任务中删除,是否会调用 onUserInteraction() - MJM
我没有检查它。你可以通过调试或放置日志来检查它。顺便说一下,我没有将onResume()作为userInteraction提及,因为仅包括onUserInteraction()将为您提供正确的结果。 - Khemraj Sharma
仅使用onUserInteraction而不管理其他任何内容,将减少复杂性并在用户真正与应用程序交互时为您提供准确的结果。 - Khemraj Sharma
奇怪为什么这个答案的投票很少!顺便感谢@KhemrajSharma的回答。 - Mohd Qasim

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