วิธีตรวจจับการไม่ใช้งานของผู้ใช้ใน Android


101

ผู้ใช้เริ่มแอพของฉันและล็อกอิน
เลือก Session Timeout เป็น 5 นาที
ดำเนินการบางอย่างในแอป (ทั้งหมดอยู่เบื้องหน้า)
ตอนนี้ผู้ใช้นำ Myapp ไปที่พื้นหลังและเริ่มแอปอื่น ๆ
----> ตัวจับเวลานับถอยหลังเริ่มต้นและออกจากระบบผู้ใช้หลังจาก 5 นาที
หรือผู้ใช้ปิดหน้าจอ
----> ตัวจับเวลานับถอยหลังเริ่มต้นและออกจากระบบผู้ใช้หลังจาก 5 นาที

ฉันต้องการพฤติกรรมเดียวกันแม้ว่าแอปจะอยู่เบื้องหน้า แต่ผู้ใช้ไม่ได้โต้ตอบกับแอปเป็นเวลานานพูดว่า 6-7 นาที สมมติว่าหน้าจอเปิดอยู่ตลอดเวลา ฉันต้องการตรวจจับประเภทของการไม่ใช้งานของผู้ใช้ (ไม่มีการโต้ตอบกับแอปแม้ว่าแอปจะอยู่เบื้องหน้า) และเริ่มจับเวลาถอยหลัง


1
คุณสามารถให้ตัวจับเวลานั้นทำงานและรีเซ็ตทุกครั้งที่ผู้ใช้ทำอะไรได้หรือไม่?
Kyle P

คำตอบ:


112

ฉันคิดวิธีแก้ปัญหาที่ฉันพบว่าค่อนข้างง่ายตามคำตอบของ Fredrik Wallenius นี่คือคลาสกิจกรรมพื้นฐานที่ต้องขยายออกไปตามกิจกรรมทั้งหมด

public class MyBaseActivity extends Activity {

    public static final long DISCONNECT_TIMEOUT = 300000; // 5 min = 5 * 60 * 1000 ms


    private static Handler disconnectHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // todo
            return true;
        }
    });

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

3
สิ่งนี้จะสร้างหลายอินสแตนซ์ของHandlerและRunnableสำหรับแต่ละอินสแตนซ์ที่Activityสร้างขึ้น หากเราแปลงสมาชิกทั้งสองเป็นstaticสิ่งนี้จะหลีกเลี่ยงได้ นอกจากนี้คุณช่วยบอกฉันได้ไหมว่าทำไมคุณถึงโทรstopDisconnectTimer()เข้ามาonStop()``
Gaurav Bhor

@Gaurav ในกรณีของฉันสิ่งนี้จะถูกนำไปใช้ในกิจกรรมเดียวเท่านั้น (ดังนั้นฉันจึงไม่พบปัญหากับstaticตัวปรับแต่ง) สำหรับonStop()สิ่งที่ฉันจำได้ฉันโทรonBackPressed()เพื่อกลับไปที่หน้าจอเข้าสู่ระบบในการโทรกลับที่ตัดการเชื่อมต่อซึ่งจะเรียกonStop()วิธีการ เมื่อผู้ใช้กลับไปยังหน้าจอเข้าสู่ระบบด้วยตนเองโดยการกดกลับมาตอบสนองความต้องการการจับเวลาจะหยุดเช่นกันดังนั้นในstopDisconnectTimer() onStop()ฉันเดาว่าส่วนนี้ขึ้นอยู่กับความต้องการและการนำไปใช้ของคุณ
gfrigon

@gfrigon สามารถเปลี่ยนเส้นทางผู้ใช้ไปยังกิจกรรมการเข้าสู่ระบบได้หรือไม่?
Apostrofix

@Apostrifix แน่นอนว่าเป็นไปได้ ในกรณีของฉันมีเพียงกิจกรรมเดียวเท่านั้นคือการโทรไปยังที่onBackPressed()เพียงพอ หากคุณมีกิจกรรมมากกว่าหนึ่งกิจกรรมในสแต็กคุณก็ต้องสร้างเจตนาสำหรับเรื่องนั้น คุณอาจต้องการดูคำตอบต่อไปนี้เพื่อล้างงานกิจกรรม (และป้องกันไม่ให้ผู้ใช้เชื่อมต่ออีกครั้งที่ด้านหลัง): stackoverflow.com/questions/7075349/…
gfrigon

การทำงานที่ดี! ฉันเพิ่ม getter และ setter สำหรับ runnable แล้วตั้งค่าในคลาสขยายตามต้องการโดยใช้เมธอด onCreate ... สมบูรณ์แบบขอขอบคุณอีกครั้ง
CrandellWS

90

ฉันไม่รู้วิธีติดตามการไม่ใช้งาน แต่มีวิธีติดตามกิจกรรมของผู้ใช้ คุณสามารถรับสายเรียกกลับonUserInteraction()ในกิจกรรมของคุณที่ถูกเรียกทุกครั้งที่ผู้ใช้โต้ตอบกับแอปพลิเคชัน ฉันขอแนะนำให้ทำสิ่งนี้:

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

หากแอปของคุณมีกิจกรรมหลายอย่างทำไมไม่ใช้วิธีนี้ในคลาสซูเปอร์ที่เป็นนามธรรม (การขยายActivity) จากนั้นให้คุณมีกิจกรรมทั้งหมดที่ขยายออกไป


1
ใช่นี่เป็นวิธีหนึ่งในการทำ ... แต่แอปของฉันมีกิจกรรมที่แตกต่างกัน 30 กิจกรรมและจะมีการโต้ตอบมากเกินไปเมื่อผู้ใช้ใช้งาน ... ดังนั้นทุกครั้งที่รีเซ็ตตัวจับเวลาจะเป็นการดำเนินการที่มีค่าใช้จ่ายสูง ... กรณีที่เลวร้ายที่สุดสามารถ 50 ถึง 60 ครั้งในหนึ่งนาที
Akh

3
ฉันยังไม่ได้ตั้งเวลา แต่ฉันจะบอกว่าการรีเซ็ตตัวจับเวลาแบบนี้ lastInteraction = System.currentTimeMillis (); จะใช้เวลาพูด 2 มิลลิวินาที ทำ 60 ครั้งต่อนาทีและคุณ "หลวม" 120ms จาก 60000
Fredrik Wallenius

1
Fredrik ... ฉันกำลังใช้คำแนะนำของคุณเพื่อให้เป็นไปตามสถานการณ์นี้ .. ระยะหมดเวลาของหน้าจอตั้งไว้ที่สูงสุด 30 นาทีบนอุปกรณ์ MyApp shd หมดเวลาหลังจาก 15 นาที ... หากผู้ใช้ไม่ได้สัมผัสอะไรบนหน้าจอนานกว่า 1 นาทีฉันจะเริ่มตัวจับเวลาการออกจากระบบ 15 นาที .... ในกรณีนี้ฉันจะตรวจสอบความแตกต่าง (lastInteractionTime และ System.currentTimeMills ( )) มากกว่า 1 นาที ... แล้วไฟ ..
Akh

3
onUserInteraction () ไม่ถูกเรียกในบางกรณีอย่างไรก็ตาม (กล่องโต้ตอบไม่เรียกมันและการเลื่อนในตัวหมุน) มีวิธีแก้ปัญหาสำหรับสถานการณ์เหล่านี้หรือไม่?
AndroidNoob

คุณสามารถแบ่งปัน MyTimerClass ของคุณได้ไหม
Sibelius Seraphini

19

ฉันคิดว่าคุณควรใช้รหัสนี้ซึ่งเป็นช่วงหมดเวลาของเซสชันที่ไม่ได้ใช้งาน 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 
}

คุณช่วยชีวิตฉันด้วย onUserInteraction
codezombie

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

4
@ แนปปี้: แล้วช่วยอธิบายวิธีการทำที่ถูกต้อง ความคิดเห็นของคุณคลุมเครือและไม่แน่ใจ
Akh

2
@AKh: คำตอบอื่น ๆ แสดงความเป็นไปได้แล้ว ในการแก้ปัญหาของคุณฉันไม่เห็นประโยชน์ใด ๆ จากการสำรวจทุก ๆ 15 วินาที มันจะมีผลเช่นเดียวกันเมื่อคุณเริ่มจับเวลา "ACTION_SCREEN_OFF" โดยมีระยะเวลาสุ่มตั้งแต่ 0-15 วินาที แค่นี้ก็ไม่สมเหตุสมผล ..
Nappy

1
@Nappy: ทุก ๆ 15 วินาทีฉันไม่เพียง แต่ตรวจสอบ SCREEN_ON หรือ SCREEN_OFF แต่ยังรวมถึงเวลาโต้ตอบครั้งสุดท้ายของผู้ใช้และสถานะเบื้องหน้าของแอปด้วย จากปัจจัยทั้งสามนี้ฉันตัดสินใจอย่างมีเหตุผลว่าผู้ใช้โต้ตอบกับแอปมากน้อยเพียงใด
Akh

กรุณากรอกความคิดเห็นของคุณ .... "ถ้า isScreenof บูลีนของคุณคืออะไร" และยังต้องคำนึงถึงสถานะ foregrnd ของแอปด้วย
Akh

1
รหัสนี้เต็มไปด้วยข้อผิดพลาดตัวแปรบางตัวไม่ได้เริ่มต้น
บิ๊กเด็ก

8
@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));
}

นี่คือพื้นฐานของการแก้ปัญหาส่วนที่เหลือสามารถแก้ไขได้ขึ้นอยู่กับความต้องการส่วนหนึ่งของคุณและความซับซ้อนของสถาปัตยกรรมแอปพลิเคชัน! ขอบคุณสำหรับคำตอบ!
Hack06

วิธีใช้สิ่งนี้ในคลาสแอปพลิเคชัน
Gaju Kollur

6

ไม่มีแนวคิดเรื่อง "การไม่ใช้งานของผู้ใช้" ในระดับ OS นอกเหนือจากการออกอากาศACTION_SCREEN_OFFและการACTION_USER_PRESENTออกอากาศ คุณจะต้องกำหนด "การไม่ใช้งาน" ในแอปพลิเคชันของคุณเอง


6

แม้คุณจะสามารถจัดการกับความต้องการของคุณ@gfrigonหรือ@AKhการแก้ปัญหา

แต่นี่คือตัวจับเวลาและตัวจัดการโซลูชันฟรีสำหรับสิ่งนี้ ฉันมีโซลูชัน Timer ที่จัดการได้ดีสำหรับสิ่งนี้แล้ว แต่ฉันได้ใช้โซลูชันฟรีตัวจับเวลาและตัวจัดการสำเร็จแล้ว

ก่อนอื่นฉันจะบอกคุณว่าคุณต้องจัดการอะไรหากคุณใช้ตัวตั้งเวลาหรือตัวจัดการ

  • หากแอปของคุณถูกฆ่าโดยผู้ใช้หรือโดยเครื่องมือเพิ่มประสิทธิภาพแอปของคุณจะไม่ถูกล็อกเอาต์โดยอัตโนมัติเนื่องจากการโทรกลับทั้งหมดของคุณจะถูกทำลาย ( จัดการตัวจัดการสัญญาณเตือนหรือบริการบางอย่าง? )
  • การจับเวลาในทุกคลาสพื้นฐานจะดีหรือไม่? คุณกำลังสร้างเธรดจำนวนมากเพื่อเรียกใช้กระบวนการออกจากระบบ ( จัดการตัวจัดการแบบคงที่หรือตัวจับเวลาที่ระดับแอป? )
  • จะเกิดอะไรขึ้นหากผู้ใช้ทำงานอยู่เบื้องหลังตัวจัดการของคุณจะเริ่มกิจกรรมการเข้าสู่ระบบหากผู้ใช้ทำงานอื่น ๆ นอกแอปของคุณ ( จัดการพื้นหน้าหรือพื้นหลังของแอป? )
  • จะเกิดอะไรขึ้นถ้าหน้าจอปิดโดยอัตโนมัติ ( จัดการปิดหน้าจอเครื่องรับสัญญาณออกอากาศ? )

ในที่สุดฉันก็ใช้วิธีแก้ปัญหาซึ่งก็คือ

  1. ไม่มีตัวจัดการหรือตัวจับเวลา
  2. ไม่มีตัวจัดการสัญญาณเตือน
  3. ไม่ได้จัดการ App LifeCycle
  4. NO 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.
    }
}

1
การเข้าถึงค่ากำหนดที่ใช้ร่วมกันบนเธรดหลักในทุกการโต้ตอบของผู้ใช้ดีกว่าการเรียกใช้หลายเธรด
Nishita

@ นิชิตะตอนที่โพสต์คำตอบนี้ฉันไม่ทราบถึงความสับสนนี้ ขอบคุณสำหรับความคิดเห็นเกี่ยวกับ 1 คำตอบที่ไม่ดีของฉัน คุณพูดถูกนี่ไม่ใช่วิธีที่ง่ายที่จะทำ ฉันจะซ่อนคำตอบนี้
เขมราช

2

ในคลาสฐานกิจกรรมของฉันฉันได้สร้างคลาสที่มีการป้องกัน:

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

วิธีตรวจสอบ user is inactive ?? อินพุตใด ๆ จากระบบ?
MohsinSyd

2

ระหว่างการค้นหาของฉันฉันพบคำตอบมากมาย แต่นี่เป็นคำตอบที่ดีที่สุดที่ฉันได้รับ แต่ข้อ จำกัด ของรหัสนี้คือใช้ได้กับกิจกรรมเท่านั้นไม่ใช่สำหรับแอปพลิเคชันทั้งหมด ใช้เป็นข้อมูลอ้างอิง

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 วินาที


2

การไม่ใช้งานของผู้ใช้สามารถตรวจจับได้โดยใช้ onUserInteraction()วิธีการแทนที่ใน Android

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

    }

นี่คือรหัสตัวอย่างการ ลงชื่อเข้าใช้ (HomeActivity -> LoginActivity) หลังจาก 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");

    }

}

2

การจัดการผู้ใช้ในช่วงหมดเวลาการโต้ตอบใน 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 resetHandler() {
      timeoutHandler?.removeCallbacks(interactionTimeoutRunnable);
      timeoutHandler?.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second

}

 // start countdown
fun startHandler() {
    timeoutHandler?.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second
}

1

นี่คือโซลูชันที่สมบูรณ์ซึ่งจัดการกับการไม่มีการใช้งานของผู้ใช้หลังจากผ่านไปไม่กี่นาที (เช่น 3 นาที) วิธีนี้จะช่วยแก้ปัญหาทั่วไปเช่นกิจกรรมที่กระโดดเข้าสู่เบื้องหน้าเมื่อแอปอยู่เบื้องหลังเมื่อหมดเวลา

ประการแรกเราสร้าง BaseActivity ซึ่งกิจกรรมอื่น ๆ ทั้งหมดสามารถขยายได้

นี่คือรหัส BaseActivity

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

    }

}

ต่อไปเราจะสร้างอินเทอร์เฟซสำหรับ "Logout Listener" ของเรา

package com.example.timeout;

public interface LogoutListener {

    void onSessionLogout();

}

สุดท้ายเราสร้างคลาส Java ซึ่งขยาย "Application"

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>

0

ฉันคิดว่าต้องเป็นโดยการรวมตัวจับเวลากับเวลาเปิดใช้งานล่าสุด

เป็นเช่นนี้:

  1. ใน onCreate (Bundle savedInstanceState) เริ่มจับเวลาโดยพูดว่า 5 นาที

  2. ใน onUserInteraction () เพียงแค่เก็บเวลาปัจจุบัน

ค่อนข้างเรียบง่ายจนถึงตอนนี้

ตอนนี้เมื่อตัวจับเวลาป๊อปทำเช่นนี้:

  1. ใช้เวลาปัจจุบันและลบเวลาโต้ตอบที่เก็บไว้เพื่อรับ timeDelta
  2. หาก timeDelta เป็น> = 5 นาทีแสดงว่าคุณทำเสร็จแล้ว
  3. ถ้า timeDelta เป็น <5 นาทีให้เริ่มจับเวลาอีกครั้ง แต่เวลานี้ใช้เวลา 5 นาที - เวลาที่เก็บไว้ กล่าวอีกนัยหนึ่งคือ 5 นาทีเป็นการโต้ตอบสุดท้าย

0

ฉันมีสถานการณ์คล้ายกับคำถาม 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();
    }
}

ทรัพยากรเสริม

Android: ล้างกองกิจกรรม

คลาส Handler นี้ควรเป็นแบบคงที่หรืออาจมีการรั่วไหล


0

สิ่งที่ดีที่สุดคือจัดการสิ่งนี้ในแอปทั้งหมดของคุณ (สมมติว่าคุณมีกิจกรรมหลายอย่าง) โดยการลงทะเบียนAppLifecycleCallbacksในแอปพลิเคชัน calss คุณสามารถใช้registerActivityLifecycleCallbacks()ในคลาส Application ด้วยการเรียกกลับต่อไปนี้ (ขอแนะนำให้สร้างคลาส AppLifecycleCallbacks ที่ขยาย ActivityLifecycleCallbacks):

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

0
open class SubActivity : AppCompatActivity() {
    var myRunnable:Runnable
    private var myHandler = Handler()

    init {
        myRunnable = Runnable{
            toast("time out")
            var intent = Intent(this, MainActivity::class.java)
            startActivity(intent)

        }
    }

    fun toast(text: String) {
        runOnUiThread {
            val toast = Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT)
            toast.show()
        }
    }

   override fun onUserInteraction() {
        super.onUserInteraction();
        myHandler.removeCallbacks(myRunnable)
        myHandler.postDelayed(myRunnable, 3000)
    }

    override fun onPause() {
        super.onPause()
        myHandler.removeCallbacks(myRunnable)
    }

    override fun onResume() {
            super.onResume()
            myHandler.postDelayed(myRunnable, 3000)
    }
}

ขยายกิจกรรมของคุณด้วย

YourActivity:SubActivity(){}

เพื่อไปที่ MainActivity เมื่อผู้ใช้ไม่ได้ใช้งานหลังจาก 3,000 มิลลิวินาทีบน YourActivity

ฉันใช้คำตอบก่อนหน้านี้และแปลงเป็น kotlin

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.