【发布时间】:2010-10-09 06:17:48
【问题描述】:
我的 android 应用程序需要在第一个活动中输入密码。我希望能够在应用程序空闲一定时间后自动将应用程序发送回密码输入屏幕。
应用程序有多个活动,但我希望所有活动的超时都是全局的。因此,在Activity 的onPause() 方法中创建计时器线程是不够的。
我不确定应用程序处于空闲状态的最佳定义是什么,但没有活动处于活动状态就足够了。
【问题讨论】:
标签: android
我的 android 应用程序需要在第一个活动中输入密码。我希望能够在应用程序空闲一定时间后自动将应用程序发送回密码输入屏幕。
应用程序有多个活动,但我希望所有活动的超时都是全局的。因此,在Activity 的onPause() 方法中创建计时器线程是不够的。
我不确定应用程序处于空闲状态的最佳定义是什么,但没有活动处于活动状态就足够了。
【问题讨论】:
标签: android
我知道另一个答案已被接受,但我在处理类似问题时遇到了这个问题,并认为我将尝试另一种更简单的方法,我认为如果其他人想尝试的话,我不妨记录一下沿着相同的路径。在此处输入代码
一般的想法只是在任何活动暂停时在 SharedPreference 中跟踪系统时钟时间 - 听起来很简单,但是如果你只使用这些,就会存在一个安全漏洞,因为该时钟会在重启时重置。要解决这个问题:
Application 子类或共享静态单例类,具有全局解锁自启动状态(最初为假)。此值应与您的应用程序的进程一样长。Activity 的onPause 中的系统时间(自启动后的realtime)保存到SharedPreference。SharedPreference 的值;如果不存在或大于SharedPreference 值 + 超时间隔,则也显示锁定屏幕。除了超时之外,如果您的应用被杀死并重新启动或者您的手机重新启动,这种方法还会自动锁定您的应用,但我认为这对于大多数应用来说并不是一个特别糟糕的问题。它有点过于安全,可能会不必要地锁定任务切换很多的用户,但我认为通过完全消除任何后台进程/唤醒锁问题(不需要服务、警报或接收器)来减少代码和复杂性是值得的权衡)。
要解决不考虑时间锁定应用程序的进程终止问题,您可以使用 SharedPreference 并为系统启动广播意图注册一个侦听器以将该 Preference 设置为 false .这重新增加了初始解决方案的一些复杂性,其好处是在应用程序的进程在超时间隔内被后台终止的情况下更加方便,尽管对于大多数应用程序来说,这可能是矫枉过正。
【讨论】:
我通过使用 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
【讨论】:
查看OpenIntentsSafe 如何实现此功能。
【讨论】:
这对我来说是一个非常有用的帖子。支持@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 方法。
【讨论】: