Android Architecture Components-Lifecycle
Android Architecture Components顧名思義為一組系應用架構級元件庫,為管理UI元件的生命周期,資料加載等提供系統級支援。
詳細介紹及加入項目方法參考官方:https://developer.android.com/topic/libraries/architecture/
Android開發中Activity/Fragment承載過重任務一直是一大痛點,各種規則第三方庫等都收效甚微。但Lifecycle的出現從系統架構層提供了新的MVVM架構方案。
Lifecycle
Lifecycle不是簡單的作為一個庫以供使用,而是深入到了現有架構中,說明如下
Fragments and Activities in Support Library 26.1.0 and later already implement the LifecycleOwner interface.
即Support Library 26.1.0及以後的版本中的AppCompatActivity和Fragment預設都是實作了LifecycleOwner接口的.
Lifecycle包括LiveData 和 ViewModel兩部分。
LiveData
LiveData用來加載資料,并通知在其上的觀察者。
一般的使用流程為:建立執行個體A -> A.observe(owner, observer) -> A加載資料(set/post value)-> observer.onChanged -> 更新UI
其中owner為實作了LifecycleOwner的執行個體,observer為實作了Observer接口的執行個體用來在有資料變化時回調其onChanged進行資料更新。
大多數時候LiveData被關聯到Activity或Fragment中用來加載資料并更新UI,其觀察者模式的本質需要小心謹慎的處理register/unregister事件。這時将實作了LifecycleOwner接口的AppCompatActivity和Fragment直接傳進observe方法則隻需關心資料的擷取和更新而不用擔心生命周期等問題。
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
.......
}
ViewModel
從MVVM的架構上來說ViewModel連接配接UI與model,并且ViewModel與View雙向綁定。
ViewModel一般的擷取方式如下:
ProductViewModel model = ViewModelProviders.of(this).get(ProductViewModel.class);
其中this即Fragment或FragmentActivity的子類,Fragment或FragmentActivity都實作了ViewModelStoreOwner接口
FragmentActivity中的getViewModelStore定義
/**
* Returns the {@link ViewModelStore} associated with this activity
*
* @return a {@code ViewModelStore}
*/
@NonNull
@Override
public ViewModelStore getViewModelStore() {
......
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
而ViewModelProviders的of()最終調用的為ViewModelStores.of(activity)
ViewModelProviders
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
ViewModelStores.of(activity)則是擷取activity/fragment中定義的ViewModelStore對象
ViewModelStores
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
最後到ViewModelProvider的get方法中
ViewModelProvider
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
擷取ViewModel的key為DEFAULT_KEY + ":" + canonicalName,若mViewModelStore執行個體中沒有則建立。即每個
ViewModel在每個Activity/Fragment執行個體中隻會建立一次并且在onDestroy時才會clear。是以官方說明強調ViewModel中一定不能引用View或者持有Activity/Fragment的實作接口等引用。
ViewModel's only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.
項目實戰
Github:https://github.com/kevinshine/RedditG
界面加載及更新使用了Lifecycle,資料流使用了reddit的api
合理的使用Lifecycle可以優雅的将各個層串聯起來。
加載網絡資料并呈現
正常實作,在SubredditPageFragment中使用AsyncTask異步擷取資料并更新UI,執行上并沒有什麼不妥,但資料的擷取等邏輯與UI處理寫在了一起。
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loadData();
}
private void loadData() {
Log.d(TAG, "loadData:" + mSubName);
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
Log.d(TAG, "create paginator:" + mSubName);
DefaultPaginator<Submission> paginator = RedditManager.getInstance().getRedditClient()
.subreddit(mSubName)
.posts()
.build();
mPaginator = paginator;
Listing<Submission> submissions = null;
// catch java.net.SocketTimeoutException
try {
submissions = paginator.next();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "load data timeout:" + paginator.getBaseUrl());
}
// update UI
if (submissions != null) {
mSubList.addAll(submissions);
mMainHandler.post(() -> {
mAdapter.notifyDataSetChanged();
});
}
});
}
使用ViewModel和LiveData進行重構後Fragment中隻監聽資料變化并更新UI,擷取資料及相關業務都放到了ViewModel中。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
.....
// init ViewModel and load data
mSubredditPageViewModel = ViewModelProviders.of(this).get(SubredditPageViewModel.class);
mSubredditPageViewModel.getPaginatorData().observe(this,(DefaultPaginator<Submission> paginator)->{
mPaginator = paginator;
// second load list data
mSubredditPageViewModel.loadSubmissionList(mPaginator);
});
mSubredditPageViewModel.getSubmissionList().observe(this,((Listing<Submission> result)->{
// third update UI
if (result != null) {
mSubList.addAll(result);
mAdapter.notifyDataSetChanged();
}
}));
// first create Paginator
mSubredditPageViewModel.createPaginator(mSubName);
return view;
}