2014-02-21 41 views
16

某些第三方庫在活動生命週期中使用鉤子以正確工作 - 例如Facebook SDK(https://developers.facebook.com/docs/android/login-with-facebook/)。Mortar +流與第三方庫掛鉤到活動生命週期

我遇到了一些麻煩,弄清楚如何與單活動流+砂漿安裝乾淨地調和這個模型。例如,如果我想使用Facebook登錄作爲登錄流(w/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

在https:/ /github.com/square/mortar/issues/97#issuecomment-50798195我們討論瞭如果活動範圍結束時活動正在運行,PauseAndResumePresenter如何調用註冊者的onPause方法是一個好主意。我還沒有更新代碼來做到這一點。 – rjrjr

9

因此,我一直在將個人應用程序移植到流程和迫擊炮上進行評估,以供企業使用。我還沒有遇到過我必須擁有整個活動生命週期的場景,但是按照當前版本的流程(v0.4)&迫擊炮(v0.7),這是我認爲你必須要做的事情創造性地爲自己解決。我已經認識到這是我自己的一個潛在問題,並且已經考慮瞭如何克服它:

我還想指出,我沒有實際使用過Facebook SDK。你將不得不爲自己選擇最好的方法。

  1. 您可以從每個活動生命週期事件的活動中發佈事件。你基本上使用RXJava的Obserbles來提到這種方法。如果你真的很想使用RXJava,你可以使用PublishSubject這個,但我可能會去從你可以訂閱EventBus簡單的事件。這可能是最簡單的方法。
  2. 根據Facebook SDK的工作原理,您也可以在活動中注入Facebook SDK組件,然後從中初始化它。然後將Facebook SDK組件注入到您的視圖中以便使用。畢竟Flow和Mortar的整個系統都深深地融入了依賴注入?這種方法也很簡單,但取決於Facebook SDK的工作原理,它可能不是最好的選擇。如果你確實走了這條路,你需要聽取這篇文章底部的警告。
  3. 這給我帶來了我最後的想法。當他們需要訪問其子視圖/演示者中的活動ActionBar時,Square有類似的問題。他們通過他們所謂的ActionBarOwner.java在他們的示例應用程序中暴露了對ActionBar的訪問。然後他們實現ActionBarOwner接口,並在DemoActivity.java中給出自己的一個實例。如果你研究他們如何實現這一點並通過注入來分享它,你可以創建一個類似的類。 AcivityLifecycleOwner或其他(名稱需要工作),並且您可以從演示者訂閱回調。如果你決定走這條路,而且不小心,你可以很容易地結束內存泄漏。任何時候你都會訂閱任何事件(我建議你在主持人中訂閱),你還需要確保你在onDestroy方法中取消訂閱。我已經創建了一個未經測試的簡短示例,說明了我對此解決方案的含義。

無論您使用哪種方法,您都可能需要確保您的onCreate和onDestroy方法實際上來自您的演示者,而不是來自活動的確切事件。如果你只在單個視圖上使用sdk,那麼活動的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,因爲您的演示者無法訪問這些事件,因爲它們不會被創建,或者它們已經被銷燬。您需要使用Load和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

我覺得這第三次的方法可能是最合理的 - 它的排序的動作條案件的逆 - 而不是有被很多人寫到一個視圖中,這真的更多一個週期寫入到一個潛在的一大堆的聽衆。 我將試驗這一點 - 但看起來好像你可以用FlowOwner足夠聰明(參見mortar中的示例代碼)來免費獲得onCreate的東西。那麼這裏唯一缺少的就是onActivityForResult。 – secureboot

+0

這似乎是做事情的最佳方式,但onLoad/onResume/onStart之間的相互作用是超級,超級尷尬 - 有時你會得到一個onResume,有時是一個onLoad,沒有onResume等等。 – secureboot