我的Android应用程序需要在第一个活动中输入密码。我希望能够在应用程序闲置一段固定时间后自动将应用程序发送回密码输入屏幕。
应用程序有多个活动,但我希望超时对所有活动都是全局的。因此,在onPause()
的{{1}}方法中创建计时器线程是不够的。
我不确定应用程序空闲的最佳定义是什么,但没有活动就足够了。
答案 0 :(得分:7)
我知道已经接受了另一个答案,但我遇到了类似的问题,我想我会尝试另一种更简单的方法,我想我也可以记录是否有其他人想要尝试去同样的路径。输入代码
一般的想法只是在任何Activity暂停时跟踪SharedPreference中的系统时钟时间 - 听起来很简单,但是,如果你使用的话,那就有一个安全漏洞,因为那个时钟会在重启时重置。解决这个问题:
Application
子类或共享静态单例类,具有全局解锁启动状态(最初为false)。只要您的应用程序的过程,该值应该存在。Activity
的{{1}}中的系统时间(realtime启动后)保存到onPause
。SharedPreference
值;如果它不存在或大于SharedPreference
值+超时间隔,也会显示锁定屏幕。除了超时,如果您的应用程序被杀死并重新启动或手机重新启动,此方法也会自动锁定您的应用程序,但我不认为这对大多数应用程序来说都是一个特别糟糕的问题。它有点过于安全,可能会对任务切换很多的用户造成不必要的锁定,但我认为通过彻底清除任何后台进程/唤醒锁问题(无需服务,警报或接收器)来减少代码和复杂性是值得的权衡)。
要解决在不管时间的情况下锁定应用程序的进程,而不是共享appwidewide单例以进行解锁 - 启动,您可以使用SharedPreference并为系统引导广播意图注册一个侦听器,将Preference设置为false 。这再次增加了初始解决方案的一些复杂性,在应用程序的进程在超时间隔内的后台处理被杀死的情况下可以获得更多便利,尽管对于大多数应用程序来说它可能有点过分。
答案 1 :(得分:3)
我通过使用AlarmManager来安排和取消超时操作来解决这个问题。
然后在我所有活动的onPause()事件中,我安排了闹钟。在我所有活动的onResume()事件中,我检查警报是否响起。如果警报响起,我关闭了我的应用程序。如果警报还没有消失,我就取消它。
我创建了Timeout.java来管理我的警报。当警报响起时,意图被触发:
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的子类,我的应用程序的所有活动都是子类来管理锁定和解锁:
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来启动和停止超时给出了以下语义。只要我的应用程序的某个活动处于活动状态,超时时钟就不会运行。由于我使用Alarm类型的AlarmManager.RTC,每当手机进入休眠状态时,超时时钟就会运行。如果在手机处于睡眠状态时发生超时,那么我的服务将在手机唤醒后立即取消超时。此外,当任何其他活动打开时,时钟会运行。
有关这些内容的更详细版本,您可以看到我在我的应用中实际实现它们的方式 https://github.com/bpellin/keepassdroid
答案 2 :(得分:2)
查看OpenIntents安全如何实现此功能。
答案 3 :(得分:0)
这对我来说是一个非常有用的帖子。支持@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调用暂停方法,从onResume调用resume方法。