ユーザーがアプリを起動してログインします。
セッションタイムアウトを5分に選択します。
アプリでいくつかの操作を行います。 (すべてフォアグラウンドで)
今、ユーザーはMyappをバックグラウンドにし、他のアプリを起動します。
---->カウントダウンタイマーが開始し、5分後にユーザーをログアウトします
またはユーザーが画面をオフにします。
---->カウントダウンタイマーが開始し、5分後にユーザーをログアウトします
アプリがフォアグラウンドにあるが、ユーザーが6〜7分と長時間アプリと対話しない場合でも、同じ動作が必要です。画面が常にオンになっていると仮定します。 ser inactivity(アプリがフォアグラウンドにある場合でもアプリとの相互作用がない)の種類を検出し、カウントダウンタイマーを開始します。
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
ブロードキャスト以外に、OSレベルでは「ユーザーの非アクティブ」という概念はありません。独自のアプリケーション内で何らかの方法で「非アクティブ」を定義する必要があります。
@ gfrigon または @ AKh ソリューションで要件を管理することもできます。
しかし、これはTimer and Handlers free solutionです。私はすでにこのためのタイマーソリューションをうまく管理しています。しかし、私は正常にタイマーとハンドラーフリーソリューションを実装しました。
最初に、タイマーまたはハンドラーを使用する場合に管理する必要があるものを教えます。
最後に、私はソリューションを実装しました
ACTION_SCREEN_ON
/ACTION_SCREEN_OFF
ブロードキャストレシーバー。最も簡単な信頼できるソリューション
ユーザーアクティビティの最終アクティビティ時間を確認するのではなく、タイマーによるユーザーの非アクティブ状態を監視しません。そのため、ユーザーが次回アプリを操作するときに、最後の操作時間を確認します。
これは、LoginActivity
の代わりに、すべてのアクティビティクラスから拡張するBaseActivity.class
です。このクラスの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();
}
onResumeメソッド-コールバックでアクションを指定できます。
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秒間のユーザーの非アクティブ後に実行されます。
ユーザーの非アクティブ状態は、AndroidのonUserInteraction()
オーバーライドメソッドを使用して検出できます
@Override
public void onUserInteraction() {
super.onUserInteraction();
}
サンプルコードは、分後のサインアウト(HomeActivity-> LoginActivity)ユーザーが非アクティブの場合
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(Bundle savedInstanceState)でタイマーを開始します(5分など)
OnUserInteraction()では、現在の時刻を保存するだけです
これまでのところ非常に簡単です。
タイマーポップが次のようになったら:
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
}
SOの質問に似た状況がありました。1分間ユーザーの非アクティブを追跡し、ユーザーをリダイレクトしてアクティビティを開始する必要があり、アクティビティスタックをクリアする必要もありました。
@gfrigonの回答に基づいて、私はこの解決策を考え出します。
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();
}
}
最善の方法は、アプリケーションのcalsに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);
}
私はこの答えを追加するには遅すぎることを知っていますが、それは先のすべての新しい視聴者にあります。上記のコードのほとんどを試し、自分で試しました。しかし、それらのどれも私のために働きませんでした。以下のコードは、新しいビューアが探しているものとまったく同じように機能しました。このコードは機能します:
public class MainActivity extends AppCompatActivity {
private Timer timer;
private Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
@Override
protected void onPause() {
super.onPause();
timer = new Timer();
Log.i("Main", "Invoking logout timer");
LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
timer.schedule(logoutTimeTask, 300000); //auto logout in 5 minutes
}
@Override
protected void onResume() {
super.onResume();
if (timer != null) {
timer.cancel();
Log.i("Main", "cancel timer");
timer = null;
}
}
private class LogOutTimerTask extends TimerTask {
@Override
public void run() {
//redirect user to login screen
Intent i = new Intent(MainActivity.this, YourActivity.class); //where
you want to load when inactive
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
}
}