【问题标题】:Lock android app after a certain amount of idle time在一定的空闲时间后锁定 android 应用程序
【发布时间】:2010-10-09 06:17:48
【问题描述】:

我的 android 应用程序需要在第一个活动中输入密码。我希望能够在应用程序空闲一定时间后自动将应用程序发送回密码输入屏幕。

应用程序有多个活动,但我希望所有活动的超时都是全局的。因此,在Activity 的onPause() 方法中创建计时器线程是不够的。

我不确定应用程序处于空闲状态的最佳定义是什么,但没有活动处于活动状态就足够了。

【问题讨论】:

    标签: android


    【解决方案1】:

    我知道另一个答案已被接受,但我在处理类似问题时遇到了这个问题,并认为我将尝试另一种更简单的方法,我认为如果其他人想尝试的话,我不妨记录一下沿着相同的路径。在此处输入代码

    一般的想法只是在任何活动暂停时在 SharedPreference 中跟踪系统时钟时间 - 听起来很简单,但是如果你只使用这些,就会存在一个安全漏洞,因为该时钟会在重启时重置。要解决这个问题:

    • 有一个Application 子类或共享静态单例类,具有全局解锁自启动状态(最初为假)。此值应与您的应用程序的进程一样长。
    • 如果当前应用状态已解锁,则将每个相关Activity 的onPause 中的系统时间(自启动后的realtime)保存到SharedPreference。
    • 如果 appwide unlocked-since-boot 状态为 false(干净的应用程序启动 - 应用程序或手机重新启动),则显示锁定屏幕。否则,在可锁定活动的 onResume 中检查SharedPreference 的值;如果不存在或大于SharedPreference 值 + 超时间隔,则也显示锁定屏幕。
    • 当应用解锁时,将 appwide unlocked-since-boot 状态设置为 true。

    除了超时之外,如果您的应用被杀死并重新启动或者您的手机重新启动,这种方法还会自动锁定您的应用,但我认为这对于大多数应用来说并不是一个特别糟糕的问题。它有点过于安全,可能会不必要地锁定任务切换很多的用户,但我认为通过完全消除任何后台进程/唤醒锁问题(不需要服务、警报或接收器)来减少代码和复杂性是值得的权衡)。

    要解决不考虑时间锁定应用程序的进程终止问题,您可以使用 SharedPreference 并为系统启动广播意图注册一个侦听器以将该 Preference 设置为 false .这重新增加了初始解决方案的一些复杂性,其好处是在应用程序的进程在超时间隔内被后台终止的情况下更加方便,尽管对于大多数应用程序来说,这可能是矫枉过正。

    【讨论】:

    • 为什么需要将时间保存在 sharedPrefs 中?如您所说,将最后一次实时保存在singelton / application中,在应用程序启动时初始化为null,然后如果它存在,则表示该应用程序没有被杀死并且您可以使用它,如果操作系统已重新启动,那么将会为空......
    【解决方案2】:

    我通过使用 AlarmManager 来安排和取消超时操作来处理这个问题。

    然后在我所有活动的 onPause() 事件中,我安排了闹钟。在我所有活动的 onResume() 事件中,我检查警报是否响起。如果闹钟响起,我会关闭我的应用程序。如果闹钟还没响,我就取消它。

    我创建了 Timeout.java 来管理我的警报。当警报响起时,就会触发 Intent:

    public class Timeout {
        private static final int REQUEST_ID = 0;
        private static final long DEFAULT_TIMEOUT = 5 * 60 * 1000;  // 5 minutes
    
        private static PendingIntent buildIntent(Context ctx) {
            Intent intent = new Intent(Intents.TIMEOUT);
            PendingIntent sender = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    
            return sender;
        }
    
        public static void start(Context ctx) {
            ctx.startService(new Intent(ctx, TimeoutService.class));
    
            long triggerTime = System.currentTimeMillis() + DEFAULT_TIMEOUT;
    
            AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
    
            am.set(AlarmManager.RTC, triggerTime, buildIntent(ctx));
        }
    
        public static void cancel(Context ctx) {
            AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
    
            am.cancel(buildIntent(ctx));
    
            ctx.startService(new Intent(ctx, TimeoutService.class));
    
        }
    
    }
    

    然后,我创建了一个服务来捕获警报生成的意图。它在我的应用程序类实例中设置了一些全局状态,以指示应用程序应该锁定:

    public class TimeoutService extends Service {
        private BroadcastReceiver mIntentReceiver;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            mIntentReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    String action = intent.getAction();
    
                    if ( action.equals(Intents.TIMEOUT) ) {
                        timeout(context);
                    }
                }
            };
    
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intents.TIMEOUT);
            registerReceiver(mIntentReceiver, filter);
    
        }
    
        private void timeout(Context context) {
            App.setShutdown();
    
            NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            nm.cancelAll();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            unregisterReceiver(mIntentReceiver);
        }
    
        public class TimeoutBinder extends Binder {
            public TimeoutService getService() {
                return TimeoutService.this;
            }
        }
    
        private final IBinder mBinder = new TimeoutBinder();
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
    }
    

    最后,我创建了一个 Activity 的子类,我的应用程序的所有 Activity 都从该子类中管理锁定和解锁:

    public class LockingActivity extends Activity {
    
        @Override
        protected void onPause() {
            super.onPause();
    
            Timeout.start(this);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
    
            Timeout.cancel(this);
            checkShutdown();
        }
    
        private void checkShutdown() {
            if ( App.isShutdown() ) {
                finish();
            }
    
        }
    
    }
    

    使用 onPause 和 onResume 启动和停止超时给了我以下语义。只要我的应用程序的一项活动处于活动状态,超时时钟就不会运行。由于我使用了 AlarmManager.RTC 的警报类型,因此每当手机进入睡眠状态时,超时时钟都会运行。如果在手机处于睡眠状态时发生超时,那么我的服务将在手机唤醒后立即获取超时。此外,时钟会在任何其他活动打开时运行。

    对于这些更详细的版本,你可以看到我是如何在我的应用程序中实际实现它们的https://github.com/bpellin/keepassdroid

    【讨论】:

    • 您的代码从不使用 TimeoutService 类。你能说明你是如何以及在哪里使用它的吗?
    • 我曾经在应用程序启动时启动服务,并在应用程序结束时停止它。这产生了很多令人担忧的电子邮件,因为人们看到该服务正在运行并认为它使用了不当资源。因此,我在 Timeout 的启动和停止方法中添加了代码来启动和停止服务。我编辑了我的答案以反映这一点。
    【解决方案3】:

    查看OpenIntentsSafe 如何实现此功能。

    【讨论】:

      【解决方案4】:

      这对我来说是一个非常有用的帖子。支持@Yoni Samlan 给出的概念。我是这样实现的

      public void pause() {
              // Record timeout time in case timeout service is killed    
              long time = System.currentTimeMillis();     
              SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
              SharedPreferences.Editor edit = preferences.edit();
              edit.putLong("Timeout_key", time);// start recording the current time as soon as app is asleep
              edit.apply();
          }
      
          public void resume() {       
              // Check whether the timeout has expired
              long cur_time = System.currentTimeMillis();
              SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
              long timeout_start = preferences.getLong("Timeout_key", -1);
              // The timeout never started
              if (timeout_start == -1) {
                  return;
              }   
              long timeout;
              try {
                  //timeout = Long.parseLong(sTimeout);
                  timeout=idle_delay;
              } catch (NumberFormatException e) {
                  timeout = 60000;
              }
              // We are set to never timeout
              if (timeout == -1) {
                  return;
              }
              if (idle){
              long diff = cur_time - timeout_start;
              if (diff >= timeout) {  
                  //Toast.makeText(act, "We have timed out", Toast.LENGTH_LONG).show(); 
                  showLockDialog();
              }
              }
          } 
      

      从 onPause 调用 pause 方法,从 onResume 调用 resume 方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-28
        • 2014-03-29
        • 2015-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多