理解Android Architecture Components系列(一)

Android Architecture Components是谷歌在Google I/O 2017發(fā)布一套幫助開發(fā)者解決Android架構(gòu)設(shè)計的方案。里面包含了兩大塊內(nèi)容:

  1. 生命周期相關(guān)的Lifecycle-aware Components
  2. 數(shù)據(jù)庫解決方案Room

在接下的文章里,將會分析Android Architecture Components的各個組件的功能和用法,以及這些組件背后的設(shè)計思想的簡單分析。這系列文章的分享目標(biāo)是對Android系統(tǒng)熟悉或者已經(jīng)有些開發(fā)經(jīng)驗的人,如果有看到暫時不熟悉的類如: ViewModelLiveData等時可以先按文章的邏輯讀下去,關(guān)于這些類的詳細信息,留在以后的文章中討論。首先建立起對Android Architecture Components的整體認識,然后再對細節(jié)各個擊破。這一系列文章主要還是對Google官方文檔Android Architecture Components的翻譯和加入一點不成熟的主觀理解,英文好的童鞋可以直接跳到官方文檔,講得更加清楚詳細。

總體概覽

Google官方對Android Architecture Components的定義是:

A new collection of libraries that help you design robust, testable, and maintainable apps.

即這是一個幫助構(gòu)建穩(wěn)定,易于測試和易于維護的App架構(gòu)的庫。

實際開發(fā)中經(jīng)常遇到的一個問題是:App 的架構(gòu)應(yīng)該怎樣設(shè)計。從最早的MVC,到現(xiàn)在MVP、MVVM,架構(gòu)設(shè)計花樣百出,可苦了功力賞淺的開發(fā)者。Google在I/O大會里也提到,開發(fā)者經(jīng)常向Google提出一個問題:關(guān)于構(gòu)建一個穩(wěn)定的Android App架構(gòu)設(shè)計有沒有一個推薦的方案或者廣大開發(fā)者喜聞樂見的Demo?

Google:emmmm....沒有,不存在的。

這世上本沒有問題,問的次數(shù)多了,也就成了問題。

終于Google坐不住了,祭出了神器Android Architecture Component。內(nèi)心OS:不要說我谷爹不照顧你們,看好了,我要出招了。

開發(fā)過程中經(jīng)常遇到的問題

以開發(fā)一個圖片分享的應(yīng)用為例。從選擇照片到裁剪到最后分享照片,流程要經(jīng)過好幾個步驟。這些步驟會很容易被來電或者其他App的事件打斷(如收到微信),并且隨著系統(tǒng)的運行,App的Activity等系統(tǒng)組件可能被操作系統(tǒng)回收。這就意味著不能在系統(tǒng)組件(例如Activity)中保存App 的數(shù)據(jù)和狀態(tài)并且這些系統(tǒng)組件間也不能出現(xiàn)相互依賴的狀況。
說到這里你可能會想:不對啊,不是有onSaveInstance()這些方法來保存數(shù)據(jù)嘛,怎么就不行了呢?你不讓我在這里保存數(shù)據(jù),那我在哪里存?這里先按下不表,原因在以后的文章里有解釋。讓我們先來看看關(guān)于架構(gòu)設(shè)計的一些基本原則。

設(shè)計App架構(gòu)的基本原則

  • Soc原則Separation of concerns 分離關(guān)注點原則,簡而言之就是要模塊化,低耦合。從Android的角度來看就是不要將任何和操作UI以及和調(diào)用系統(tǒng)組件的無關(guān)的代碼放到Activity或者Fragment中,因為系統(tǒng)可能因為內(nèi)存緊張等原因隨時殺掉Activity或者Fragment。應(yīng)該將這部分代碼看成是App和系統(tǒng)交互的中間層,像膠水一樣,鏈接App和Android系統(tǒng)。這么說有點累贅了,簡單的例子就是MVP。
  • Model驅(qū)動UI,最好是用持久化的數(shù)據(jù)的Model(即保存到本地的數(shù)據(jù))。為什么是持久化的數(shù)據(jù)呢?主要有一下兩點:
  1. 由于數(shù)據(jù)是持久化存儲的,所以當(dāng)系統(tǒng)因為各種原因回收資源時,并不會造成數(shù)據(jù)的丟失。
  2. 在網(wǎng)絡(luò)條件不穩(wěn)定或者網(wǎng)絡(luò)無法連接的情況下,App仍然可以工作。
    那么Model是什么呢?如果熟悉MVP的話,很容易理解Model的概念,即MVP中的M。它是一個獨立于Activity/Framgment用于保存數(shù)據(jù)的類。因為它是獨立于系統(tǒng)組件的,因此,不受系統(tǒng)生命周期的的影響。

由此就會產(chǎn)生兩個問題:

  • 如何做到Soc
  • 如何便捷、高效的持久化數(shù)據(jù)

推薦的系統(tǒng)架構(gòu)

好了,講了半天的理論真是枯燥的要死,讓我們舉個栗子吧。

以編寫一個個人信息頁面為例,由UserProfileFragment.java和對應(yīng)的布局文件user_profile_layout.xml組成。
那么為了讓這個頁面能夠展示個人信息,我們需要兩個數(shù)據(jù):

  • The User ID,用戶的ID,用于標(biāo)識唯一的用戶。給Fragment傳遞參數(shù)的方法最好方法是使用fragment arguments,因為如果系統(tǒng)在一些情況下直接殺死了App對應(yīng)的進程,那么通過這種方法傳遞的參數(shù)就會被保存,并在App下次恢復(fù)啟動的時候被讀取到。
  • The User object,用戶個人信息對應(yīng)的POJO類。即包含用戶如用戶名,年齡等信息的類。

和之前做法不同的是我們還要創(chuàng)建一個基于ViewModel的類UserProfileViewModel用于保存用戶信息,而不是直接在Fragment中保存,道理已經(jīng)在前面闡述過。什么是ViewModel?這里簡單的做一個概念上的介紹:

A ViewModel provides the data for a specific UI component, such as a fragment or activity, and handles the communication with the business part of data handling, such as calling other components to load the data or forwarding user modifications. The ViewModel does not know about the View and is not affected by configuration changes such as recreating an activity due to rotation.

即:ViewModel是為特定的UI(例如Activity/Fragment)提供數(shù)據(jù)的類,同時它也承擔(dān)和數(shù)據(jù)相關(guān)的業(yè)務(wù)邏輯處理功能:比如根據(jù)uid請求網(wǎng)絡(luò)獲取完整的用戶信息,或者將用戶修改的頭像上傳到服務(wù)器。因為ViewModel是獨立于View(例如Activity/Fragment)這一層的,所以并不會被View層的事件影響,比如Activity被回收或者屏幕旋轉(zhuǎn)等并不會造成ViewModel的改變。

好像啰里八嗦的講了一堆還是不太明白,讓我們來理一理現(xiàn)在都有涉及到哪些東西。現(xiàn)在已經(jīng)有三個文件了:

  • user_profile.xml: 個人信息的布局文件
  • UserProfileFragment.java:承載布局文件,顯示用戶信息的Fragment
  • UserProfileViewModel.java:為UserProfileFragment提供數(shù)據(jù),并且根據(jù)用戶在Fragment上的操作提供相應(yīng)業(yè)務(wù)邏輯的類

emmm......還是不太明白呀,show me the code!!!(簡單起見,user_profile.xml布局文件大家自行腦補吧)

先從最不熟悉的UserProfileViewModel開始(這里面省略了如何獲取和處理用戶信息的業(yè)務(wù)邏輯,詳細的獲取用戶信息的邏輯稍后的文章會有介紹),代碼如下:

public class UserProfileViewModel extends ViewModel {
    private String userId;
    private User user;

    //初始化傳遞uid進來
    public void init(String userId) {
        this.userId = userId;
    }
    //提供完整的用戶信息
    public User getUser() {
        return user;
    }
}

UserProfileFragment:

public class UserProfileFragment extends LifecycleFragment {
    private static final String UID_KEY = "uid";
    private UserProfileViewModel viewModel;

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //通過Arguments獲取uid
        String userId = getArguments().getString(UID_KEY);
        //創(chuàng)建ViewModel,不必太在意ViewModel的創(chuàng)建形式,這個之后會做詳細的分析。現(xiàn)在只需要明白是在哪里生成的就行。
        viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
        viewModel.init(userId);
    }

    @Override
    public View onCreateView(LayoutInflater inflater,
                @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.user_profile, container, false);
    }
}

注意:這里面UserProfileFragment繼承的是LifecycleFragment而不是Fragment,這是由于目前 Architecture Components 處于Alpha階段。等LifeCycle API穩(wěn)定之后,Android Support Library里的Fragment將會實現(xiàn)LifecycleOwner接口。(至于什么是LifecycleOwner,這里暫不做介紹,大概的意思是和感知組件生命周期相關(guān)。)截止到寫這篇文章為止,support V4中的Fragment已經(jīng)實現(xiàn)了LifecycleOwner,LifecycleFragment處于Deprecated狀態(tài),所以可以直接繼承V4 包中的Fragment即可。

好了,現(xiàn)在已經(jīng)熟悉了三塊代碼了,那么我們怎么將他們聯(lián)系在一起呢?換句話說,如果我們在UserProfileViewModel獲取到了用戶信息,那么我們怎么通知Fragment去顯示相應(yīng)的字段呢?(寫個接口?不行的!這么高級的事情不能這么干的)這時候LiveData就上場了。那么什么是LiveData呢?這里仍然是做一個簡單的概念上的介紹:

LiveData is an observable data holder. It lets the components in your app observe LiveData objects for changes without creating explicit and rigid dependency paths between them. LiveData also respects the lifecycle state of your app components (activities, fragments, services) and does the right thing to prevent object leaking so that your app does not consume more memory.

即:LiveData是一個包含可以被觀察的數(shù)據(jù)載體。這么說又有點不好理解了,其實他就是基于觀察者模式去做的。當(dāng)LiveData的數(shù)據(jù)發(fā)生變化時,所有對這個LiveData變化感興趣的類都會收到變化的更新。并且他們之間并沒有類似于接口回調(diào)這樣的明確的依賴關(guān)系。LiveData還能夠感知組件(例如activities, fragments, services)的生命周期,防止內(nèi)存泄漏。其實這和RxJava非常相似,但是LiveData能夠在組件生命周期結(jié)束后自動阻斷數(shù)據(jù)流的傳播,防止產(chǎn)生空指針等意外。這個RxJava是不同的。

那么說的這么好,這個LiveData怎么用呢?其實非常簡單,就是把UserProfileViewModelgetUser()方法的返回類型從User改成LiveData<User>就好,這樣Fragment就能在User發(fā)生改變的時候及時被通知,LiveData更厲害的功能是,當(dāng)對應(yīng)的Fragment生命周期結(jié)束時,自動釋放持有的引用。具體代碼如下:

public class UserProfileViewModel extends ViewModel {
    ...
    //private User user;
    private LiveData<User> user;
    public LiveData<User> getUser() {
        return user;
    }
}

對應(yīng)的Fragment中代碼:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    viewModel.getUser().observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                //update UI
            }
        });
}

這樣一旦UserProfileViewModel中的user發(fā)生改變,那么Fragment中就在onChanged()會收到相應(yīng)的改變,并根據(jù)改變更新相應(yīng)的UI。

如果對比之前類似RxJava這樣的基于觀察者模式的庫時,就會發(fā)現(xiàn)這里并沒有在Fragment的onStop()方法中對UserProfileViewModel解注冊觀察者。因為在LiveData中這是沒有必要的,因為LiveData可以感知Fragment的生命周期狀態(tài),在收到onStart()開始后調(diào)用通知回調(diào),并且在onStop()后不再調(diào)用回調(diào)通知。當(dāng)然在收到Fragment的onDestroy()方法后還會自動的移除觀察者。

從上面的代碼可以看出我們并沒有對 Configuration Changes(例如用戶旋轉(zhuǎn)屏幕)做任何處理。 ViewModel會在收到Configuration Changes時自動保存數(shù)據(jù)。當(dāng)新的Fragment(Configuration Changes 后新生成的Fragment)進入生命周期時,它會收到之前的ViewModel實例,并且會收到ViewModel中保存的數(shù)據(jù)。從這里不難看出,ViewModel的生命周期要比Views長,所以不能在ViewModel中持有Views的引用,否則會造成內(nèi)存泄漏等問題。ViewModel詳細的生命周期稍后的文章會做詳細的分析。

對上面這段文字總結(jié)下就是,把數(shù)據(jù)放在ViewModel中就不再需要擔(dān)心生命周期的問題,而且對如果這個數(shù)據(jù)是以LiveData的形式被傳遞出去,那么任何改動都會被實時的傳遞給UI層。

這里有個疑問那就是如果Activity在onStop()后LiveData中數(shù)據(jù)發(fā)生改變,但是此時Activity對應(yīng)的ViewModel是不會回調(diào)數(shù)據(jù)改變的。那么如果這時Activty重新回到界面onResume()后,還會收到最新的數(shù)據(jù)嗎?答案是:會的。

舉個簡單的應(yīng)用就是修改頭像,我們會跳到修改頭像的Activity去修改頭像,在修改成功后返回個人中心頁面,這時候數(shù)據(jù)會自動刷新減少了很多繁雜的邏輯代碼。

好了,大概看到這里已經(jīng)對Android Architecture Components中LifeCycle部分有了初步的認識,大概知道了LifecyclesUserProfileFragment)、LiveDataLiveData<User>)、ViewModelUserProfileViewModel)在什么情況下使用,以及一些使用的優(yōu)點,這樣其實已經(jīng)久夠了。對于這樣一個框架,首先應(yīng)該建立起一個整體的認識,然后再去填充細節(jié)的用法和技巧。

相關(guān)文章:
理解Android Architecture Components系列(一)
理解Android Architecture Components系列(二)
理解Android Architecture Components系列之Lifecycle(三)
理解Android Architecture Components系列之LiveData(四)
理解Android Architecture Components系列之ViewModel(五)
理解Android Architecture Components系列之Room(六)
理解Android Architecture Components系列之Paging Library(七)
理解Android Architecture Components系列之WorkManager(八)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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