設計思路
- 定義 BaseActivity 作為所有頁面父類。
- 重寫
dispatchTouchEvent() 記錄最後操作時間戳。
- 啟動 Timer 定期檢查間隔,超時則觸發登出邏輯。
完整實現代碼
1. BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
private static final long TIMEOUT_MS = 5 * 60 * 1000; // 5 分鐘
private static Timer sTimer;
private static long sLastTouchTime = System.currentTimeMillis();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
sLastTouchTime = System.currentTimeMillis();
return super.dispatchTouchEvent(ev);
}
@Override
protected void onResume() {
super.onResume();
startTimer();
}
@Override
protected void onPause() {
super.onPause();
// 可選:後台不計時,視業務而定
}
private static void startTimer() {
if (sTimer != null) return;
sTimer = new Timer(true); // 守護線程
sTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (System.currentTimeMillis() - sLastTouchTime > TIMEOUT_MS) {
// 切換到主線程執行登出
new Handler(Looper.getMainLooper()).post(() -> {
logoutAllActivities();
});
stopTimer();
}
}
}, 0, 1000); // 每秒檢查一次
}
private static void stopTimer() {
if (sTimer != null) {
sTimer.cancel();
sTimer = null;
}
}
private static void logoutAllActivities() {
// 方案 A:發送廣播通知所有 Activity finish
Intent intent = new Intent("ACTION_FORCE_LOGOUT");
LocalBroadcastManager.getInstance(getAppContext()).sendBroadcast(intent);
// 方案 B:維護 Activity 棧列表逐個 finish (需 Application 管理)
// ActivityStackManager.getInstance().finishAll();
// 方案 C:啟動登錄頁並清空棧
Intent login = new Intent(getAppContext(), LoginActivity.class);
login.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
getAppContext().startActivity(login);
}
// 獲取 Application Context 的靜態方法 (需在 Application 子類中實現)
private static Context getAppContext() { return MyApplication.getInstance(); }
}
2. 子 Activity 註冊廣播
public class MainActivity extends BaseActivity {
private BroadcastReceiver mLogoutReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LocalBroadcastManager.getInstance(this).registerReceiver(mLogoutReceiver,
new IntentFilter("ACTION_FORCE_LOGOUT"));
}
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLogoutReceiver);
}
}
關鍵點說明
| 要點 | 說明 |
| 靜態變量 | Timer、lastTouchTime 為 static,全進程共享,避免多 Activity 多次創建定時器 |
| 守護線程 | new Timer(true) 不阻塞進程退出 |
| 主線程切換 | UI 操作 (finish/startActivity) 必須在主線程,用 Handler.post |
| 清理註冊 | onDestroy 取消註冊廣播,防內存洩漏 |
| 配置化 | 超時時間、檢查間隔建議放入配置文件/SharedPreferences |
替代方案
- JobScheduler / WorkManager:系統級定時任務,進程被殺也能觸發,但最小間隔 15 分鐘,不適合精確 5 分鐘需求。
- RxJava/RxAndroid:
Observable.interval(1, TimeUnit.SECONDS) + takeUntil 實現更優雅。