Android Jetpack是谷歌在2018年I/O開發者大會上推出的新一代組件、工具和架構指導,旨在加快開發者的 Android 應用開發速度。 ——[官方介紹網站]1java
Google爸爸老司機在2018年的I/O大會上發車了,推出了新一代的開發組件、工具和架構指導,並打包在一塊兒,取名爲「Jetpack」,顧名思義,Jetpack直譯過來的意思就是噴氣揹包,Google也使用了一個很形象的穿戴噴氣揹包的android機器人來作形象代言,以下圖。android
經過圖片能夠很明確的感覺到谷歌爸爸的意圖:帶你飛!。簡單的概況,Jetpack推出的主要目的是爲了可以讓開發者更加快速、方便以及高質量的完成產品開發,Jetpack的主要做用能夠概況爲如下幾點:程序員
Jetpack組件主要分爲四個方向:基礎,架構,行爲和UI。詳情見下表:數組
基礎 | 架構 | 行爲 | UI |
---|---|---|---|
AppCompat | Data Binding | Download Manager | Animation & transitions |
Android KTX | Lifecycles | Media & playback | Auto |
Multidex | LiveData | Notifications | Emoji |
Test | Navigation | Permissions | Fragment |
- | Paging | Sharing | Layout |
- | Room | Slices | Palette |
- | ViewMode | - | TV |
- | WorkManager | - | Wear OS by Google |
如上表格,有些內容是很早就有的,有些是最近推出的,Jetpack將這些內容打包在一塊兒,共同組成Jetpack,本系列主要對Jetpack新推出的架構這一塊內容進行敘述。架構
ViewMode主要用來管理和存儲與UI綁定的數據,同時ViewMode還與UI的生命週期相關聯。例如ViewMode的一大特點在於:與ViewMode相關聯的UI界面若是由於某些緣由須要從新繪製建立時,例如橫豎屏切換,致使Activity銷燬並從新建立時,ViewMode仍然能夠保留以前讀取到的數據不會由於Activity的銷燬而丟失,這樣咱們無需額外再浪費資源去再次請求數據。app
有時候Activity或者Fragment的生命週期的變更是不受控制的,常常會由於各類事件的調度致使界面須要從新建立。當Activity須要從新建立的時候,以前與之綁定的數據也會丟失。好比你的應用界面經過list來展現用戶名單,若是界面從新建立時,以前獲取的用戶名單數據須要再次從新獲取,可是用戶名單數據相對來講是一個比較穩定的靜態數據,再次獲取一次數據顯然浪費了系統資源。異步
有的同窗看到這裏後可能會有疑問:不對呀,Android中不是提供了onSaveInstanceState()方法來保存數據嗎,而後從新執行OnCreate的時候,經過Bundle參數來再次獲取保存的數據?這種方式也是可行的,可是有個限制,即這種方式只能保存數據量較小的狀況,而且數據被序列化才行。若是遇到數據量較大的時候,好比圖片數據,這種方案顯示力不從心了。ide
咱們在平常請求數據後對UI進行綁定還有一個常見的問題,有時候咱們會把數據的的請求放到異步去操做,這樣不會由於長時間獲取數據致使UI進程的堵塞。可是隨之帶來的問題也挺多,例如咱們須要管理和維護好獲取到數據後的回調,另外在銷燬當前UI的時候,咱們須要確保異步任務中的資源有效的獲得了清理,防止出現內存溢出。一旦咱們的界面須要從新繪製的時候,咱們上述全部的異步操做須要從新建立和執行,這樣顯然浪費了系統的開銷。函數
咱們平常使用的Activity或者Fragment,他們的主要職責就是展現UI,以及與用戶的操做行爲進行交互,或者與系統的一些事件進行通訊,例如權限管理對話框。若是還要求Activity對數據請求的事件進行管理和維護,這個已經超出了Activity原本的意圖,而且隨着事務的增多,Activity會愈來愈臃腫,一旦出了問題,須要花費大量精力去維護。工具
ViewMode的推出正是基於上述問題給出的解決方案,完美高效的將UI控制器和數據業務進行分離,UI控制器只負責UI展現相關的工做,數據業務只負責獲取數據的相關工做。
經過上文的鋪墊,那麼如何使用ViewMode呢,爲了方便說明,本文采用一個簡單的例子,經過Activity中的list展現一組用戶的姓名,下面詳細進行說明。ViewMode是一個抽象類,因此咱們須要經過extends集成它纔可以使用,代碼以下所示:
public class MyViewMode extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers(){
if (users == null){
users = new MutableLiveData<>();
loadUsers();
}
return users;
}
private void loadUsers(){
String[] names = {"張三","李四","王五","John","小明","Leo","Wang","Li","Ha","Yun"};
List<User> userList = new ArrayList<>();
for (int i = 0; i < names.length; i++){
User user = new User();
user.setName(names[i]);
userList.add(user);
}
users.setValue(userList);
}
}
複製代碼
上述例子較爲簡單,代碼行數沒有多少,主要定義了一個類型爲MutableLiveData變量:users與兩個方法。這裏的users就是咱們用來存放數據的變量容器,可能有的同窗注意到了,它的類型是MutableLiveData,這個類型是什麼呢?看起來很眼熟。若是各位有印象的話,咱們在上文中介紹Jetpack的時候,在介紹Jetpack構成的時候,架構中有一個LiveData。恩對,這個變量類型也是Jetpack的一員。可不要小看它,它的本領不少,咱們會在下一文中單獨對它進行討論。你們只要這裏記住,他是用來存儲數據的容器便可,而MutableLiveData是對LiveData的擴展,主要實現了set和post方法來方便更改LiveData的值。
loadUser的方法很容易理解,爲了方便測試,咱們這裏定義了一個字符串數組,而後經過for循環進行遍歷存儲到list中,最後經過LiveData提供的setValue方法將數據存放到users中。getUser是針對數據users的get方法,爲了防止每次讀取user的時候都要建立一次數據,對數據進行判空處理,只有爲空的狀況下才調用loadUser去加載數據。
ViewMode的實現方式就是這麼簡單,它不須要關心UI是如何呈現的,它只關心數據如何獲取。徹底與UI無關。
下面看下Activity的實現方式,主要代碼以下:
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private MyAdapter adapater;
private List<User> mDatas;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
MyViewMode model = ViewModelProviders.of(this).get(MyViewMode.class);
model.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(@Nullable List<User> users) {
mDatas = users;
adapater.notifyDataSetChanged();
}
});
}
.
.
.
複製代碼
看完代碼是否是發現太簡單了,沒有什麼多餘須要說明的,主要是在OnCreate方法中,首先獲取咱們定義的MyViewMode,獲取的方式是經過ViewModelProviders的of方法綁定activity的實例進行初始化,而後經過get方法來獲取到MyViewMode的實例。
接着第二句經過調用咱們在MyViewMode已經定義過的getUser方法來獲取LiveData數據,LiveData數據能夠再經過observe方法進行數據回調的返回,如上代碼中的onChanged回調。因此咱們只要在onChange方法中作好數據刷新UI的操做便可。
注意:ViewMode中不能引用任何View的實例,也不能引用任何持有Activity或者Context的實例。若是有些請求數據的狀況必須用到Context,在繼承ViewMode的時候,能夠改成繼承AndroidViewMode,這個類會返回一個帶有Context的構造函數。
ViewMode在其生命週期的範圍內會一直保存在內存中,當依附的Activity被finish後纔會銷燬,或者當依附的Fragment detached後進行銷燬。爲了驗證,咱們在上述的例子中,每一個生命週期中打出相應的log,而後在MyViewMode的loadUser和getUser也打出相應的log。而後程序運行後執行橫豎屏切換來讓生命週期從新Oncreate,而後查看獲得的log以下:
2018-07-31 10:34:48.573 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onCreate
2018-07-31 10:34:48.607 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel getUsers
2018-07-31 10:34:48.607 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel loadUsers
2018-07-31 10:34:48.612 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onResume
2018-07-31 10:34:51.044 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onStop
2018-07-31 10:34:51.045 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onDestroy
2018-07-31 10:34:51.102 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onCreate
2018-07-31 10:34:51.142 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel getUsers
2018-07-31 10:34:51.148 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onResume
2018-07-31 10:35:01.437 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onStop
2018-07-31 10:35:01.438 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onDestroy
2018-07-31 10:35:01.485 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onCreate
2018-07-31 10:35:01.509 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel getUsers
2018-07-31 10:35:01.519 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onResume
2018-07-31 10:35:04.550 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onStop
2018-07-31 10:35:04.551 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel onCleared
2018-07-31 10:35:04.551 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onDestroy
複製代碼
當打開應用後,執行onCreate的時候,ViewMode會經過調用getUsers和loadUsers來獲取數據,當切換手機橫豎屏後,MainActivity會destroy並從新onCreate來重構當前界面,因此咱們在log中會看到生命週期再次從新觸發onCreate,可是ViewMode僅僅調用了getUsers來返回數據,並無調用loadUsers,咱們再回頭看上面getUsers的邏輯,當判斷users數據爲空的狀況下,纔會去執行loadUsers,這樣也就意味着,咱們切換橫豎屏後,activity被銷燬並重建後,user的數據並無丟失,因此並無從新執行獲取數據的操做。
細心的讀者可能發現了,在日誌的最後,ViewMode會執行onCleared操做,這個是ViewMode的一個回調,代表當前Activity要完全關閉,ViewMode須要作一些回收清理的操做,以下代碼:
@Override
protected void onCleared() {
super.onCleared();
/** * 這裏能夠執行一些資源釋放、數據清理的操做 */
}
複製代碼
下面用一張圖來標註ViewMode的生命週期與Activity的生命週期的關聯,以下圖所示:
咱們在平日裏會遇到這樣的場景,好比文件管理器這個應用,文件管理器的主界面經過一個MainFragment進行封裝實現,主要呈現各個文件類別的入口,好比有音樂、視頻、圖片、文檔等的入口,點擊對應的入口項,會進入對應的詳情頁,而詳情頁是由另一個DetailFragment實現。咱們傳統的實現方案是在DetailFragment中抽象暴露出一些回調接口,而後當在MainFragment進行點擊不一樣的入口時,執行DetailFragment中的回調接口來展現不一樣的Detail詳情內容。
一樣,咱們也能夠利用ViewMode來實現上述場景,關鍵代碼以下所示:
public class SharedViewModel extends ViewModel {
//selected保存的是被選中的item的狀態或者數據
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
//主要經過masterFragment進行調用交互,用來更新selected中的值
public void select(Item item) {
selected.setValue(item);
}
//主要給detailFragment進行回調,用來通知selected的值的更新狀況
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 -> {
//當點擊某一個item的時候,更新viewmode中的selected的值
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在onCreate中綁定ViewMode的selected的值,當有更新時通知DetailFragment
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, item -> {
// Update the UI.
});
}
}
複製代碼
上述代碼的邏輯很簡單,MasterFragment與DetailFragment並不直接進行交互,而是各自與ViewMode進行交互,MasterFragment用來更新維護ViewMode中的數據,DetailFragment能夠收到來自ViewMode中數據更新的通知。這樣便達到了兩個frangment之間的數據通訊。
ViewMode其實還有不少應用場景,可是須要和LiveData、Room等其餘的JetPack構件一塊兒使用效果更佳,因此這裏先賣個關子,等後續內容將LiveData、Room等內容都涉及到後,咱們統一利用這些構件來嘗試作一些更牛叉的事情。
我本身是一名從事了5年Android的老程序員,辭職目前在作講師,今年年初我花了一個月整理了一份最適合2019年學習的Android學習乾貨,各個方面都有整理,送給每一位Android開發的小夥伴,這裏是開發者彙集地,歡迎初學和進階中的小夥伴。"
加QQ羣:457848807(招募中)