16

一些第三方库在活动生命周期中使用挂钩来正常工作 - 例如,Facebook SDK ( https://developers.facebook.com/docs/android/login-with-facebook/ )。

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

例如,如果我想使用 Facebook 登录作为登录流(w/FlowView/FlowOwner)的一部分,而不是在活动中,如果你需要在 onCreate 中为特定流设置钩子,那么最聪明的方法是什么? onResume、onPause、onDestroy、onSaveInstanceState、onActivityResult 等?

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

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

4

2 回答 2

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

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

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

现在,感兴趣的各方可以注入一个注册商接口来选择暂停和恢复呼叫,而无需继承任何内容。

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

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

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

像这样把事情联系起来。(请注意,无需取消注册。)

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 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();
    }
  }
}
于 2014-07-31T17:13:14.237 回答
9

因此,我一直在将个人应用程序移植到 flow 和砂浆上,以评估它以供企业使用。我还没有遇到过我必须拥有整个活动生命周期的情况,但是就当前版本的 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 时,他们也遇到了类似的问题。他们通过他们称为ActionBarOwner.java的东西在他们的示例应用程序中公开了对 ActionBar 的访问。然后他们实现 ActionBarOwner 接口并在DemoActivity.java中给出自身的一个实例. 如果您研究他们如何实现这一点并通过注入共享它,您可以创建一个类似的类。AcivityLifecycleOwner 或其他东西(名称需要工作),您可以订阅来自演示者的回调。如果您决定走这条路,并且不小心,您很容易导致内存泄漏。任何时候您订阅任何事件(我建议您在演示者中订阅),您都需要确保在 onDestroy 方法中取消订阅。我在下面创建了一个简短的未经测试的示例,说明我对这个解决方案的含义。

无论您使用哪种方法,您都可能需要确保您的 onCreate 和 onDestroy 方法实际上来自您的演示者,而不是来自活动的确切事件。如果您仅在单个视图上使用 sdk,则可能在您的视图实例化之前很久就调用了 Activity 的 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() {
  }

}

将被注入到活动中然后连接到相应事件的活动生命周期所有者。我故意不包括 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();
    }
  }
}

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

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

}
于 2014-02-22T19:43:33.343 回答