首页 文章

如何检测Android中的用户不活动状态

提问于
浏览
87

用户启动我的应用程序并登录 .
选择会话超时为5分钟 .
在应用程序上执行某些操作 . (全部在前景)
现在用户将Myapp带到后台并启动其他应用程序 .
---->倒计时器启动并在5分钟后退出用户
或者用户关闭屏幕 .
---->倒计时器启动并在5分钟后退出用户

我想要相同的行为,即使应用程序在前台,但用户不会长时间说6-7分钟与应用程序交互 . 假设屏幕始终处于开启状态 . 我想检测一种 user inactivity (即使应用程序在前台也没有与应用程序进行交互)并启动我的倒数计时器 .

13 回答

  • 4

    根据Fredrik Wallenius的回答,我提出了一个很简单的解决方案 . 这是一个需要由所有活动扩展的基本活动类 .

    public class MyBaseActivity extends Activity {
    
        public static final long DISCONNECT_TIMEOUT = 300000; // 5 min = 5 * 60 * 1000 ms
    
    
        private Handler disconnectHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                // todo
                return true;
            }
        });
    
        private Runnable disconnectCallback = new Runnable() {
            @Override
            public void run() {
                // Perform any required operation on disconnect
            }
        };
    
        public void resetDisconnectTimer(){
            disconnectHandler.removeCallbacks(disconnectCallback);
            disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
        }
    
        public void stopDisconnectTimer(){
            disconnectHandler.removeCallbacks(disconnectCallback);
        }
    
        @Override
        public void onUserInteraction(){
            resetDisconnectTimer();
        }
    
        @Override
        public void onResume() {
            super.onResume();
            resetDisconnectTimer();
        }
    
        @Override
        public void onStop() {
            super.onStop();
            stopDisconnectTimer();
        }
    }
    
  • 0

    我不知道跟踪不活动的方法,但有一种方法可以跟踪用户活动 . 您可以在每次用户与应用程序进行任何交互时调用的活动中捕获名为 onUserInteraction() 的回调 . 我建议做这样的事情:

    @Override
    public void onUserInteraction(){
        MyTimerClass.getInstance().resetTimer();
    }
    

    如果您的应用程序包含多个活动,为什么不将此方法放在抽象超类中(扩展 Activity ),然后让所有活动扩展它 .

  • 5
    public class MyApplication extends Application {
          private int lastInteractionTime;
          private Boolean isScreenOff = false; 
          public void onCreate() {
            super.onCreate();
            // ......   
            startUserInactivityDetectThread(); // start the thread to detect inactivity
            new ScreenReceiver();  // creating receive SCREEN_OFF and SCREEN_ON broadcast msgs from the device.
          }
    
          public void startUserInactivityDetectThread() {
            new Thread(new Runnable() {
              @Override
              public void run() {
                while(true) {
                  Thread.sleep(15000); // checks every 15sec for inactivity
                  if(isScreenOff || getLastInteractionTime()> 120000 ||  !isInForeGrnd)
                    {
                      //...... means USER has been INACTIVE over a period of
                      // and you do your stuff like log the user out 
                    }
                  }
              }
            }).start();
          }
    
          public long getLastInteractionTime() {
            return lastInteractionTime;
          }
    
          public void setLastInteractionTime(int lastInteractionTime) {
            this.lastInteractionTime = lastInteractionTime;
          }
    
          private class ScreenReceiver extends BroadcastReceiver {
    
            protected ScreenReceiver() {
               // register receiver that handles screen on and screen off logic
               IntentFilter filter = new IntentFilter();
               filter.addAction(Intent.ACTION_SCREEN_ON);
               filter.addAction(Intent.ACTION_SCREEN_OFF);
               registerReceiver(this, filter);
            }
    
            @Override
            public void onReceive(Context context, Intent intent) {
              if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                isScreenOff = true;
              } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
                isScreenOff = false;
              }
            }
          }
        }
    

    isInForeGrnd ===> logic is not shown here as it is out of scope of the question

    你可以使用下面的设备代码将锁定唤醒到cpu-

    if(isScreenOff || getLastInteractionTime()> 120000 ||  !isInForeGrnd)
        {
          //...... means USER has been INACTIVE over a period of
          // and you do your stuff like log the user out 
    
          PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    
          boolean isScreenOn = pm.isScreenOn();
          Log.e("screen on.................................", "" + isScreenOn);
    
          if (isScreenOn == false) {
    
            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "MyLock");
    
            wl.acquire(10000);
            PowerManager.WakeLock wl_cpu = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyCpuLock");
    
            wl_cpu.acquire(10000);
          }
        }
    
  • 2

    我认为你应该使用这段代码,这是5分钟的空闲会话超时: - >

    Handler handler;
    Runnable r;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler = new Handler();
        r = new Runnable() {
    
           @Override
           public void run() {
                // TODO Auto-generated method stub
                Toast.makeText(MainActivity.this, "user is inactive from last 5 minutes",Toast.LENGTH_SHORT).show();
            }
        };
        startHandler();
    }
    @Override
    public void onUserInteraction() {
         // TODO Auto-generated method stub
         super.onUserInteraction();
         stopHandler();//stop first and then start
         startHandler();
    }
    public void stopHandler() {
        handler.removeCallbacks(r);
    }
    public void startHandler() {
        handler.postDelayed(r, 5*60*1000); //for 5 minutes 
    }
    
  • 3
    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        delayedIdle(IDLE_DELAY_MINUTES);
    }
    
    Handler _idleHandler = new Handler();
    Runnable _idleRunnable = new Runnable() {
        @Override
        public void run() {
            //handle your IDLE state
        }
    };
    
    private void delayedIdle(int delayMinutes) {
        _idleHandler.removeCallbacks(_idleRunnable);
        _idleHandler.postDelayed(_idleRunnable, (delayMinutes * 1000 * 60));
    }
    
  • 77

    除了 ACTION_SCREEN_OFFACTION_USER_PRESENT 广播之外,在操作系统级别没有"user inactivity"的概念 . 您必须在自己的应用程序中以某种方式定义"inactivity" .

  • 94

    在我的活动基类中,我创建了受保护的类:

    protected class IdleTimer
    {
        private Boolean isTimerRunning;
        private IIdleCallback idleCallback;
        private int maxIdleTime;
        private Timer timer;
    
        public IdleTimer(int maxInactivityTime, IIdleCallback callback)
        {
            maxIdleTime = maxInactivityTime;
            idleCallback = callback;
        }
    
        /*
         * creates new timer with idleTimer params and schedules a task
         */
        public void startIdleTimer()
        {
            timer = new Timer();            
            timer.schedule(new TimerTask() {
    
                @Override
                public void run() {             
                    idleCallback.inactivityDetected();
                }
            }, maxIdleTime);
            isTimerRunning = true;
        }
    
        /*
         * schedules new idle timer, call this to reset timer
         */
        public void restartIdleTimer()
        {
            stopIdleTimer();
            startIdleTimer();
        }
    
        /*
         * stops idle timer, canceling all scheduled tasks in it
         */
        public void stopIdleTimer()
        {
            timer.cancel();
            isTimerRunning = false;
        }
    
        /*
         * check current state of timer
         * @return boolean isTimerRunning
         */
        public boolean checkIsTimerRunning()
        {
            return isTimerRunning;
        }
    }
    
    protected interface IIdleCallback
    {
        public void inactivityDetected();
    }
    

    所以在 onResume 方法中 - 你可以在你的回调中指定动作你想用它做什么...

    idleTimer = new IdleTimer(60000, new IIdleCallback() {
                @Override
                public void inactivityDetected() {
                    ...your move...
                }
            });
            idleTimer.startIdleTimer();
    
  • 0

    在我的搜索过程中,我找到了很多答案,但这是我得到的最佳答案 . 但是这个代码的限制是它只适用于不是整个应用程序的活动 . 以此为参考 .

    myHandler = new Handler();
    myRunnable = new Runnable() {
        @Override
        public void run() {
            //task to do if user is inactive
    
        }
    };
    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        myHandler.removeCallbacks(myRunnable);
        myHandler.postDelayed(myRunnable, /*time in milliseconds for user inactivity*/);
    }
    

    例如,您使用8000,任务将在用户不活动8秒后完成 .

  • 0

    迟到总比没有好

    即使您可以使用@gfrigon@AKh解决方案来管理您的需求 .

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

    首先我告诉你 what you have to manage 如果你使用定时器或处理程序 .

    • 如果您的应用被用户或优化器杀死,您的应用将永远不会自动注销,因为所有回调都会被销毁 . ( Manage some Alarm Manager or Service?

    • 在每个基类中都有计时器吗?您正在为调用注销过程( Manage static Handler or Timer at app level? )创建许多线程 .

    • 如果用户在后台,如果用户在您的应用程序之外进行其他工作,您的处理程序将启动登录活动 . ( Manage app foreground or background? ) .

    • 如果屏幕自动关闭怎么办? ( Manage screen off on broadcast receiver?

    最后我实施了一个解决方案

    • 您不必使用Hander或Timer .

    • 您不必使用Alarm Manager .

    • 您不必使用App Lifecycle .

    • 您不必使用 ACTION_SCREEN_ON / ACTION_SCREEN_OFF 广播接收器 .

    Solution

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

    这是 BaseActivity.class ,您将从每个活动类而不是 LoginActivity 扩展 . 您将在此类的字段 TIMEOUT_IN_MILLI 中定义注销时间 .

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

    我认为需要将计时器与最后一次激活时间相结合 .

    像这样:

    • 在onCreate(Bundle savedInstanceState)中启动一个计时器,比如5分钟

    • 在onUserInteraction()中只存储当前时间

    到目前为止很简单 .

    现在当计时器弹出时这样做:

    • 获取当前时间并减去存储的交互时间以获取timeDelta

    • 如果timeDelta> = 5分钟,你就完成了

    • 如果timeDelta <5分钟再次启动计时器,但这次使用5分钟 - 存储时间 . 换句话说,5分钟形成最后一次互动

  • 1

    我有类似的问题,我需要跟踪用户不活动1分钟,然后重定向用户启动活动,我还需要清除活动堆栈 .

    根据@gfrigon的回答,我想出了这个解决方案 .

    ActionBar.java

    public abstract class ActionBar extends AppCompatActivity {
    
        public static final long DISCONNECT_TIMEOUT = 60000; // 1 min
    
        private final MyHandler mDisconnectHandler = new MyHandler(this);
    
        private Context mContext;
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            mContext = this;
        }
    
    
    
        /*
        |--------------------------------------------------------------------------
        | Detect user inactivity in Android
        |--------------------------------------------------------------------------
        */
    
        // Static inner class doesn't hold an implicit reference to the outer class
    
        private static class MyHandler extends Handler {
    
            // Using a weak reference means you won't prevent garbage collection
    
            private final WeakReference<ActionBar> myClassWeakReference;
    
            public MyHandler(ActionBar actionBarInstance) {
    
                myClassWeakReference = new WeakReference<ActionBar>(actionBarInstance);
            }
    
            @Override
            public void handleMessage(Message msg) {
    
                ActionBar actionBar = myClassWeakReference.get();
    
                if (actionBar != null) {
                    // ...do work here...
                }
            }
        }
    
    
        private Runnable disconnectCallback = new Runnable() {
    
            @Override
            public void run() {
    
                // Perform any required operation on disconnect
    
                Intent startActivity = new Intent(mContext, StartActivity.class);
    
                // Clear activity stack
    
                startActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    
                startActivity(startActivity);
            }
        };
    
        public void resetDisconnectTimer() {
    
            mDisconnectHandler.removeCallbacks(disconnectCallback);
            mDisconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
        }
    
        public void stopDisconnectTimer() {
    
            mDisconnectHandler.removeCallbacks(disconnectCallback);
        }
    
        @Override
        public void onUserInteraction(){
    
            resetDisconnectTimer();
        }
    
        @Override
        public void onResume() {
    
            super.onResume();
            resetDisconnectTimer();
        }
    
        @Override
        public void onStop() {
    
            super.onStop();
            stopDisconnectTimer();
        }
    }
    

    补充资源

    Android: Clear Activity Stack

    This Handler class should be static or leaks might occur

  • 8

    用户不活动可以使用android中的 onUserInteraction() 覆盖方法进行检测

    @Override
        public void onUserInteraction() {
            super.onUserInteraction();
    
        }
    

    以下是用户不活动时的示例代码 signout (HomeActivity-->LoginActivity) after 3min

    public class HomeActivity extends AppCompatActivity {
    
        private static String TAG = "HomeActivity";
        private Handler handler;
        private Runnable r;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home);
    
    
            handler = new Handler();
            r = new Runnable() {
    
                @Override
                public void run() {
    
                    Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
                    startActivity(intent);
                    Log.d(TAG, "Logged out after 3 minutes on inactivity.");
                    finish();
    
                    Toast.makeText(HomeActivity.this, "Logged out after 3 minutes on inactivity.", Toast.LENGTH_SHORT).show();
                }
            };
    
            startHandler();
    
        }
    
        public void stopHandler() {
            handler.removeCallbacks(r);
            Log.d("HandlerRun", "stopHandlerMain");
        }
    
        public void startHandler() {
            handler.postDelayed(r, 3 * 60 * 1000);
            Log.d("HandlerRun", "startHandlerMain");
        }
    
        @Override
        public void onUserInteraction() {
            super.onUserInteraction();
            stopHandler();
            startHandler();
        }
    
        @Override
        protected void onPause() {
    
            stopHandler();
            Log.d("onPause", "onPauseActivity change");
            super.onPause();
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            startHandler();
    
            Log.d("onResume", "onResume_restartActivity");
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            stopHandler();
            Log.d("onDestroy", "onDestroyActivity change");
    
        }
    
    }
    
  • 0

    最好的办法是通过在Application calss中注册 AppLifecycleCallbacks 来处理整个应用程序(假设您有多个活动) . 您可以在Application类中使用带有以下回调的 registerActivityLifecycleCallbacks() (我建议创建一个扩展ActivityLifecycleCallbacks的AppLifecycleCallbacks类):

    public interface ActivityLifecycleCallbacks {
        void onActivityCreated(Activity activity, Bundle savedInstanceState);
        void onActivityStarted(Activity activity);
        void onActivityResumed(Activity activity);
        void onActivityPaused(Activity activity);
        void onActivityStopped(Activity activity);
        void onActivitySaveInstanceState(Activity activity, Bundle outState);
        void onActivityDestroyed(Activity activity);
    }
    

相关问题