砂浆+流与挂接到活动周期第三方库砂浆、第三方、周期、挂接

2023-09-06 13:09:50 作者:上帝不该造就孤单的人

一些第三方库使用挂接到活动周期才能正常工作 - 例如,Facebook的SDK(https://developers.facebook.com/docs/android/login-with-facebook/).

Some third party libraries use hooks into the activity lifecycle to work correctly - for instance, the Facebook SDK (https://developers.facebook.com/docs/android/login-with-facebook/).

我有一些麻烦搞清楚如何与单活动流程+砂浆建立清晰调和这种模式。

I'm having some trouble figuring out how to reconcile this model cleanly with a single-activity flow+mortar setup.

举例来说,如果我想使用Facebook登录作为登录流程的一部分(W / FlowView / FlowOwner),而不是其他活动中,什么是最聪明的办法,如果你需要挂钩的特定流拉动这一关在的onCreate,onResume,在onPause,的onDestroy,的onSaveInstanceState,onActivityResult等?

For instance, if I want to use Facebook login as part of a login Flow (w/ FlowView/FlowOwner), but not otherwise in the activity, what's the smartest way to pull this off if you need hooks for that particular flow in onCreate, onResume, onPause, onDestroy, onSaveInstanceState, onActivityResult, etc?

这不是显而易见什么最干净的路径是 - 创建一个可观察到的每个生命周期阶段的活动,并订阅流呢?好像这条道路迅速转予同样采用Android的生命周期,如果你不小心。有没有更好的办法?

It's not immediately obvious what the cleanest path is - create an observable for each lifecycle activity stage and subscribe the flow to it? Seems like that path quickly devolves to the same Android lifecycle if you're not careful. Is there a better way?

我爱的单一活动模式,我真的很想把一切都按流量/砂浆不活动管理,如果可能的话。还是我想这个的方式,从根本上变得更困难比它应该是什么?

I love the single activity model, and I'd really like to keep everything managed by flow/mortar and not activities, if possible. Or am I thinking about this in a way that is fundamentally making it more difficult than it should be?

推荐答案

我们还没有必要启动和停止,到目前为止,但确实有依赖暂停和恢复的几个景点。我们用一个活动presenter按照你的建议,但要避免任何一种普遍的超类的。相反,它暴露了兴趣presenters可以选择加入服务。这种转播需要的就是加入了onEnterScope(范围)的方法。这里的code。

We haven't had a need for start and stop so far, but do have a few spots that rely on pause and resume. We use an ActivityPresenter as you suggest, but avoid any kind of universal superclass. Instead it exposes a service that interested presenters can opt in to. This kind of hookup need is why the onEnterScope(Scope) method was added. Here's the code.

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

First, have the activity implement this interface:

/**
 * 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();
}

和有它注入presenter,并做出相应的电话:

And have it inject the presenter and make the appropriate calls:

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();
}

现在有关方面可以注入一个注册商接口选择在暂停和继续呼叫,没有继承任何东西。

Now interested parties can inject a registrar interface to opt-in to pause and resume calls, without subclassing anything.

/**
 * 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();
}

在客户端presenter实现这个接口:

Have the client presenter implement this interface:

/**
 * <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();
}

和挂钩事情是这样的。 (注意,没有必要取消注册。)

And hook things up like this. (Note that there is no need to unregister.)

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() {
}

这里的presenter该活动注入,使这一切的工作。

Here's the presenter that the activity injects to make it all work.

/**
 * 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();
    }
  }
}