【问题标题】:OnClickListener fired after onPause?onPause 后触发 OnClickListener?
【发布时间】:2015-07-15 13:37:59
【问题描述】:

我正在工作的项目使用视图呈现器抽象。 这是所有主要类的简化版本。

抽象活动(连接 Presenter 实例,带 View)

public abstract class MvpActivity<Presenter extends MvpPresenter>
        extends ActionBarActivity {

  protected Presenter mPresenter;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mPresenter = getPresenterInstance();
  }

  @Override protected void onResume() {
    super.onResume();
    mPresenter.onResume(this);
  }

  @Override protected void onPause() {
    mPresenter.onPause();
    super.onPause();
  }
}

视图界面

public interface MyView {
  void redirect();
}

视图实现

public class MyActivity
        extends MvpActivity<MyPresenter>
        implements MyView {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_view);

    Button myButton = (Button)findViewById(R.id.my_button);

    myButton.setOnClickListener(v -> mPresenter.onButtonPressed());
  }

  @Override protected MyPresenter getPresenterInstance() {
    return new MyPresenter();
  }

  @Override void redirect(){
    startActivity(new Intent(this, MyOtherActivity.class));
  }

摘要演示者

public abstract class MvpPresenter<ViewType> {

  private ViewType mView;

  public void onResume(ViewType view) {
    mView = view;
  }

  public void onPause() {
    mView = null;
  }

  protected ViewType getView() {
    if (mView == null) {
      throw new IllegalStateException("Presenter view is null");
    }
    return mView;
  }
}

以及演示者实现

public class MyPresenter extends MvpPresenter<MyView> {

  @Override public void onResume(MyView myView){
    super.onResume(myView);
    Log.("MyPresenter", "Presenter resumed");
  }

  @Override public void onPause(){
    super.onPause()
    Log.("MyPresenter", "Presenter paused");
  }

  public void onButtonPressed(){
    getView().redirect();
  }
}

当从 MyPresenter.onButtonPressed() 方法调用时,由 getView().redirect(); 触发的 "IllegalStateException: Presenter view is null" 会出现此问题。

这对我来说没有任何意义,因为如果侦听器被触发,视图应该始终不为空。仅当执行仅从 MvpActivity.onPause() 调用的 MvpPresenter.onPause() 时,视图才设置为 null。发生这种情况后,我不希望收到任何点击事件,那么我在这里错过了什么?

遗憾的是,我无法通过手动测试应用程序来重现此问题。报告来自 Crashlytics。

注意:retrolambda 用于按钮点击监听器

2017 年 10 月 7 日更新

解决此问题的一些方法:

- https://developer.android.com/reference/android/view/View.html#cancelPendingInputEvents()

- https://github.com/JakeWharton/butterknife/blob/master/butterknife/src/main/java/butterknife/internal/DebouncingOnClickListener.java

【问题讨论】:

    标签: android android-activity android-actionbaractivity


    【解决方案1】:

    简短回答:不要那样做。

    不幸的是,您依赖的是未定义的事件顺序。 Activity 生命周期事件和 Window 事件是两个不同的东西,尽管它们通常密切相关。当活动因任何原因暂停时,您将获得 onPause() 。但是在视图的窗口失去焦点之前,视图触摸事件不会被解除。

    活动在其窗口失去焦点时暂停是很常见的——例如,当屏幕被锁定或启动另一个活动时。但正如您所看到的,您可以在没有焦点更改的情况下暂停,也可以在没有暂停的情况下更改焦点。即使这两个事件同时发生,当 onPause() 已被调用但窗口输入处理程序仍处于活动状态时,时间窗口也会很窄。

    与任何未定义的行为一样,您看到的实际结果会因操作系统版本和硬件类型而异。

    如果您需要确保在 onPause 后不会收到 View 消息,您应该在 onPause 中解开您的处理程序。

    【讨论】:

    • 感谢您的回答。我不知道这种行为。您知道我可以阅读的任何文档吗?我什至不知道如何搜索它。此外,当您说“取消挂钩处理程序”时,您指的是侦听器以及与 UI 元素或活动可能具有的任何 android.os.Handler 相关的所有内容?再次感谢
    • 可能为时已晚,不能做任何好事,但是......“解开你的处理程序”指的是侦听器,而不是处理程序对象本身。我用 Java 以外的语言编写代码的时间太长了。 ;-) 至于文档...好吧,没有任何关于事件顺序的文档的事实意味着该顺序没有记录,因此不能依赖。 :-D
    • 一方面,我发现“暂停”的应用程序仍然可以为用户输入生成事件是绝对残酷的——说订单未记录并不意味着基本常识不再适用。这是另一个让我讨厌为 Android 编程的 Android 主义。
    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 1970-01-01
    • 2018-07-21
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多