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方法则只需关心数据的获取和更新而不用担心生命周期等问题。
public void observe( LifecycleOwner owner, 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} */ public ViewModelStore getViewModelStore() { ...... if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } return mViewModelStore; }
而ViewModelProviders的of()最终调用的为ViewModelStores.of(activity)
ViewModelProviders
public static ViewModelProvider of( FragmentActivity activity, 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
public static ViewModelStore of( FragmentActivity activity) { if (activity instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) activity).getViewModelStore(); } return holderFragmentFor(activity).getViewModelStore(); }
最后到ViewModelProvider的get方法中
ViewModelProvider
public <T extends ViewModel> T get( 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( String key, 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处理写在了一起。
public void onActivityCreated( 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中。
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; }