Mortar + Flow与第三方库挂钩到活动生命周期

sec*_*oot 16 android mortar square-flow

一些第三方库使用钩子进入活动生命周期才能正常工作 - 例如,Facebook SDK(https://developers.facebook.com/docs/android/login-with-facebook/).

我在弄清楚如何使用单活动流+砂浆设置干净地协调这个模型时遇到了一些麻烦.

例如,如果我想使用Facebook登录作为登录流程的一部分(带有FlowView/FlowOwner),而不是活动中的其他内容,如果您需要在onCreate中为该特定流程挂钩,那么最明智的方法是将其解除, onResume,onPause,onDestroy,onSaveInstanceState,onActivityResult等?

最明确的路径是什么并不是很明显 - 为每个生命周期活动阶段创建一个observable并订阅它的流程?如果您不小心,似乎该路径会快速转移到相同的Android生命周期.有没有更好的办法?

我喜欢单一的活动模型,如果可能的话,我真的希望保持一切由流/迫击炮管理,而不是活动.或者我是否以一种从根本上使其变得更加困难的方式来思考这个问题?

rjr*_*rjr 13

到目前为止,我们还没有开始和停止的需要,但确实有一些依赖暂停和恢复的点.我们按照您的建议使用ActivityPresenter,但避免使用任何类型的通用超类.相反,它会公开一个感兴趣的演示者可以选择加入的服务.这种连接需求是添加onEnterScope(Scope)方法的原因.这是代码.

首先,让活动实现这个接口:

/**
 * Implemented by {@link android.app.Activity} instances whose pause / resume state
 * is to be shared. The activity must call {@link PauseAndResumePresenter#activityPaused()}
 * and {@link PauseAndResumePresenter#activityResumed()} at the obvious times.
 */
public interface PauseAndResumeActivity {
  boolean isRunning();

  MortarScope getMortarScope();
}
Run Code Online (Sandbox Code Playgroud)

并让它注入演示者并进行适当的调用:

private boolean resumed;
@Inject PauseAndResumePresenter pauseNarcPresenter;

@Override protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  pauseNarcPresenter.takeView(this);
}

@Override public boolean isRunning() {
  return resumed;
}

@Override protected void onResume() {
  super.onResume();
  resumed = true;
  pauseNarcPresenter.activityResumed();
}

@Override protected void onPause() {
  resumed = false;
  super.onPause();
  pauseNarcPresenter.activityPaused();
}

@Override protected void onDestroy() {
  pauseNarcPresenter.dropView(this);
  super.onDestroy();
}
Run Code Online (Sandbox Code Playgroud)

现在感兴趣的各方可以注入一个注册商界面来选择暂停和恢复呼叫,而无需任何子类化.

/**
 * Provides means to listen for {@link android.app.Activity#onPause()} and {@link
 * android.app.Activity#onResume()}.
 */
public interface PauseAndResumeRegistrar {
  /**
   * <p>Registers a {@link PausesAndResumes} client for the duration of the given {@link
   * MortarScope}. This method is debounced, redundant calls are safe.
   *
   * <p>Calls {@link PausesAndResumes#onResume()} immediately if the host {@link
   * android.app.Activity} is currently running.
   */
  void register(MortarScope scope, PausesAndResumes listener);

  /** Returns {@code true} if called between resume and pause. {@code false} otherwise. */
  boolean isRunning();
}
Run Code Online (Sandbox Code Playgroud)

让客户端演示者实现此接口:

/**
 * <p>Implemented by objects that need to know when the {@link android.app.Activity} pauses
 * and resumes. Sign up for service via {@link PauseAndResumeRegistrar#register(PausesAndResumes)}.
 *
 * <p>Registered objects will also be subscribed to the {@link com.squareup.otto.OttoBus}
 * only while the activity is running.
 */
public interface PausesAndResumes {
  void onResume();

  void onPause();
}
Run Code Online (Sandbox Code Playgroud)

并且像这样挂钩.(请注意,无需取消注册.)

private final PauseAndResumeRegistrar pauseAndResumeRegistrar;

@Inject
public Presenter(PauseAndResumeRegistrar pauseAndResumeRegistrar) {
  this.pauseAndResumeRegistrar = pauseAndResumeRegistrar;
}

@Override protected void onEnterScope(MortarScope scope) {
  pauseAndResumeRegistrar.register(scope, this);
}

@Override public void onResume() {
}

@Override public void onPause() {
}
Run Code Online (Sandbox Code Playgroud)

这是活动注入的演示者,使其全部工作.

/**
 * Presenter to be registered by the {@link PauseAndResumeActivity}.
 */
public class PauseAndResumePresenter extends Presenter<PauseAndResumeActivity>
    implements PauseAndResumeRegistrar {

  private final Set<Registration> registrations = new HashSet<>();

  PauseAndResumePresenter() {
  }

  @Override protected MortarScope extractScope(PauseAndResumeActivity view) {
    return view.getMortarScope();
  }

  @Override public void onExitScope() {
    registrations.clear();
  }

  @Override public void register(MortarScope scope, PausesAndResumes listener) {
    Registration registration = new Registration(listener);
    scope.register(registration);

    boolean added = registrations.add(registration);
    if (added && isRunning()) {
      listener.onResume();
    }
  }

  @Override public boolean isRunning() {
    return getView() != null && getView().isRunning();
  }

  public void activityPaused() {
    for (Registration registration : registrations) {
      registration.registrant.onPause();
    }
  }

  public void activityResumed() {
    for (Registration registration : registrations) {
      registration.registrant.onResume();
    }
  }

  private class Registration implements Scoped {
    final PausesAndResumes registrant;

    private Registration(PausesAndResumes registrant) {
      this.registrant = registrant;
    }

    @Override public void onEnterScope(MortarScope scope) {
    }

    @Override public void onExitScope() {
      registrations.remove(this);
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Registration that = (Registration) o;

      return registrant.equals(that.registrant);
    }

    @Override
    public int hashCode() {
      return registrant.hashCode();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 在https://github.com/square/mortar/issues/97#issuecomment-50798195中,我们讨论了如果在范围结束时actiivty正在运行,PauseAndResumePresenter如何调用注册人的onPause方法是个好主意.我还没有更新代码来做到这一点. (2认同)

spi*_*ce7 9

因此,我一直在将个人应用程序移植到流量和迫击炮上,以便为企业使用进行评估.我还没有遇到过一个我不想拥有整个活动生命周期的场景,但是就当前版本的flow(v0.4)和迫击炮(v0.7)而言,这是我认为你必须要做的事情.创造性地为自己解决.我已经认识到这是一个潜在的问题,并且已经考虑过如何克服它:

我还想指出,我实际上并没有使用Facebook SDK.你必须为自己选择最好的方法.

  1. 您可以为活动中的每个活动生命周期事件发布事件.您基本上使用RXJava的Observables 提到了这种方法.如果你真的真的想使用RXJava,你可以使用PublishSubject,但我可能会使用你可以订阅的EventBus中的简单事件.这可能是最简单的方法.
  2. 您也可以根据Facebook SDK的工作方式,在活动中注入Facebook SDK组件,然后从中初始化它.然后还将Facebook SDK组件注入您要使用的视图中.Flow和Mortar的整个系统毕竟深深融入了依赖注入?这种方法也相当简单,但根据Facebook SDK的工作方式,它可能不是最好的选择.如果你确实走了这条路,你需要在这篇文章的底部留意我的警告.
  3. 这让我们想到了最后一个想法.当Square需要访问其子视图/演示者中的Activity的ActionBar时,Square也有类似的问题.他们通过他们称为ActionBarOwner.java的东西在他们的示例应用程序中公开了对ActionBar的访问.然后,他们实现ActionBarOwner接口,并在DemoActivity.java中提供自己的实例.如果你研究他们如何实现这个并通过注入分享它,你可以创建一个类似的类.AcivityLifecycleOwner或其他东西(名称需要工作),您可以从演示者订阅回调.如果您决定沿着这条路走下去,并且不小心,您很容易就会出现内存泄漏.任何时候你订阅任何事件(我建议你在演示者中订阅),你需要确保你也取消订阅onDestroy方法.我已经为下面的解决方案创建了一个未经测试的简短样本.

无论您使用哪种方法,您都可能需要确保您的onCreate和onDestroy方法实际上来自您的演示者,而不是来自活动的确切事件.如果您只在单个视图上使用sdk,则可能在视图实例化之前很久就调用了活动的onCreate,并且在视图被销毁后将调用Activity的onDestroy.我认为演示者的onLoad和onDestroy应该足够了,但我还没有测试过.

祝你好运!

解决方案#3的未经测试的代码示例:

您的所有演示者都可以扩展此类而不是ViewPresenter,然后覆盖您想要事件的每个方法,就像在活动中一样:

public abstract class ActivityLifecycleViewPresenter<V extends View> extends ViewPresenter<V>
    implements ActivityLifecycleListener {

  @Inject ActivityLifecycleOwner mActivityLifecycleOwner;

  @Override protected void onLoad(Bundle savedInstanceState) {
    super.onLoad(savedInstanceState);
    mActivityLifecycleOwner.register(this);
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    mActivityLifecycleOwner.unregister(this);
  }

  @Override public void onActivityResume() {
  }

  @Override public void onActivityPause() {
  }

  @Override public void onActivityStart() {
  }

  @Override public void onActivityStop() {
  }

}
Run Code Online (Sandbox Code Playgroud)

活动生命周期所有者将被注入活动,然后连接到相应的事件.我故意不包括onCreate和onDestroy,因为你的演示者将无法访问这些事件,因为它们不会被创建或者它们已经被销毁.您需要使用演示者onLoad和onDestroy方法代替这些方法.也有可能不会调用其中一些其他事件.

public class ActivityLifecycleOwner implements ActivityLifecycleListener {

  private List<ActivityLifecycleListener> mRegisteredListeners
      = new ArrayList<ActivityLifecycleListener>();

  public void register(ActivityLifecycleListener listener) {
    mRegisteredListeners.add(listener);
  }

  public void unregister(ActivityLifecycleListener listener) {
    mRegisteredListeners.remove(listener);
  }

  @Override public void onActivityResume() {
    for (ActivityLifecycleListener c : mRegisteredListeners) {
      c.onActivityResume();
    }
  }

  @Override public void onActivityPause() {
    for (ActivityLifecycleListener c : mRegisteredListeners) {
      c.onActivityPause();
    }
  }

  @Override public void onActivityStart() {
    for (ActivityLifecycleListener c : mRegisteredListeners) {
      c.onActivityStart();
    }
  }

  @Override public void onActivityStop() {
    for (ActivityLifecycleListener c : mRegisteredListeners) {
      c.onActivityStop();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,您需要将生命周期所有者挂钩到活动:

public class ActivityLifecycleExample extends MortarActivity {

  @Inject ActivityLifecycleOwner mActivityLifecycleOwner;

  @Override protected void onResume() {
    super.onResume();
    mActivityLifecycleOwner.onActivityResume();
  }

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

  @Override protected void onStart() {
    super.onStart();
    mActivityLifecycleOwner.onActivityStart();
  }

  @Override protected void onStop() {
    super.onStart();
    mActivityLifecycleOwner.onActivityStop();
  }

}
Run Code Online (Sandbox Code Playgroud)