2014-02-21 3 views
16

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

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

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

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

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

ответ

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

Теперь заинтересованные стороны могут придать регистратору интерфейс opt- чтобы приостановить и возобновить вызовы, без подклассификации.

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

Over in https://github.com/square/mortar/issues/97#issuecomment-50798195 мы обсудили, как было бы хорошей идеей для PauseAndResumePresenter вызывать метод onPause регистратора, если активация выполняется, когда их область действия заканчивается. Я еще не обновил код для этого. – rjrjr

9

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

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

  1. Вы можете публиковать события из активности для каждого события жизненного цикла. Вы, по сути, упоминали этот подход, используя RXJava 's Observables. Если вы действительно хотели использовать RXJava, для этого вы могли бы использовать PublishSubject, но я бы, вероятно, пошел с простыми событиями из EventBus, на которые вы могли подписаться. Это, наверное, самый простой подход.
  2. Вы также можете, в зависимости от того, как работает SDK Facebook, возможно, введет компонент Facebook SDK в свою деятельность и оттуда его инициализирует. Затем добавьте компонент 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(); 
    } 

} 
+0

Я думаю, что этот третий подход может быть наиболее разумным - это своего рода инверсия случае ActionBar - вместо того, чтобы там быть много людей, пишущих в один вид, это действительно еще один жизненный цикл записи в потенциально связкой слушателей. Я собираюсь поэкспериментировать с этим немного - но кажется, что вы можете быть достаточно умны с FlowOwner (см. Пример кода в минометном), чтобы бесплатно получить материал onCreate. Тогда здесь отсутствует только onActivityForResult. – secureboot

+0

Кажется, это лучший способ сделать что-то, но взаимодействие между onLoad/onResume/onStart супер, супер неудобно - иногда вы получаете onResume, иногда onLoad и no onResume и т. Д. – secureboot

Смежные вопросы