Android協(xié)調(diào)員:解決你還不知道你存在的問題

原文地址:https://hackernoon.com/coordinators-solving-a-problem-you-didnt-even-know-you-had-e86623f15ebf#.nm0brlclo

Square公司有一個大多人還不知道的庫-Coordinators,不是一個很具有描述性的名字瞳氓,甚至在Github上也沒有太多的信息梧躺。

簡單的生命周期,無論是安卓還是其他

簡單的示例代碼:

Class ExampleCoordinator extends Coordinator{
  @Override public void attach(View view){
    //附加監(jiān)聽器哼蛆,加載狀態(tài),其他操作
  }
  
  @Override public void detach(View view){
    //解綁監(jiān)聽器圈驼,保存狀態(tài)等
  }
}

你可以在任何View中綁定一個Coordinator

Coordinators.bind(view,coordinatorProvider);

Activity

每個人都知道Activity人芽,他在App中代表單個界面,而且是你定義的特定意圖的入口點绩脆。

如果要開啟一個新的Activity萤厅,可以使用Intent橄抹,就像activity.startActivity(activity,OtherActivity.class)

他們也不能嵌套,雖然之前可以使用LocalActivityManager和ActivityGroup實現(xiàn)嵌套惕味,但是在Api11之后已經(jīng)過時了楼誓。

Activities通過使用setContentView()來顯示View,通常假定他只能顯示一種特定的布局,如果需要一個不同的界面名挥,你就需要使用一個新的Activity代替疟羹。

Fragments

完整的Fragment和Activity的生命周期
完整的Fragment和Activity的生命周期

Fragment是屏幕上的一個片段,技術(shù)上顯示一個自定義的視圖組禀倔,被Activity中的FragmentManager所管理榄融,F(xiàn)ragment與FragmentController所關(guān)聯(lián),而FragmentController與宿主Activity相關(guān)聯(lián)救湖。

他們的設(shè)計主要是為了創(chuàng)建‘子屏幕功能’愧杯,繼承所有重要的生命周期回調(diào),比如onCreate()鞋既、onActivityResult()力九、onPermissionResult()、甚至是更重要的onSaveInstanceState(Bundle bundle).

他也有自己的生命周期回調(diào)方法邑闺,比如onAttach()跌前、onCreateView()onActivityCreated()陡舅、onDestroyView()抵乓、onDetach().

你可以通過getSupportFragmentManager().beginTransaction().add(R.id.container,new MyFragment()).commit();來創(chuàng)建一個Fragment。

大多數(shù)時候蹭沛,你只需要訪問你綁定的Activity臂寝,和onCreateView()onDestroyView().

如果有復(fù)雜的情況,你可以在一個Fragment中嵌套一個Fragment摊灭,就可以通過Fragment的getChildFragmentManager(),這有時會導(dǎo)致混亂咆贬,無論你使用getFragmentManager或者是getChildFragmentManager().

有人可能會問,我們在我們Activity的子視圖中真的需要這些生命周期函數(shù)嗎帚呼。

自定義Viewgroups

你知道Activity和Fragment中有什么共同點嗎掏缎,他們顯示Viewgroups,都是以xml格式聲明的煤杀,并且他們還有一些不錯的回調(diào)眷蜈。

public class MyCustomViewGroup extends RelativeLayout {
    public MyCustomViewGroup(Context context) {
        super(context);
        init(context);
    }

    public MyCustomViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MyCustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(21)
    public MyCustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        if(!isInEditMode()) {
           // ...
        }
    }  

    @OnClick(R.id.button)
    public void doSomething() {
        // ...
    }
  
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ButterKnife.bind(this);
    }
  
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // pretty much `onStart()`
    }

    @Override
    protected void onDetachedFromWindow() {
        // pretty much `onStop()`
        super.onDetachedFromWindow();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<fully.qualified.MyCustomViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Go to next"/> <!-- centerInParent works because -->
                                    <!-- custom viewgroup is also RelativeLayout -->
</fully.qualified.MyCustomViewGroup>

我們可以擴展我們的視圖組(DrawableLayout、RelativeLayout沈自、FrameLayout酌儒,LinearLayout等等),并選擇我們自己定義的視圖組作為需要繼承的來源枯途。

代替getSupportFragmentManager().beginTransaction().blah().commit()我們可以輕松的實現(xiàn)inflate忌怎、addView籍滴、removeView

自定義View創(chuàng)建通過inflation榴啸,他有四個構(gòu)造方法孽惰,需要通過繼承Android特定的類來工作。

Coordinators

為了脫離自定義視圖組的限制鸥印,我們可以將所有的試圖控制器邏輯移出Viewgroup本身勋功,并將其作為標記附加進去。

下面是他的工作原理:

public final class Coordinators{
  private Coordinators(){
  }
  public static void bind(View view,CoordinatorProvider provider){
    View.OnAttachStateListener binding = new Binding(coordinator,view);
    view.addOnAttachStateChangeListener(binding);
  }
}
```

```
final class Binding implements View.OnAttachStateChangeListener{
  Binding(Coordinator coordinator,View view){
    this.coordinator = coordinator;
    this.view = view;
  }

  @Override public void onViewAttachedToWindow(@NonNull View v){
    coordinator.attach(view);
    view.setTag(R.id.coordinator,null);
  }
  
  @Override public void onViewDetachedFromWindow(@NonNull View view){
    coordinator.detach(view);
    view.setTag(R.id.coordinator,null);
  }
}
```
你可以創(chuàng)建一個協(xié)調(diào)器库说,他可以是任何一個POJO類狂鞋,并可以附加到任何自定義視圖組,而無需擴展他璃弄。
使用協(xié)調(diào)器要销,你會收到一個```attach(View)```和```detach(View)```回調(diào),代替原來Viewgroup的```onAttachToWindow()```和```onDetachedFromWindow()```回調(diào)夏块。

然后將協(xié)調(diào)器實例作為標記存儲,然后纤掸,協(xié)調(diào)器可以獲得```Coordinator coordinator = (Coordinator)getTag(R.id.coordinator)```,這當然是由Coordinators類提供的脐供。

這有什么好處?現(xiàn)在我們的類是一個POJO借跪,我們可以直接使用Dagger注入這個類政己,而不必指定一個void inject(MyCustomViewGroup group)方法。

```
public class TasksCoordinator
        extends Coordinator {
    @Inject // <-- !
    public TasksCoordinator() {
    }

    @Inject
    TasksPresenter tasksPresenter;

    @OnClick(R.id.noTasksAdd)
    void openAddNewTask() {
        tasksPresenter.openAddNewTask();
    }

    @BindView(R.id.noTasks)
    View noTasksView;

    @BindView(R.id.noTasksIcon)
    ImageView noTaskIcon;

    // ...
  
    @Override
    protected final void attach(View view) {
        this.unbinder = ButterKnife.bind(this, view);
        presenter.takeCoordinator(this);
    }

    @Override
    protected final void detach(View view) {
        presenter.dropCoordinator(this);
        unbinder.unbind();
    }
}
```
有了這些掏愁,我們獲得了以下好處:
我們不需要繼承一個視圖組和定義4個構(gòu)造函數(shù)歇由,以便定義我們自己的“視圖控制器”邏輯。
我們可以直接從Dagger直接注入我們新創(chuàng)建的協(xié)調(diào)器果港,而不需要void inject(MyCoordinator協(xié)調(diào)器)
我們不再需要一個Context來創(chuàng)建一個“視圖”的實例沦泌,我們可以只創(chuàng)建一個Coordinator來代替。

### 結(jié)論


就個人而言辛掠,我喜歡協(xié)調(diào)員所采取的方向谢谦。我不完全確定onAttachedToWindow()和onDetachedFromWindow()回調(diào)本身是否真的足夠,但在我們自己的類中定義一個attach(View)和detach(View)方法萝衩,然后將它與一個標簽相關(guān)聯(lián)并不難回挽。

作為回報,我們收到一個POJO視圖控制器猩谊,可以直接通過Dagger創(chuàng)建和注入千劈,而無需創(chuàng)建4個構(gòu)造函數(shù),并在自己的XML文件中固定自定義視圖組牌捷。

如果導(dǎo)航邏輯與活動/片段分離墙牌,并且移動到Presenter層涡驮,則新的協(xié)調(diào)器方法尤其適用。它使得更清潔的代碼 - 應(yīng)用程序狀態(tài)轉(zhuǎn)換不是視圖應(yīng)該管理的憔古,畢竟遮怜。

這個是我的測試代碼地址: [https://github.com/Zhuinden/simple-stack/tree/master/simple-stack-example-mvp](https://github.com/Zhuinden/simple-stack/tree/master/simple-stack-example-mvp).
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鸿市,隨后出現(xiàn)的幾起案子锯梁,更是在濱河造成了極大的恐慌,老刑警劉巖焰情,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陌凳,死亡現(xiàn)場離奇詭異,居然都是意外死亡内舟,警方通過查閱死者的電腦和手機合敦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來验游,“玉大人充岛,你說我怎么就攤上這事「酰” “怎么了崔梗?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垒在。 經(jīng)常有香客問我蒜魄,道長,這世上最難降的妖魔是什么场躯? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任谈为,我火速辦了婚禮,結(jié)果婚禮上踢关,老公的妹妹穿的比我還像新娘伞鲫。我一直安慰自己,他們只是感情好耘成,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布榔昔。 她就那樣靜靜地躺著,像睡著了一般瘪菌。 火紅的嫁衣襯著肌膚如雪撒会。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天师妙,我揣著相機與錄音诵肛,去河邊找鬼。 笑死,一個胖子當著我的面吹牛怔檩,可吹牛的內(nèi)容都是我干的褪秀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼薛训,長吁一口氣:“原來是場噩夢啊……” “哼媒吗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乙埃,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤闸英,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后介袜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甫何,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年遇伞,在試婚紗的時候發(fā)現(xiàn)自己被綠了辙喂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸠珠,死狀恐怖巍耗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渐排,我是刑警寧澤芍锦,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站飞盆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏次乓。R本人自食惡果不足惜吓歇,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望票腰。 院中可真熱鬧城看,春花似錦、人聲如沸杏慰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缘滥。三九已至轰胁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間朝扼,已是汗流浹背赃阀。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留擎颖,地道東北人榛斯。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓观游,卻偏偏與公主長得像,于是被迫代替她去往敵國和親驮俗。 傳聞我的和親對象是個殘疾皇子懂缕,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容