Android Jetpack 架構組件

Android Jetpack 架構組件

image

架構原則

關注分離點

要遵循的最重要的原則是分離關注點。一種常見的錯誤是在一個 Activity 或 Fragment 中編寫所有代碼。這些基于界面的類應僅包含處理界面和操作系統交互的邏輯。應盡可能使這些類保持精簡,這樣可以避免許多與生命周期相關的問題。

請注意,您并不擁有 Activity 和 Fragment 的實現,這些只是表示 Android 操作系統與應用之間關系的粘合類。操作系統可能會根據用戶交互或因內存不足等系統條件隨時銷毀它們。為了提供令人滿意的用戶體驗和更易于管理的應用維護體驗,最好盡量減少對它們的依賴。

通過模型驅動界面

另一個重要原則是您應該通過模型驅動界面,最好是持久性模型。模型是負責處理應用數據的組件。它們獨立于應用中的 View 對象和應用組件,因此不受應用的生命周期以及相關關注點的影響。

持久性是理想之選,原因如下:

  • 如果 Android 操作系統銷毀應用以釋放資源,用戶不會丟失數據。
  • 當網絡連接不穩定或不可用時,應用會繼續工作。
    應用所基于的模型類應明確定義數據管理職責,這樣將使應用更可測試且更一致。

以上內容在摘自Google官方文檔:應用架構指南

本文主要內容

Lifecycle : 管理您的 Activity 和 Fragment 生命周期

ViewModel : 以注重生命周期的方式管理界面相關的數據

LiveData : 在底層數據更改時通知視圖

Lifecycle

生命周期管理(Lifecycles)組件,幫助開發者創建 “可感知生命周期的” 組件,讓其自己管理自己的生命周期,從而減少內存泄露和崩潰的可能性。
android.arch.lifecycle組件提供一系列接口讓開發者很容易自定義可監聽生命周期的組件——根據生命周期組件的狀態改變自定義組件的行為!

相關類簡介

  • Lifecycle

抽象Android組件(Activity/Fragment)的生命周期,可通過addObserver(LifecycleObserver observer)監聽其狀態

  • LifecycleObserver

抽象生命周期觀察者接口,內部無抽象方法,實際通過注解方法的方式接收生命周期變更事件

  • LifecycleOwner

持有生命周期對象,包含抽象方法Lifecycle getLifecycle()

  • LifecycleRegistry

Lifecycle具體實現類

  • ReportFragment

Activity生命周期變更時通過attach一個ReportFragment接收生命周期回調方法,然后在ReportFragment中調用LifecycleRegistry.handleLifecycleEvent(event),最終把事件傳遞給觀察Lifecycle的LifecycleObserver對象

自定義LifecycleObserver

繼承LifecycleObserver,通過注解自定義方法接收生命周期變更事件

注意這種方式被注解的方法最多可以有兩個參數,并且需準守以下規則:

  • 空參數 ()
  • 一個參數(LifecycleOwer ower)
  • 兩個參數(LifecycleOwer ower, Lifecycle.Event event),對應注解Lifecycle.Event.ON_ANY

例如給以下位置服務組件,添加響應生命周期組件事件

class LocationService implements LifecycleObserver{

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void start() {
        // do get location
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void stop(LifecycleOwer ower) {
        // do stop get location
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    public void onAny(LifecycleOwer ower, Lifecycle.Event event) {
        // do something
    }

    public void addListener(LocationListener listener) {
    }
}

創建Lifecycle對象,監聽Lifecycle

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;
    private LocationService locationService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
        
        locationService = new LocationService();
        getLifecycle().addObserver(locationService);
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecycleRegistry.markState(Lifecycle.State.STARTED);
    }
    
    @Override
    public void onResume() {
        super.onResume();
        lifecycleRegistry.markState(Lifecycle.State.RESUMED);
    }
    
    // ...省略其他生命周期方法...

    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}
如果使用java8 則繼承DefaultLifecycleObserver,并且添加依賴
 class TestObserver implements DefaultLifecycleObserver {
 *     Override
 *     public void onCreate(LifecycleOwner owner) {
 *         // your code
 *     }
 * }
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

Activity生命周期對應 Lifecycle 狀態

image

ViewModel

主要特點

  • 幫助UI Controller準備數據給View展示,將數據獲取從UI Controller分離出來
  • 在橫豎屏切換時數據不會被銷毀
  • ViewModel獨立于View和LifecycleOwner,便于測試
  • 可以在不同Fragment中共享數據

ViewModel的生命周期

image

使用Demo

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        
        // 在第一次調用onCreate()時創建ViewModel
        // 重復調用onCreate()會獲取到第一次創建的ViewModel
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

在同一個Activity中的Fragment之間共享ViewModel

有以下好處
  • 不用在Activity里協調Fragment之間的數據
  • Fragment不用關心其他Fragment跟ViewModel之間的關系
  • 每個Fragment都有自己的生命周期,不會受到其他Fragment的影響
public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

使用ViewModle替換Loaders

主要目的在于將數據獲取從UI Controller中分離出來,讓ViewModel持有數據(Model)

LiveData

是一種可觀察的數據存儲器。應用中的其他組件可以使用此存儲器來監控對象的更改,而無需在它們之間創建明確且嚴格的依賴路徑。LiveData 組件還遵循應用組件(如 Activity、Fragment 和 Service)的生命周期狀態,并包括清理邏輯以防止對象泄漏和過多的內存消耗。

LiveData的優勢

  • 典型的觀察者模式,保證UI展示跟數據同步
  • 不用擔心內存泄露,LiveData會在lifecycle生命周期結束時自動清理,不用手動remove觀察者(observeForever()除外,需要手動調用removeObserver()方法移除)
  • 不會因為Activity stoped而crash,LiveData會監聽Activity的狀態,在inactive狀態時不會給組件發送事件
  • 不用手動管理生命周期
  • 總是會刷新數據(比如Activity在后臺,這時數據更新了,當Activity回到前臺時會接收到數據更新事件)
  • 橫豎屏切換時也會觸發數據更新事件
  • 共享數據,你可以使用單例模式實現自己的LiveData,不同的組件可以監聽它達到數據共享的目的

使用方法

LiveData<T>是一個泛型類,通常結合ViewModel使用(ViewModel的目的是將數據和UI Controller組件分離),例如:

public class NameViewModel extends ViewModel {
    // Create a LiveData with a String
    private MutableLiveData<String> currentName;

    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }
}

通常在onCreate方法里添加觀察者

  • 不要在onResume()方法中重復添加
  • 再者可以保證數據及時被顯示出來
public class NameActivity extends AppCompatActivity {
    private NameViewModel model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(this).get(NameViewModel.class);
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };
        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.getCurrentName().observe(this, nameObserver);
    }
}

在調用observe()方法之后,且LiveData有設置初始化數據,如果Activity是active狀態,onChanged方法會立刻回調,或者當Activity狀態改變為active狀態時回調

使用LiveData來更新UI的好處

  • 防止Fragment或Activity過于臃腫,他們的職責是用來展示數據,但不持有數據
  • 將LiveData從UI Controller中分離,這樣可以使得在configuration變更時數據不被銷毀(所以一般將LiveData放在ViewModel對象里面使用,因為ViewModel獨立于UI Controller)

更新數據

// 主線程回調
public void postValue(T value)

// 當前線程回調
public void setValue(T value)

自定義LiveData

主要重載下面三個方法:

  • onActive() 當LiveData有一個觀察者處于active狀態時回調
  • onInactive() 當所有觀察者處于inactive狀態時回調
  • setValue(T value) 設置數據并給對應處于active狀態的觀察者發送通知
    例如下面獲取地理位置信息LocationLiveData
public class LocationLiveData extends LiveData<Location> {
    // 地理位置服務
    private LocationService locationService = new LocationService();

    public LocationLiveData() {
        locationService.addListener(new LocationListener() {
            public void onLocation(Location location) {
                setValue(location);
            }
        });
    }

    @Override
    protected void onActive() {
        // 當有活躍狀態觀察者時開啟地理位置獲取
        locationService.start();
    }

    @Override
    protected void onInactive() {
        // 當有沒有活躍狀態觀察者時開啟地理位置獲取
        locationService.stop();
    }
}

LiveData變換

public static <X, Y> LiveData<Y> map(LiveData<X> source, final Function<X, Y> mapFunction)

public static <X, Y> LiveData<Y> switchMap(LiveData<X> source, final Function<X, LiveData<Y>> switchMapFunction)

兩個方法差不多,只是map轉換函數返回具體數據對象,switchMap返回LiveData<T>

  • User --> userName
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

  • userId --> User
private LiveData<User> getUser(String id) {
  ...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

Lifecycle 和 LiveData 的區別

Lifecycle和LiveData都類似觀察者模式

  • Lifecycle具有生命周期的概念,而LiveData沒有生命周期,他是根據觀察者(LifecycleOwner)的生命周期狀態來決定要不要回調Observer.onChange()方法
  • Lifecycle添加觀察者是LifecycleObserver,他是一個空接口,實際回調是LifecycleObserver實現類的注解方法
  • LiveData會根據LifecycleOwner的生命周期為ON_DESTROY時自動移除Observer

參考文獻:

Guide to app architecture
Handling Lifecycles
ViewModel
LiveData
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379