【问题标题】:How to lock out user after X amount of inactivity?如何在 X 量不活动后锁定用户?
【发布时间】:2018-04-11 15:02:28
【问题描述】:

每次用户处于非活动状态 X 时间时,我都需要显示 Activity。我正在尝试通过自定义CountDownTimer 来实现这一点,它在我的BaseActivity 中以onUserInteraction 开头:

@Override
public void onUserInteraction() {
    super.onUserInteraction();
    inactivityTimer.cancel();
    inactivityTimer.start();
}

在我的自定义CountDownTimer 中,我启动所需的Activity onFinish

@Override
public void onFinish() {
    BaseActivity baseActivity = new BaseActivity();
    Log.i("TIMER ENDED: ", "NOW STARTING LOCKACTIVITY");
    baseActivity.showLock();
}

这是我在BaseActivity 中的showLock() 方法

public void showLock() {
    Intent intent = new Intent(getApplicationContext(), LockActivity.class);
    startActivity(intent);
}

每次计时器结束时,我都会得到一个 NPE。 (java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference) 即使我尝试使用getApplication().getBaseContext()thisthis.getBaseContext()this.getApplicationContext()getApplicationContext()and getBaseContext() 而不是 getApplicationContext()

但是,如果我像这样在方法调用中设置Context

@Override
public void onFinish() {
    BaseActivity baseActivity = new BaseActivity();
    Context context = MyApplication.getInstance().getApplicationContext();
    Log.i("TIMER ENDED: ", "NOW STARTING LOCKACTIVITY");
    baseActivity.showLock(context);
}

这个在showLock():

public void showLock(Context context) {
    Intent intent = new Intent(context, LockActivity.class);
    startActivity(intent);
}

这次我又收到了一个 NPE (java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference)。

所以我的问题是,每次从当前的Activity 调用我的showLock() 时,我如何获得正确的Context,这将调用该方法?

重要提示:我项目中的每个Activity 都继承BaseActivity,而BaseActivity 本身又继承AppCompatActivity

编辑

我尝试了 Marcin 的建议,在处理了几个错误后,我最终使用了他的方法。如果其他人很好奇并想知道如何在 X 量不活动后打开活动,这对我有用:

由于我所有的活动都继承了一个主要的BaseActivity,所以我放了一个自定义的Handler,其中包含一个WeakReferenceBaseActivity。我还覆盖了handleMessage,在这里我调用了我想要的方法:

private static class InactivityHandler extends Handler {
    private WeakReference<BaseActivity> baseActivityWeakReference;

    private InactivityHandler(BaseActivity baseActivity) {
        baseActivityWeakReference = new WeakReference<>(baseActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        BaseActivity baseActivity = baseActivityWeakReference.get();
        if (baseActivity != null) {
            baseActivity.showLock();
        }
    }
}

一段时间后,在onUserInteraction 中将Message 发送到队列:

@Override
public void onUserInteraction() {
    super.onUserInteraction();
    inactivityHandler.removeMessages(MESSAGE_WHAT, MESSAGE_TOKEN);
    inactivityHandler.sendMessageDelayed(inactivityHandler.obtainMessage(MESSAGE_WHAT, MESSAGE_TOKEN), DELAY_TIME);
}

对于好奇的人,这是我的showLock 方法:

public void showLock() {
    Intent intent = new Intent(this, LockActivity.class);
    startActivity(intent);
}

【问题讨论】:

    标签: java android android-activity android-context


    【解决方案1】:

    根据您的描述,我假设在用户处于非活动状态一段时间后,您的应用需要显示一个锁定屏幕,用户需要在其中重新输入其凭据。

    除非整个场景涉及任何计数(例如,您显示实际倒计时),否则CountDownTimer 可能不是执行此任务的最佳选择。

    您可以改为使用Handler。在 Android 中,主线程有它关联的消息队列。处理程序能够将消息发布到此队列,以便稍后在给定时间接收它们。

    您的示例实现可能如下所示:

    private static class LockScreenHandler extends Handler {
      private WeakReference<BaseActivity> activityRef;
    
      public LockScreenHandler(BaseActivity activity) {
        activityRef = new WeakReference<>(activity);
      }
    
      @Override public handleMessage(Message msg) {
        BaseActivity activity = activityRef.get();
    
        if (activity != null) {
          activity.showLock();
        } // Otherwise the activity got destroyed in the meantime
      }
    }
    

    您可以使用 Handler 发送 Runnables 或 Messages。在我们的例子中,Message 非常好。因此,在您的基本活动中,您可能有一些与消息相关的字段:

    private static final int MESSAGE_WHAT = 1;
    private static final Object MESSAGE_TOKEN = new Object();
    

    然后你在 onUserInteraction 中使用你的处理程序:

    @Override public void onUserInteraction() {
      super.onUserInteraction();
      handler.removeMessages(MESSAGE_WHAT, MESSAGE_TOKEN);
      handler.postDelayed(handler.obtainMessage(MESSAGE_WHAT, MESSAGE_TOKEN), INACTIVITY_DELAY);
    }
    

    如果您决定采用您的 CountDownTimer 解决方案,您可以采用相同的技术,方法是创建一个静态内部类并在构造函数中提供您的活动。

    无论您采用哪种方式,重要的是要注意,您的 BaseActivity 可能会被系统破坏,并且不正确使用 Handler(以及内部依赖于 Handler 的 CountDownTimer)可以防止对该活动的引用被破坏,从而导致到内存泄漏。因此:

    • 如果您使用 Handler 或 CountDownTimer 作为内部类,请确保它是 static。非静态内部类持有对其封闭类的引用。消息持有对其目标处理程序的引用,因此只要消息在队列中,它的处理程序就不会被销毁。

    • 出于与上述相同的原因,使用 Wea​​kReference 来保存您的活动。如果没有其他对象持有对给定对象的引用,则 WeakReferences 将被清除。

    【讨论】:

    • 我花时间按照您建议的方式重做整个事情,在处理了这里和那里的几个错误之后,它起作用了。感谢您的时间和精力,非常感谢!
    【解决方案2】:

    一个活动一个上下文本身。因此,只需在 Activity 中使用 this

    public void showLock() {
        Intent intent = new Intent(this, LockActivity.class);
        startActivity(intent);
    }
    

    【讨论】:

    • 我尝试使用this。用更多信息编辑了问题。
    • BaseActivity 中的 this 将引用调用该方法的实际活动。它不是指 BaseActivity
    • 只有正在运行的活动才有上下文。 BaseActivity 没有运行,就像类 Activity 一样。如果您使用this,它是否有效? Activity 的行为不受其启动位置的影响。
    • 我也是这么想的,但是当我使用this时它会抛出相同的NPE。
    • 我认为是新启动的活动导致了异常。如果应用在 Activity 的 onCreate Android 中崩溃,只需说“无法启动 Activity”,就好像之前的 Activity 导致了它一样。
    【解决方案3】:

    如果这不是一个选项,因为您要覆盖一个函数,您应该使用 MainActivity.this(当 MainActivity 是您的活动的名称时)

    MainActivity.this.startActivity(MainActivity.this, ...);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-20
      • 1970-01-01
      相关资源
      最近更新 更多