Android Jetpack架構組件Lifecycle+ViewModel+ LiveData

什么是Android Jetpack?

Android Jetpack是谷歌在2018年I/O開發者大會上推出的新一代組件、工具和架構指導,旨在加快開發者的 Android 應用開發速度。 ——官方介紹網站

Android Jetpack 組件是庫的集合,這些庫是為協同工作而構建的,不過也可以單獨采用,同時利用 Kotlin 語言功能幫助您提高工作效率。可全部使用,也可混合搭配!

Android Jetpack組件的優勢:

Jetpack推出的主要目的是為了能夠讓開發者更加快速、方便以及高質量的完成產品開發

  • 輕松管理應用程序的生命周期,后臺任務的管理,導航的處理等等
  • 利用Jetpack組件進行開發可以有效減少內存溢出、崩潰的概率,提升應用開發的質量

Jetpack組件主要分為四個方向:基礎,架構,行為和UI。詳情見下

Android Jetpack組件推薦的使用項目架構

上面架構組件的功能如下:

  • Activity和Fragment負責產品與用戶的交互
  • ViewModel作為數據的存儲和驅動
  • Resposity負責調度數據的獲取(Room儲存本地序列化的數據,Retrofit獲取遠程數據的數據)

ViewModel+ LiveData

ViewModel的優點:
  • 解決了運行中斷和界面重建時的數據保存問題 (橫豎屏切換,導致Activity銷毀并重新創建時,ViewMode仍然可以保留之前讀取到的數據不會因為Activity的銷毀而丟失,這樣我們無需額外再浪費資源去再次請求數據)
  • 配合LiveData實時獲取最新數據
  • 實現Activity中Fragment之間的數據交互(數據共享)
  • 數據和界面的分離,使數據驅動界面 (ViewModel類被設計為通過lifecycle感知的方式存儲和管理UI相關數據)
ViewModel的生命周期:
  • ViewModel對象的范圍是在獲取ViewModel時傳遞給ViewModelProvider的Lifecycle生命周期
  • ViewModel在內存中直到Activity銷毀或Fragment被移除
  • 系統首次調用活動對象的onCreate()方法時,通常會請求ViewModel
  • 系統可能會在整個活動的整個生命周期中多次調用onCreate(),例如當設備屏幕旋轉時
  • ViewModel從第一次請求ViewModel直到活動完成并銷毀時存在

ViewMode在其生命周期的范圍內會一直保存在內存中,所以橫豎屏切換 當切換手機橫豎屏后,Activity會destroy并重新onCreate來重構當前界面,生命周期再次重新觸發onCreate,但是ViewMode 并沒有重新執行獲取數據的操作。

由于 ViewModel 生命周期可能長與 activity 生命周期,所以為了避免內存泄漏 Google 禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用。
如果有些請求數據的情況必須用到Context,在繼承ViewMode的時候,可以改為繼承AndroidViewMode,這個類會返回一個帶有Context的構造函數。

ViewMode執行onCleared操作,這個是ViewMode的一個回調,表明當前Activity要徹底關閉,ViewMode需要做一些回收清理的操作


LiveData

優點:

  • 確保UI界面的數據狀態
  • 沒有內存泄漏,不會因為Activity的不可見導致Crash
  • 一個存放可被觀察的數據持有類,但與一般的被觀察者不同的是,它是有生命周期感知功能,解決了android開發者需要去手動處理生命周期的痛點。
  • 共享資源
 viewMode.getUserLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String users) {
                fragment2.setText("fragment2==\n" + users);
            }
        });

LiveData是一個observable數據持有類,LiveData是生命周期感知的,這意味著它跟隨其他應用程序組件(如activities, fragments, or services)的生命周期。這種感知能力確保LiveData只更新處于活躍生命周期狀態的應用程序組件
LiveData與一個Observer關聯,如果觀察者的生命周期處于STARTED或RESUMED狀態,則表示觀察者處于活動狀態。LiveData只通知活躍的觀察者做更新。注冊到LiveData對象中的不活躍的觀察者則得不到數據更新的通知。

注冊一個observer并與實現了LifecycleOwner接口的對象配對。這種關系允許當相應的Lifecycle對象的狀態改變為DESTROYED時,觀察者被移除

利用ViewMode(配合LiveData實時獲取最新數據)進行Fragment之間的數據交互
public class OneFragment extends BaseFragment {
    @BindView(R2.id.fragment2)
    TextView fragment2;
    @Override
    protected int initLayout() {
        return R.layout.fragment_detail;

    }
    @Override
    protected void initView(View view) {
        //綁定ViewMode的selected的值,當有更新時通知DetailFragment
        SharedViewModel viewMode = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        viewMode.getUserLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String users) {
                fragment2.setText("fragment2==\n" + users);
            }
        });


    }

}


public class TwoFragment extends BaseFragment {
    @BindView(R2.id.fragment1)
    TextView fragment1;


    SharedViewModel viewMode;

    @Override
    protected void initView(View view) {
        //注意:這里ViewModelProviders.of(getActivity())這里的參數需要是Activity,而不能是Fragment,否則收不到監聽
        viewMode = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

        fragment1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //當點擊某一個item的時候,更新viewmode中的selected的值
                viewMode.select("fragment1點擊后,更新viewmode中的值,fragment2 里面的數據同步更新");
            }
        });
    }

    @Override
    protected void initData() {
        viewMode.getUserLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String users) {
                Log.w("TAG", "====" + users);
            }
        });

    }

    @Override
    protected int initLayout() {
        return R.layout.fragment_master;
    }
}





public class SharedViewModel extends AndroidViewModel {
    //userLiveData保存的是被選中的item的狀態或者數據
    private MutableLiveData<String> userLiveData;


    public MutableLiveData<String> getUserLiveData() {
        if (userLiveData == null) {
            Log.w("TAG", "SharedViewModel-getUserLiveData");
            userLiveData = new MutableLiveData<>();

        }
        return userLiveData;
    }

    //主要通過masterFragment進行調用交互,用來更新selected中的值
    public void select(String item) {
        userLiveData.setValue(item);
    }


    public SharedViewModel(@NonNull Application application) {
        super(application);
    }

    /**
     * 這里可以執行一些資源釋放、數據清理的操作
     * ViewMode會執行onCleared操作,這個是ViewMode的一個回調,
     * 表明當前Activity要徹底關閉,ViewMode需要做一些回收清理的操作,如下代碼:
     */
    @Override
    protected void onCleared() {
        super.onCleared();

    }
}


上述代碼的邏輯很簡單,OneFragment與TwoFragment并不直接進行交互,而是各自與ViewMode進行交互,OneFragment用來更新維護ViewMode中的數據,TwoFragment可以收到來自ViewMode中數據更新的通知。這樣便達到了兩個frangment之間的數據通信。

LifeCycles原理

Lifecycles是生命周期管理組件 另一組件的生命周期狀態(隨著Activity和Fragment)的變化而執行動作,support 26 以上的兼容包中的AppCompatActivity與Fragment中默認已實現了LifeCycleOwner接口,保證了LiveData及ViewModel具備了生命周期感知與內存緩存的能力。

場景使用:在平時的開發過程中,我們難免有些邏輯的執行是和UI的生命周期相結合的,需要在特定的生命周期中執行相應的方法,我們平時做的可能就是在View中的每個周期調用Present中獲取數據的方法,然后在調用View的回調接口更新UI,但現在使用Lifecycles可以使用注解和觀察的模式自動調用Observe中定義好的方法。

     //誰觀察生命周期  就注冊誰  兩個角色定義好后,需要讓他們之間建立聯系
        //獲取Lifecycle
        getLifecycle().addObserver(new LocationListener());


public class LocationListener implements LifecycleObserver {
    private static final String TAG = "TAG";

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onActivityCreate(LifecycleOwner owner) {
        Log.w(TAG, "onActivityCreate");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onActivityDestroy(LifecycleOwner owner) {
        Log.w(TAG, "onActivityDestroy");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onActivityPause(LifecycleOwner owner) {
        Log.w(TAG, "onActivityPause");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onActivityResume(LifecycleOwner owner) {
        Log.w(TAG, "onActivityResume");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onActivityStart(LifecycleOwner owner) {
        Log.w(TAG, "onActivityStart");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onActivityStop(LifecycleOwner owner) {
        Log.w(TAG, "onActivityStop");
    }

}


Lifecycle 原理 如何感知 activity 或 fragment 生命周期

1、activity 和 fragment 已經實現了 LifecycleOwner
2、出現了一個LifecycleRegistry,是 Lifecycle 的一個實現類。通過markState方法在onSaveInstanceState把 Lifecycle 狀態標記為Lifecycle.State.CREATED。
3、onCreate 方法里有個ReportFragment,
4、利用 fragment 的特性,綁定了一個 fragment 然后在其生命周期dispatch()方法中調用了LifecycleRegistry的handleLifecycleEvent,此方法便是通知觀察者的地方。Lifecycle.Event,判斷執行事件后下一個到達的狀態,然后使用moveToState()中修改活動的生命周期
5、通知觀察者addObserver 后,把observer維護到ObserverWithState然后裝到 map 里。
然后通過handleLifecycleEvent方法最終遍歷map 通知 observer。

偽代碼
public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner{

 private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

 @CallSuper
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
        super.onSaveInstanceState(outState);
    }

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSavedStateRegistryController.performRestore(savedInstanceState);
        ReportFragment.injectIfNeededIn(this);
        if (mContentLayoutId != 0) {
            setContentView(mContentLayoutId);
        }
    }


  public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

}

ViewModel 原理

    getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });


ViewModel 和 onSaveInstaceState方法區別在于:ViewModel只能保存因為配置更改導致重建的數據,但是它能保存大量和復雜的數據;onSaveInstaceState能保存配置更改導致重建和資源限制導致重建的數據,但是它只能保存少量簡單的數據。ViewModel使用SavedStateHandle能夠保存資源限制導致重建的數據。

根據傳入的Activity獲取、創建、添加并以鍵值對保存Fragment,從VIewStore的Map中或Factory的create()中獲取ViewMode
1、獲取ViewProvider:
2、獲取ViewModelStore:由前面的源碼可以知道創建ViewProvider時傳入兩個參數:ViewModelStore 和 Factory;顯然從名字就可以看出他們的作用,Factory負責創建,ViewModelStore負責存儲

ViewModelStore內部維護者一個Map集合保存者ViewModel對象的鍵值對

ViewModel的生命周期要比Activity長一點。因為在ComponentActivity 實現了的getViewModelStore ,ViewModelStore在Activity重建前后能保持同一個對象就是通過NonConfigurationInstances實現的。

ActivityThread 里面performLaunchActivity方法里面,啟動Activity 調用了Activity的attach方法,在這個方法,將已有的NonConfigurationInstances賦值給了新的Activity對象。所以Activity 獲取到的ViewMode是同一個,
這樣NonConfigurationInstances能保證ViewModelStore在Activity重建前后是同一個對象,同時也知道為啥ViewModel的生命周期比Activity的生命周期要長一點

LiveData 原理,如何做到生命周期感知:

涉及到LifecycleOwner屬于另一個架構組件 lifecycle,lifecycle原理上面已經講述

偽代碼
   //LiveData數據可以再通過observe方法進行數據回調的返回,如上代碼中的onChanged回調。
    //  從Livedata添加觀察者的方法 observe 開始:
        viewMode.getUsers().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String users) {
                viewmode_text.setText("viewMode獲取到的數據--" + users);
            }
        });

 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }


第一個參數為LifecycleOwner用于提供當前的生命周期狀態,DESTROYED的時候不做任何操作。
第二個為觀察者observer,首先把observer包裝成了LifecycleBoundObserver,然后把LifecycleBoundObserver維護到mObservers里
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();
mObservers是一個LinkedList結構的容器 通過putIfAbsent方法判斷,容器中此觀察者是不是已經存在,如果存在且LifecycleOwner不同的話則拋異常,LifecycleOwner相同則 return 不重復添加。
LifecycleBoundObserver實現了LifecycleObserver,為lifecycle 的觀察者,通過上文的 observe方法添加到了lifecycle觀察中,

接下來主要看LifecycleBoundObserver
通過此類持有 Livedata 的觀察者observer,當 生命周期發生變化時 會回調onStateChanged方法,然后 Livedata 的觀察者在onStateChanged中執行相應的邏輯。

  @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }
    //接著執行activeStateChanged
     void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }

通過巧妙的設計實現了:
1、狀態沒有變化,什么也不做。
2、變為活(active)就是調用onActive(),非活(inactive)就調用onInactive().
3、另外,變為活的話就調用dispatchingValue方法,此方法為回調觀察者的方法

事件的通知
LiveData通過 setValue 或 postValue 方法去改變持有的數據,并通知觀察者,最終都是調用dispatchingValue()方法:

 void dispatchingValue(@Nullable ObserverWrapper initiator) {
   ........................................
//遍歷之前注冊的觀察者
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

 private void considerNotify(ObserverWrapper observer) {
   ................................
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
     //開始通知了
        observer.mObserver.onChanged((T) mData);
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容