Mortar + Flow с сторонними библиотеками, подключенными к жизненному циклу активности

В некоторых сторонних библиотеках для правильной работы задействованы крючки в жизненном цикле активности – например, SDK для Facebook ( https://developers.facebook.com/docs/android/login-with-facebook/ ).

У меня возникли проблемы с выяснением того, как примирить эту модель с помощью однонаправленного потока + раствора.

Например, если я хочу использовать вход в Facebook как часть потока входа (w / FlowView / FlowOwner), но не в противном случае в действии, то какой самый умный способ снять это, если вам нужны перехваты для этого конкретного потока в onCreate, OnResume, onPause, onDestroy, onSaveInstanceState, onActivityResult и т. Д.?

Не сразу понятно, что такое самый чистый путь – создать наблюдаемый для каждого этапа жизненного цикла и подписаться на него потоком? Похоже, этот путь быстро переходит к тому же жизненному циклу Android, если вы не будете осторожны. Есть ли способ лучше?

Мне нравится единственная модель деятельности, и я бы очень хотел, чтобы все, что было возможно, управлялось потоком / раствором, а не деятельностью. Или я думаю об этом так, что в корне затрудняет его, чем должно быть?

Solutions Collecting From Web of "Mortar + Flow с сторонними библиотеками, подключенными к жизненному циклу активности"

Нам пока не нужно было начинать и останавливаться, но у меня есть несколько точек, которые полагаются на паузу и возобновление. Мы используем 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(); } } } 

Поэтому я портировал личное приложение на поток и раствор, чтобы оценить его для бизнеса. Я не столкнулся с сценарием, в котором я должен был иметь весь жизненный цикл активности, но поскольку ситуация стоит на текущей версии потока (v0.4) & mortar (v0.7), это то, что я думаю, вам придется Творчески решать для себя. Я осознал это как потенциальную проблему для себя и подумал о том, как ее преодолеть:

Я также хотел бы отметить, что я фактически не использовал SDK для Facebook. Вам нужно будет выбрать лучший метод для себя.

  1. Вы можете публиковать события из активности для каждого события жизненного цикла. Вы, по сути, упоминали этот подход, используя Observables RXJava . Если вы действительно хотели использовать RXJava, для этого вы можете использовать PublishSubject , но я бы, вероятно, пошел с простыми событиями из EventBus, на который вы могли подписаться. Это, наверное, самый простой подход.
  2. Вы также можете, в зависимости от того, как работает SDK в Facebook, возможно, внедрить компонент SDK Facebook в свою деятельность и оттуда инициализировать его. Затем добавьте компонент Facebook SDK в свой вид, который будет использоваться. Вся система Flow и Mortar полностью интегрирована в инъекцию зависимостей в конце концов? Этот подход также довольно прост, но в зависимости от того, как работает SDK в Facebook, вероятно, это не самый лучший вариант. Если вы пошли по этому маршруту, вам нужно будет прислушаться к моему предупреждению внизу этого сообщения.
  3. Это подводит нас к моей последней идее. У Square была аналогичная проблема, когда они нуждались в доступе к ActionBar Activity в своих суб-представлениях / презентаторах. Они открыли доступ к ActionBar в своем примерном приложении через то, что они назвали ActionBarOwner.java . Затем они реализуют интерфейс ActionBarOwner и дают экземпляр самого себя в DemoActivity.java . Если вы изучите, как они реализовали это и разделили его с помощью инъекции, вы можете создать аналогичный класс. AcivityLifecycleOwner или что-то (имя нуждается в работе), и вы можете подписаться на обратные вызовы от него. Если вы решите спуститься по этому маршруту и ​​не будете осторожны, вы можете легко потерять память. Каждый раз, когда вы подписываетесь на какие-либо события (я бы рекомендовал вам подписаться в презентаторе), вам нужно будет также отказаться от подписки в методе onDestroy. Я создал короткий непроверенный образец того, что я имею в виду для этого решения ниже.

Независимо от того, какой подход вы используете, вам, вероятно, нужно будет убедиться, что ваши методы onCreate и onDestroy действительно приходят от вашего ведущего, а не точные события из активности. Если вы используете только sdk в одном представлении, функция onCreate этого действия вызывается задолго до того, как вероятно создается экземпляр вашего представления, и onDestroy для Activity будет вызван после того, как ваше представление будет уничтожено. Мне кажется, что презентация 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(); } }