android mortar square-flow

android - Mortar+Flow con bibliotecas de terceros conectadas al ciclo de vida de la actividad



square-flow (2)

Así que he estado portando una aplicación personal al flujo y el mortero para evaluarla para uso de las empresas. No he encontrado un escenario en el que TENÍA que tener el ciclo de vida completo de la actividad todavía, pero como están las cosas con la versión actual de flow (v0.4) & mortar (v0.7), creo que tendrá que hacerlo. Resuelve creativamente por ti mismo. He reconocido esto como un problema potencial para mí y he pensado en cómo superarlo:

También me gustaría señalar que en realidad no he usado el SDK de Facebook. Tendrás que elegir el mejor método para ti.

  1. Puede publicar eventos de la actividad para cada evento del ciclo de vida de la actividad. Esencialmente mencionaste este enfoque usando los Observables de RXJava . Si realmente quisieras usar RXJava, podrías usar un PublishSubject para esto, pero probablemente iría con eventos simples de un EventBus puedas suscribirte. Este es probablemente el enfoque más fácil.
  2. También podría, dependiendo de cómo funciona el SDK de Facebook, posiblemente inyectar el componente SDK de Facebook en la actividad, y desde allí inicializarlo. A continuación, también inyecte el componente SDK de Facebook en su vista para ser utilizado. ¿El sistema completo de Flow and Mortar está profundamente integrado en la inyección de dependencia después de todo? Este enfoque también es bastante simple, pero dependiendo de cómo funciona el SDK de Facebook, probablemente no sea la mejor opción. Si siguieras esta ruta, deberías prestar atención a mi advertencia al final de esta publicación.
  3. Esto nos lleva a mi última idea. Square tuvo un problema similar cuando necesitaban acceso a la Barra de Acción de una Actividad en sus sub-vistas / presentadores. Expusieron el acceso a ActionBar en su aplicación de ejemplo a través de algo que llamaron ActionBarOwner.java . Luego implementan la interfaz ActionBarOwner y dan una instancia de sí mismo en DemoActivity.java . Si estudia cómo lo implementaron y lo comparte mediante inyección, podría crear una clase similar. AcivityLifecycleOwner o algo (el nombre debe funcionar), y puede suscribirse a devoluciones de llamada desde un presentador. Si decide seguir esta ruta y no tiene cuidado, puede terminar fácilmente con una pérdida de memoria. En cualquier momento en que se suscriba a cualquiera de los eventos (le recomiendo que se suscriba en el presentador), deberá asegurarse de cancelar también la suscripción al método onDestroy. He creado una breve muestra no probada de lo que quiero decir con esta solución a continuación.

Independientemente del método que utilice, es probable que deba asegurarse de que sus métodos onCreate y onDestroy provengan de su presentador y no los eventos exactos de la actividad. Si solo está utilizando el SDK en una sola vista, se ha llamado a onCreate de la actividad mucho antes de que se haya creado una instancia de su vista, y se llamará a OnDestroy para la Actividad después de que se destruya su vista. El presentador onLoad y onDestroy deberían bastar, creo, sin embargo, no he probado esto.

¡La mejor de las suertes!

Ejemplo de código no probado para la solución # 3:

Todos sus presentadores podrían extender esta clase en lugar de ViewPresenter y luego anular cada método para el que deseaban los eventos como lo haría en una actividad:

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

El propietario del ciclo de vida de la actividad se inyectaría en la actividad y luego se conectaría a los eventos correspondientes. A propósito no incluí onCreate y onDestroy, ya que el presentador no podría tener acceso a esos eventos ya que no se crearían o ya serían destruidos. Necesitaría usar los presentadores onLoad y onDestroy en lugar de esos. También es posible que algunos de estos otros eventos no sean llamados.

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

Ahora necesita conectar el propietario del ciclo de vida a la actividad:

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

Algunas bibliotecas de terceros utilizan enlaces en el ciclo de vida de la actividad para funcionar correctamente, por ejemplo, el SDK de Facebook ( https://developers.facebook.com/docs/android/login-with-facebook/ ).

Estoy teniendo algunos problemas para averiguar cómo reconciliar este modelo de manera limpia con una configuración de mortero y flujo de una sola actividad.

Por ejemplo, si quiero usar el inicio de sesión de Facebook como parte de un flujo de inicio de sesión (con FlowView / FlowOwner), pero no de otra manera en la actividad, ¿cuál es la forma más inteligente de lograrlo si necesita enlaces para ese flujo en particular en onCreate? onResume, onPause, onDestroy, onSaveInstanceState, onActivityResult, etc?

No es inmediatamente obvio cuál es la ruta más limpia: ¿cree un observable para cada etapa de actividad del ciclo de vida y suscríbase al flujo? Parece que esa ruta se convierte rápidamente en el mismo ciclo de vida de Android si no tienes cuidado. ¿Hay alguna manera mejor?

Me encanta el modelo de actividad única, y realmente me gustaría mantener todo gestionado por flow / mortar y no actividades, si es posible. ¿O estoy pensando en esto de una manera que fundamentalmente lo hace más difícil de lo que debería ser?


No hemos tenido la necesidad de comenzar y parar hasta ahora, pero tenemos algunos puntos que dependen de la pausa y la reanudación. Usamos un ActivityPresenter como usted sugiere, pero evitamos cualquier clase de superclase universal. En su lugar, expone un servicio al que los presentadores interesados ​​pueden optar. Este tipo de necesidad de conexión es la razón por la cual se agregó el método onEnterScope (Scope). Aquí está el código.

Primero, haga que la actividad implemente esta interfaz:

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

Y haz que inyecte al presentador y haga las llamadas apropiadas:

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

Ahora las partes interesadas pueden inyectar una interfaz de registrador para optar por hacer una pausa y reanudar las llamadas, sin subclasificar nada.

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

Haga que el presentador del cliente implemente esta interfaz:

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

Y enganchar cosas así. (Tenga en cuenta que no es necesario cancelar el registro).

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

Aquí está el presentador que la actividad inyecta para que todo funcione.

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