题
用户启动我的应用程序并登录。
选择会话超时为5分钟。
在应用程序上进行一些操作。 (全部在前景)
现在,用户将MyApp带到后台并启动其他应用程序。
---->计算计时器启动并在5分钟后注销用户
或用户关闭屏幕。
---->计算计时器启动并在5分钟后注销用户
即使该应用在前景中,我也希望采取相同的行为,但用户长期以来不与应用程序进行6-7分钟的互动。假设屏幕一直在上面。我想检测到 用户无效 (即使该应用在前景中也没有与应用程序互动)并启动我的计数计时器。
解决方案 4
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 ===>逻辑在这里未显示,因为它不在问题的范围
您可以通过在下面使用设备代码唤醒锁定到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);
}
}
其他提示
我提出了一个解决方案,根据弗雷德里克·沃伦纽斯(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();
}
}
我不知道一种跟踪无活动的方法,但是有一种方法可以跟踪用户活动。您可以捕获一个叫做的回调 onUserInteraction()
在您的活动中,每次用户与应用程序进行任何交互时。我建议这样做这样的事情:
@Override
public void onUserInteraction(){
MyTimerClass.getInstance().resetTimer();
}
如果您的应用程序包含多个活动,为什么不将此方法放入抽象的超级类中(扩展 Activity
),然后让所有活动扩展。
我认为您应该使用此代码,这是用于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
}
@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));
}
在操作系统级别上没有“用户无效”的概念 ACTION_SCREEN_OFF
和 ACTION_USER_PRESENT
广播。您必须在自己的应用程序中以某种方式定义“无活动”。
即使您也可以通过 @gfrigon 或者 @akh 解决方案。
但这是 计时器和处理程序免费解决方案 为了这。我已经为此提供了良好的计时器解决方案。但是我已经成功地实施了计时器和处理程序的免费解决方案。
首先我告诉你 你必须管理什么 如果使用计时器或处理程序。
- 如果您的应用程序被用户或优化器杀死,则您的应用程序将永远不会自动注销,因为您的所有回调都被破坏了。 ((管理一些警报管理器或服务?)
- 在每个基类中都有计时器好吗?您只是为了调用注销过程而制作许多线程(在应用程序级别管理静态处理程序或计时器?).
- 如果用户在后台,则您的处理程序将开始登录活动,如果用户在应用程序之外进行其他工作。 ((管理应用程序前景还是背景?).
- 如果屏幕自动下车怎么办。 ((管理广播接收器上的屏幕?)
最后,我实施了一个解决方案
- 没有处理程序或计时器。
- 没有警报管理器。
- 不管理应用程序生命周期。
- 不
ACTION_SCREEN_ON
/ACTION_SCREEN_OFF
广播接收器。
最简单的可靠解决方案
我们不会通过计时器观察到用户无效,而不是我们会检查用户活动的最后一个活动时间。因此,下次用户交互应用程序时,我会检查最后一个交互时间。
这是 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.
}
}
在我的活动基类中,我创建了受保护的类:
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();
}
所以 俄罗斯 方法 - 您可以在回调中指定操作您想处理什么...
idleTimer = new IdleTimer(60000, new IIdleCallback() {
@Override
public void inactivityDetected() {
...your move...
}
});
idleTimer.startIdleTimer();
在搜索过程中,我找到了很多答案,但这是我得到的最好的答案。但是该代码的限制是它仅适用于活动而不是整个应用程序。以此为参考。
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秒钟后完成。
用户不活动可以使用 onUserInteraction()
Android中的覆盖方法
@Override
public void onUserInteraction() {
super.onUserInteraction();
}
这是示例代码, 3分钟后的签名(家庭反应性 - >登录) 当用户无效时
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");
}
}
我认为必须将计时器与最后一个激活时间组合在一起。
如此这样:
在OnCreate(捆绑savedinstancestate)中开始一个计时器,说5分钟
在OnuserInteraction()中只存储当前时间
到目前为止很简单。
现在,当计时器流行确实喜欢时:
- 花时间并减去存储的互动时间以获取timedelta
- 如果TimeDERTA为> = 5分钟,您就完成了
- 如果TimeDERTA为<5分钟再次开始计时器,但是这次使用5分钟 - 存储的时间。换句话说,5分钟形成最后一个互动
我的情况与SO问题相似,在这里我需要在其中跟踪用户不活动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();
}
}
互补资源
最好的事情是通过注册在整个应用程序(假设您有多个活动)上处理此问题 AppLifecycleCallbacks
在应用程序中。您可以使用 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);
}
在Kotlin的交互超时处理用户:
//Declare handler
private var timeoutHandler: Handler? = null
private var interactionTimeoutRunnable: Runnable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_aspect_ratio)
//Initialise handler
timeoutHandler = Handler();
interactionTimeoutRunnable = Runnable {
// Handle Timeout stuffs here
}
//start countdown
startHandler()
}
// reset handler on user interaction
override fun onUserInteraction() {
super.onUserInteraction()
resetHandler()
}
//restart countdown
fun reset() {
timeoutHandler!!.removeCallbacks(interactionTimeoutRunnable);
timeoutHandler!!.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second
}
// start countdown
fun startHandler() {
timeoutHandler!!.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second
}
这是一个完整的解决方案,可在几分钟后处理用户无效(例如3分钟)。这解决了常见问题,例如当应用程序在超时的背景中时,活动跳入前景。
首先,我们创建了所有其他活动都可以扩展的碱基反应性。
这是基础性代码。
package com.example.timeout;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import javax.annotation.Nullable;
public class BaseActivity extends AppCompatActivity implements LogoutListener {
private Boolean isUserTimedOut = false;
private static Dialog mDialog;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((TimeOutApp) getApplication()).registerSessionListener(this);
((TimeOutApp) getApplication()).startUserSession();
}
@Override
public void onUserInteraction() {
super.onUserInteraction();
}
@Override
protected void onResume() {
super.onResume();
if (isUserTimedOut) {
//show TimerOut dialog
showTimedOutWindow("Time Out!", this);
} else {
((TimeOutApp) getApplication()).onUserInteracted();
}
}
@Override
public void onSessionLogout() {
isUserTimedOut = true;
}
public void showTimedOutWindow(String message, Context context) {
if (mDialog != null) {
mDialog.dismiss();
}
mDialog = new Dialog(context);
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
mDialog.setContentView(R.layout.dialog_window);
mDialog.setCancelable(false);
mDialog.setCanceledOnTouchOutside(false);
TextView mOkButton = (TextView) mDialog.findViewById(R.id.text_ok);
TextView text_msg = (TextView) mDialog.findViewById(R.id.text_msg);
if (message != null && (!TextUtils.isEmpty(message)) && (!message.equalsIgnoreCase("null"))) {
text_msg.setText(message);
}
mOkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDialog != null){
mDialog.dismiss();
Intent intent = new Intent(BaseActivity.this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
}
});
if(!((Activity) context).isFinishing())
{
//show dialog
mDialog.show();
}
}
}
接下来,我们为“注销侦听器”创建一个接口
package com.example.timeout;
public interface LogoutListener {
void onSessionLogout();
}
最后,我们创建一个Java类,该类扩展“应用程序”
package com.example.timeout;
import android.app.Application;
import java.util.Timer;
import java.util.TimerTask;
public class TimeOutApp extends Application {
private LogoutListener listener;
private Timer timer;
private static final long INACTIVE_TIMEOUT = 180000; // 3 min
public void startUserSession () {
cancelTimer ();
timer = new Timer ();
timer.schedule(new TimerTask() {
@Override
public void run() {
listener.onSessionLogout ();
}
}, INACTIVE_TIMEOUT);
}
private void cancelTimer () {
if (timer !=null) timer.cancel();
}
public void registerSessionListener(LogoutListener listener){
this.listener = listener;
}
public void onUserInteracted () {
startUserSession();
}
}
注意:不要忘记将“ timeoutapp”类添加到您的应用程序中的应用程序标签
<application
android:name=".TimeOutApp">
</application>