在onPause之后触发OnClickListener?

Rob*_*ill 6 android android-activity android-actionbaractivity

我正在使用的项目使用视图展示器抽象.这是所有主要类的简化版本.

抽象活动(Wire 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

视图界面

public interface MyView {
  void redirect();
}
Run Code Online (Sandbox Code Playgroud)

视图实现

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));
  }
Run Code Online (Sandbox Code Playgroud)

抽象的主持人

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

和演示者实现

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

当从方法调用时"IllegalStateException: Presenter view is null"触发该问题.getView().redirect();MyPresenter.onButtonPressed()

这对我没有任何意义,因为如果侦听器被触发,视图应始终不为null.如果MvpPresenter.onPause()仅执行调用,则视图仅设置为null MvpActivity.onPause().发生这种情况后,我不希望收到任何点击事件,所以我在这里缺少什么?

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

注意:retrolambda用于按钮单击侦听器

更新10/07/2017

解决此问题的一些方法:

- 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

Ian*_*wis 11

简短的回答:不要这样做.

不幸的是,您依赖的是未定义的事件顺序.活动生命周期事件和窗口事件是两个不同的事情,即使它们通常密切相关.当活动因任何原因暂停时,您将获得onPause().但View视图窗口失去焦点后,View触摸事件不会被取消.

当一个活动窗口失去焦点时,例如当屏幕被锁定或启动另一个活动时,它就会非常常见.但正如您所见,您可以在没有焦点变化的情况下暂停,并且可以不停顿地进行焦点更改.即使两个事件一起发生,当调用onPause()但窗口输入处理程序仍处于活动状态时,会有一个很窄的时间窗口.

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

如果您需要确保在onPause之后没有收到View消息,则应该在onPause中取消挂钩处理程序.

  • 举个例子,我发现"暂停"的应用程序仍然可以为用户输入生成事件 - 说订单没有记录并不意味着基本的常识不再适用,这绝对是残酷的.这是另一个使我厌恶Android编程的Android主义. (4认同)
  • 谢谢你的回答.我不知道这种行为.你知道我能读到的任何文件吗?我甚至不知道如何搜索它.另外,当你说'解开你的处理程序'时,你指的是监听器以及与UI元素或活动可能具有的任何android.os.Handler相关的所有东西?再次感谢 (2认同)