如何检测用户在Android中有多个活动一段时间不活动

时间:2018-05-29 14:16:42

标签: android session handle lockscreen

我知道同样的问题是here,已经有人提出问题并且有答案,但我对此答案有疑问。

我的要求是,我想确定用户是否处于非活动状态5分钟,然后用户从应用程序中自动注销并上传登录并注销服务器上的日志。

我已尝试过上述链接中的所有答案,但该答案适用于单项活动,但我想跟踪多项活动。

为此我创建了抽象类

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


}

app中的其他活动

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

MenuActivity

 public class MenuActivty extends SessionTimeOutActivity{
....
}

问题

1)在锁定屏幕中自动注销不起作用,它不调用disconnectCallback

2)使用app时,它会显示toast消息“Session time out”

2 个答案:

答案 0 :(得分:6)

让我们首先了解您的代码没有按照您的意愿行事的原因。

问题1。您应该避免在Android上的活动中长时间运行任务,尤其是当它们不可见时,因为系统可能会终止您的活动或应用程序进程,因此代码将无法运行

问题2。每个活动都会将disconnect回调发布到您的处理程序,如果用户与第二个活动进行交互,则第一个活动的回调将不会重置并触发。

<强>解。

现在让我们考虑解决这个问题的方法。 最好有一个地方来跟踪不活动。它可以是单例,也可以使用应用程序对象。 并分别处理2个案例:第一个是你的用户在应用程序内并且没有使用它而另一个是你的用户不在应用程序时。

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

@Override
public void onUserInteraction()

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

对于第二个目标,您可以使用Activity Lifecycle Callbacks。 每次暂停活动时都会保存时间戳。然后,每次恢复活动时,将此时间戳与恢复时间进行比较,如果大于5分钟,则注销用户并路由到登录屏幕。它将解决问题1.您需要保留此时间戳,因为应用程序可能被系统杀死,并且内存中共享的所有内容都将被删除。例如,使用共享首选项。

最后一件事,当应用程序进入后台时,您需要取消回调。你可以在你的活动回调的onActivityPaused(Activity activity)中,在你将触发第二种情况的逻辑的同一地方。

答案 1 :(得分:1)

即使您可以在使用this answer进行一些努力后管理您的要求。

但我正在考虑一些Timer和Handlers免费解决方案。我已经有了很好的管理计时器解决方案。但我已经成功实现了Timer和Handler免费解决方案。

首先,如果您使用计时器或处理程序,我会告诉您您需要管理的内容

  • 如果您的应用被用户或优化程序杀死,您的应用将永远无法自动注销,因为您的所有回调都会被销毁。 (管理一些警报管理器或服务?
  • 每个基类都有计时器吗?您正在为调用注销过程创建许多线程(在应用级别管理静态处理程序或计时器?)。
  • 如果用户处于后台,如果用户在您的应用之外进行其他工作,您的处理程序将启动登录活动。 (管理应用前景或背景?)。
  • 如果屏幕自动关闭怎么办? (在广播接收器上关闭屏幕?

最后我实施了一个

的解决方案
  1. 您不必使用Hander或计时器。
  2. 您不必使用闹钟管理器。
  3. 您不必使用App Lifecycle。
  4. 您不必使用ACTION_SCREEN_ON / ACTION_SCREEN_OFF广播接收器。
  5. <强>解决方案

    我们不会通过计时器观察用户不活动,而是检查用户活动的最后活动时间。因此,当用户下次交互应用时,我会检查上次交互时间。

    以下是BaseActivity.class,您将从每个活动类而不是LoginActivity进行扩展。

    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.
        }
    }