Architecture Components 是在 2017 年 Google I/O 大会上,Google 官方推出的一个构建 Android 应用架构的库。它可以帮你避免在 Android 应用开发中常见的一些问题,比如:内存泄露,管理组件生命周期等等。本文将介绍如何利用 Architecture Components 库开发一个实际的 Android 应用 ArchitecturePractice,欢迎 fork 和 star。
本文主要分为以下两部分:
在资源有限的移动设备中,在任何时候,系统都有可能为新的应用杀死一些原来的应用,那么在原来应用中的组件(Activity、Fragment、Service等等)也会被销毁,这些组件的生命周期不受开发者控制,而是由系统控制的,所以不要在应用程序组件中存储任何应用数据和状态,并且应用程序组件之间相互不要依赖。
如果不可以在应用程序组件中存储应用数据和状态,那么该如何构建应用呢?这儿有两条常见的构建原则:
处理生命周期
在 android.arch.lifecycle 包中提供了可以构建生命周期感知的组件的类和接口,这些组件可以根据 Activity/Fragment 的生命周期自动调整它的行为。
Lifecycle
对象,并通过LifecycleOwner#getLifecycle()
方法返回该对象。
并不是只有 Activity 和 Fragment 才可以实现 LifecycleOwner 接口的,任何和 Activity/Fragment 生命周期有关系的类都可以实现此接口。通过实现此接口,该类完全是生命周期可感知的,只需要对它进行初始化,它就可以进行自己的初始化和清理操作,而不受其 Activity/Fragment 的管理。详细可以参看官方文档说明:LifecycleOwner 实践
LiveData
LiveData
是一个数据持有类,它持有一个值并且该值可以被观察。不同于普通的可观察者,LiveData
遵从应用组件的生命周期,这样 Observer
便可以指定一个其应该遵循的 Lifecycle
。
如果 Observer
所依附的 Lifecycle
处于 STARTED
或者 RESUMED
状态,则 LiveData
认为 Observer
处于活跃状态。
可以感知组件生命周期的 LiveData
给我们提供了一种可能:可以在多个 Activity
、Fragment
之间共享它。
使用 LiveData
会有以下几个优势:
Observer
是绑定到 Lifecycle
对象上的,当 Lifecycle
对象被销毁的时候,LiveData
对象也会被自动清除Activity
停止而使应用崩溃:如果 Observer
所绑定的 Lifecycle
处于闲置状态(例如:Activity
处于后台运行时),他们不会接收到改变的事件Lifecycle
重新启动以后(例如:Activity
从后台重新开始运行于前台),它会接收到最新的数据(除非没有最新的数据)Activity
或者 Fragment
以为配置改变(例如:旋转屏幕)被重建以后,LiveData
将会接收到最新的数据Activity
或者 Fragment
之间共享 LiveData
数据。Fragment
只有在处于活跃的时候才会观察 LiveData
数据。由于 Fragment
提供了 Lifecycle
对象,所以 LiveData
会管理这一切。有时候,也许想在 LiveData
被下发到 Observer
之前,改变 LiveData
的值,或者是基于当前的 LiveData
下发另一个不同的 LiveData
值。Lifecycle
包中的 Transformations
可以实现这样的功能。
Transformations.map()
,使用此方法,可以将 LiveData
传递到下游
LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
,和 map()
方法类似,使用 switchMap()
应用于 LiveData
的值并解包,然后将结果传递到下游。传递给 switchMap()
的方法必须返回一个 Lifecycle
private LiveData getUser(String id) {
...;
}
LiveData userId = ...;
LiveData user = Transformations.switchMap(userId, id -> getUser(id) );
使用这两个转换,允许在整个调用链中携带观察者的 Lifecycle
信息,这样的话,只有在观察者观察到 LiveData
的返回值时,才会运算这些转换。
当你需要在 ViewModel
中添加一个 Lifecycle
对象时,Transformations
或许是一个好的解决办法。
ViewModel
ViewModel 类是用来存储和管理 UI 相关的数据,这样在配置发生变化(例如:屏幕旋转)时,数据就不会丢失。
由于应用程序组件(例如:Activity、Fragment),具有一个由 Android Framework 管理的生命周期,Activity 或 Fragment 在某些情况下(比如:内存紧张或者屏幕旋转)会发生销毁或者重新创建的情况。这样就会带来一些问题:
针对以上问题,Lifecycle 提供了一个叫 ViewModel 的类,一个 UI 控制器的帮助类,用来为 UI 准备数据。
在配置更改的时候,ViewModel 会被保留,以便其保存的数据可以立即传递给重新创建的 Activity 或者 Fragment 实例中。如果 Activity 被重新创建,它将会收到由之前的 Activity 或者 Fragment 创建的 ViewModel 实例。当所有者 Activity 被销毁以后,Framework 会调用 ViewModel#onCleared() 清楚系统资源。
在多个 Fragment 之间共享数据
在同一个 Activity 中的多个 Fragment 之间进行通信是十分常见的需求,目前通常的做法是新建一个接口,并且用 Activity 将多个 Fragment 联系起来。如果需要通信的数据比较多,就会出现接口泛滥的情况。
使用 ViewModel 可以解决这个痛点。在同一个 Activity 中的 Fragment 可以使用此 Activity 限定的 ViewModel 来处理该通讯。比如如下代码所示:
public class SharedViewModel extends ViewModel {
private final MutableLiveData- selected = new MutableLiveData
- ();
public void select(Item item) {
selected.setValue(item);
}
public LiveData
- getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// 更新 UI
});
}
}
在上面示例代码中,获取 ViewModelProvider 时两个 Fragment 都使用 getActivity() 方法,这就意味着,它们会收到同一个 Activity 限制的同一个 ViewModel 实例对象。这样做有以下几个优点:
ViewModel 的生命周期
ViewModel 对象存在于内存当中,直到传递给它的 Lifecycle 对象被完成的销毁(Activity:被完全销毁,Fragment:被完成移除)。其生命周期图如下所示:
ViewModel vs SavedInstanceState
onCreate()
方法中通过 Bundle 恢复数据。Room Persistence Library
Room 在 SQLite 之上提供了一个抽象层,以便在利用 SQLite 全部功能的同时也可以流畅发访问数据库。
在 Room 中有非常重要的三个类:
注解的类应该是一个继承了
RoomDatabase
的抽象类。在运行时,可以通过Room.databaseBuilder()
或者Room.inMemoryDatabaseBuilder()
方法获取单例。
Database
中的 entities
字段引用 Entity 类。Entity 类中的每个字段都会持久化到数据中,除非使用 @Ignore
注解修饰@Database
注解修饰的类必须包含一个没有参数的抽象方法,该方法的返回值是被 @Dao
注解的类。在编译时生成代码时,Room 创建该类的实现。Room 中的三大组件与应用程序中其他部分的关系如下图所示:
关于 Room Persistence Library 更加详细的内容,请参阅 Room Persistence Library 官方说明文档。
添加 Google Maven 仓库
在应用工程的 build.gradle
文件中添加依赖对 Google Maven 的依赖:
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
}
}
添加 Architecture Components 组件
在应用或者模块的 build.gradle
文件中添加对 Architecture Components 的依赖,如下所示:
// Lifecycles, LiveData 和 ViewModel
compile "android.arch.lifecycle:runtime:1.0.0-alpha5"
compile "android.arch.lifecycle:extensions:1.0.0-alpha5"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha5"
// Room
compile "android.arch.persistence.room:runtime:1.0.0-alpha5"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha5"
// 对 RxJava 的支持
compile "android.arch.persistence.room:rxjava2:1.0.0-alpha5"
// 对测试 Room 的支持
testCompile "android.arch.persistence.room:testing:1.0.0-alpha5"
假如在项目中有类似于下面知乎列表这样的一个页面:
关于该页面有如下两个接口:
// 请求最新的知乎列表(下拉刷新)
https://news-at.zhihu.com/api/4/news/latest
// 上拉加载历史列表(上拉加载更多)
https://news-at.zhihu.com/api/4/news/before/{date}
根据上面两个接口和 “UI设计稿”,再根据 Architecture Components 组件中提供的类,我们一步步完成此页面。首先我们分三步:
View 界面
此页面使用 Fragment 控制并显示,将其命名为 ZhihuListFragment.java
,其布局文件 fragment_zhihu_list.xml
如下所示:
fragment_zhihu_list.xml
布局文件比较简单,不需要多讲。
ZhihuListFragment.java
代码如下所示:
/**
* ZhihuListFragment.java
*
* Created by lijiankun on 17/7/30.
*/
public class ZhihuListFragment extends LifecycleFragment {
// ZhihuListFragment 所对应的 ViewModel 类的对象
private ZhihuListViewModel mListViewModel = null;
private SwipeRefreshLayout mRefreshLayout = null;
private ZhihuListAdapter mAdapter = null;
private ProgressBar mLoadMorebar = null;
private View mRLZhihuRoot = null;
// 自定义接口,将 RecyclerView 的 Adapter 对其中每个 Item 的点击事件会传到 ZhihuListFragment 中。
private final OnItemClickListener mZhihuOnItemClickListener =
new OnItemClickListener() {
@Override
public void onClick(ZhihuStory zhihuStory) {
if (Util.isNetworkConnected(MyApplication.getInstance())) {
ZhihuActivity.startZhihuActivity(getActivity(), zhihuStory.getId(),
zhihuStory.getTitle());
} else {
Util.showSnackbar(mRLZhihuRoot, getString(R.string.network_error));
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_zhihu_list, container, false);
initView(view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
subscribeUI();
}
/**
* 将 ZhihuListFragment 对应的 ZhihuListViewModel 类中的 LiveData 添加注册监听到
* 此 ZhihuListFragment
*/
private void subscribeUI() {
// 通过 ViewModelProviders 创建对应的 ZhihuListViewModel 对象
ZhihuListViewModel.Factory factory = new ZhihuListViewModel
.Factory(MyApplication.getInstance()
, Injection.getDataRepository(MyApplication.getInstance()));
mListViewModel = ViewModelProviders.of(this, factory).get(ZhihuListViewModel.class);
mListViewModel.getZhihuList().observe(this, new Observer>() {
@Override
public void onChanged(@Nullable List stories) {
if (stories == null || stories.size() <= 0) {
return;
}
L.i("size is " + stories.size());
mAdapter.setStoryList(stories);
}
});
mListViewModel.isLoadingZhihuList().observe(this, new Observer() {
@Override
public void onChanged(@Nullable Boolean aBoolean) {
if (aBoolean == null) {
return;
}
L.i("state " + aBoolean);
mRefreshLayout.setRefreshing(false);
mLoadMorebar.setVisibility(aBoolean ? View.VISIBLE : View.INVISIBLE);
}
});
mListViewModel.refreshZhihusData();
}
/**
* 初始化页面 UI
*
* @param view Fragment 的 View
*/
private void initView(View view) {
if (view == null) {
return;
}
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
mAdapter = new ZhihuListAdapter(getContext(), mZhihuOnItemClickListener);
RecyclerView recyclerView = view.findViewById(R.id.rv_zhihu_list);
recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addOnScrollListener(new ZhihuOnScrollListener());
mRefreshLayout = view.findViewById(R.id.srl_zhihu);
mRefreshLayout.setOnRefreshListener(new ZhihuSwipeListener());
mRefreshLayout.setColorSchemeResources(
android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
mLoadMorebar = view.findViewById(R.id.bar_load_more_zhihu);
mRLZhihuRoot = view.findViewById(R.id.rl_zhihu_root);
}
/**
* ZhihuSwipeListener 用于 SwipeRefreshLayout 下拉刷新操作
*/
private class ZhihuSwipeListener implements SwipeRefreshLayout.OnRefreshListener {
@Override
public void onRefresh() {
mAdapter.clearStoryList();
mListViewModel.refreshZhihusData();
}
}
/**
* ZhihuOnScrollListener 用于 RecyclerView 下拉到最低端时的上拉加载更多操作
*/
private class ZhihuOnScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
LinearLayoutManager layoutManager = (LinearLayoutManager)
recyclerView.getLayoutManager();
int lastPosition = layoutManager
.findLastCompletelyVisibleItemPosition();
if (lastPosition == mAdapter.getItemCount() - 1) {
// 上拉加载更多数据
mListViewModel.loadNextPageZhihu();
}
}
}
}
在 ZhihuListFragment.java
中注释已经比较清楚,关于 UI 方面的不多讲,其中最重要的一个方法是 subscribeUI()
方法。在该方法中,创建 ZhihuListViewModel 对象之后,对 ZhihuListViewModel 中两个重要的数据进行注册观察并更新 UI,两个重要的数据分别是:LiveData
和 >
LiveData
:
LiveData>
:表示从数据层获取到的知乎列表的数据LiveData
:表示是否正在获取数据的状态,以控制界面中加载动画的显示和隐藏ViewModel 控制层
在 ZhihuListViewModel
类中需要三个 LiveData 类型的属性。LiveData 类型的数据和 RxJava 中的 Observables
工作模式类似,当 LiveData 持有的数据发生变化时,通知观察者。如下面代码所示:
public class ZhihuListViewModel extends AndroidViewModel {
// 请求接口中查询的日期参数
private MutableLiveData mZhihuPageDate = new MutableLiveData<>();
// Zhihu 列表的数据
private final LiveData> mZhihuList;
// 是否正在进行网络请求的状态参数
private final LiveData mIsLoadingZhihuList;
......
private ZhihuListViewModel(Application application) {
super(application);
// 使用 Transformations.switchMap() 方法,表示当 View 改变 mZhihuPageDate 参数的值时,则进行 zhihu 列表数据的请求
mZhihuList = Transformations.switchMap(mZhihuPageDate, new Function>>() {
@Override
public LiveData> apply(String input) {
......
}
});
}
public LiveData> getZhihuList() {
return mZhihuList;
}
public LiveData isLoadingZhihuList() {
return mIsLoadingZhihuList;
}
/**
* 下拉刷新,获取最新的 Zhihu 列表数据
*/
public void refreshZhihusData() {
mZhihuPageDate.setValue("today");
}
/**
* 上拉加载更多时,获取 Zhihu 历史列表数据
*
* @param positon 表示列表滑动到最后一项
*/
public void loadNextPageZhihu(int positon) {
if (!Util.isNetworkConnected(MyApplication.getInstance())) {
return;
}
mZhihuPageDate.setValue(String.valueOf(positon));
}
public static class Factory extends ViewModelProvider.NewInstanceFactory {
@NonNull
private final Application mApplication;
public Factory(@NonNull Application application) {
mApplication = application;
}
@Override
public T create(Class modelClass) {
return (T) new ZhihuListViewModel(mApplication);
}
}
}
注:由于 ViewModel 存活的时间可能会比个别的 activity 和 fragment 实例更长,所以它决不能引用 View,或任何持任何 activity(context)。如果 ViewModel 需要 Application 的 context(如:调用系统服务),可以继承 AndroidViewModel 类,可以在构造函数中接受 Application。
上面示例的 ZhihuListViewModel 类是功能并不完整的 ViewModel 类,因为它只是向 View 层提供了操作 Zhihu 列表数据和监听数据请求状态的接口,那么 ZhihuListViewModel 该从哪里获取数据呢?换句话说,数据源在哪里?
在 ArchitecturePractice 项目中,封装了 DataRepository 类,表示所有数据的源头。
那 ZhihuListViewModel 应该持有一个 DataRepository
对象,来获取数据。完整的 ZhihuListViewModel
类如下所示:
/**
* ZhihuListViewModel.java
*
* Created by lijiankun on 17/7/30.
*/
public class ZhihuListViewModel extends AndroidViewModel {
// 请求接口中查询的日期参数
private MutableLiveData mZhihuPageDate = new MutableLiveData<>();
// Zhihu 列表的数据
private final LiveData> mZhihuList;
// 数据源
private DataRepository mDataRepository = null;
private ZhihuListViewModel(Application application, DataRepository dataRepository) {
super(application);
mDataRepository = dataRepository;
// 使用 Transformations.switchMap() 方法,当 View 改变 mZhihuPageDate 参数的值时,则进行 zhihu 列表数据的请求
mZhihuList = Transformations.switchMap(mZhihuPageDate, new Function>>() {
@Override
public LiveData> apply(String input) {
return mDataRepository.getZhihuList(input);
}
});
}
/**
* 获取 Zhihu 列表数据
*
* @return Zhihu 列表数据
*/
public LiveData> getZhihuList() {
return mZhihuList;
}
/**
* 数据请求状态由 DataRepository 控制,包括下拉刷新和上拉加载更多
*
* @return 是否在进行数据请求
*/
public LiveData isLoadingZhihuList() {
return mDataRepository.isLoadingZhihuList();
}
/**
* 下拉刷新,获取最新的 Zhihu 列表数据
*/
public void refreshZhihusData() {
mZhihuPageDate.setValue("today");
}
/**
* 上拉加载更多时,获取 Zhihu 历史列表数据
*
* @param positon 表示列表滑动到最后一项
*/
public void loadNextPageZhihu(int positon) {
if (!Util.isNetworkConnected(MyApplication.getInstance())) {
return;
}
mZhihuPageDate.setValue(String.valueOf(positon));
}
public static class Factory extends ViewModelProvider.NewInstanceFactory {
@NonNull
private final Application mApplication;
private final DataRepository mGirlsDataRepository;
public Factory(@NonNull Application application, DataRepository girlsDataRepository) {
mApplication = application;
mGirlsDataRepository = girlsDataRepository;
}
@Override
public T create(Class modelClass) {
return (T) new ZhihuListViewModel(mApplication, mGirlsDataRepository);
}
}
}
Model 数据层
正如在 ViewModel 控制层中介绍的,ArchitecturePractice 中所有的数据均由 DataRepository
类中获取。在 DataRepository
中有两个数据源:本地数据库和远端服务器,如果有网,则从服务器获取最新数据,并保存在本地数据库中;如果没有网,则从本地数据库中加载数据并显示。则 DataRepository
的初步实现是这样的:
/**
* DataRepository.java
*
* Created by lijiankun on 17/7/7.
*/
public class DataRepository {
private static DataRepository INSTANCE = null;
// 从服务器获取数据
private final DataSource mRemoteDataSource;
// 从本地数据库获取数据
private final DataSource mLocalDataSource;
private static Application sApplication = null;
private DataRepository(@NonNull DataSource remoteDataSource,
@NonNull DataSource localDataSource) {
mRemoteDataSource = remoteDataSource;
mLocalDataSource = localDataSource;
}
static DataRepository getInstance(@NonNull DataSource remoteDataSource,
@NonNull DataSource localDataSource,
Application application) {
if (INSTANCE == null) {
synchronized (DataRepository.class) {
if (INSTANCE == null) {
INSTANCE = new DataRepository(remoteDataSource, localDataSource);
sApplication = application;
}
}
}
return INSTANCE;
}
public LiveData> getZhihuList(@NonNull String date) {
if (Util.isNetworkConnected(sApplication.getApplicationContext())) {
if (date.equals("today")) {
return mRemoteDataSource.getLastZhihuList();
} else {
return mRemoteDataSource.getMoreZhihuList(date);
}
} else {
if (date.equals("today")) {
return mLocalDataSource.getLastZhihuList();
} else {
return mLocalDataSource.getMoreZhihuList(date);
}
}
}
public LiveData isLoadingZhihuList() {
if (Util.isNetworkConnected(sApplication.getApplicationContext())) {
return mRemoteDataSource.isLoadingZhihuList();
} else {
return mLocalDataSource.isLoadingZhihuList();
}
}
}
其中的 DataSource
表示获取数据的抽象层,如下所示:
public interface DataSource {
......
/**
* Zhihu 相关方法
*/
LiveData> getLastZhihuList();
LiveData> getMoreZhihuList(String date);
LiveData isLoadingZhihuList();
}
此外还有两个非常重要的类:RemoteDataSource
和 LocalDataSource
,这两个类分别实现了 DataSource
接口。
RemoteDataSource
类代码如下所示,从远端服务器获取数据使用的是 Retrofit
,并且对网络请求进行简单封装,由 ApiManager 统一向外提供网络请求接口:
/**
* RemoteDataSource.java
*
* Created by lijiankun on 17/7/7.
*/
public class RemoteDataSource implements DataSource {
private static RemoteDataSource INSTANCE = null;
private final MutableLiveData mIsLoadingZhihuList;
private final MutableLiveData> mZhihuList;
private final ApiZhihu mApiZhihu;
private String mZhihuPageDate;
{
mIsLoadingZhihuList = new MutableLiveData<>();
mZhihuList = new MutableLiveData<>();
}
private RemoteDataSource() {
mApiZhihu = ApiManager.getInstance().getApiZhihu();
}
public static RemoteDataSource getInstance() {
if (INSTANCE == null) {
synchronized (RemoteDataSource.class) {
if (INSTANCE == null) {
INSTANCE = new RemoteDataSource();
}
}
}
return INSTANCE;
}
@Override
public LiveData> getLastZhihuList() {
mIsLoadingZhihuList.setValue(true);
mApiZhihu.getLatestNews()
.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
mZhihuList.setValue(response.body().getStories());
refreshLocalZhihuList(response.body().getStories());
mZhihuPageDate = response.body().getDate();
}
mIsLoadingZhihuList.setValue(false);
}
@Override
public void onFailure(Call call, Throwable t) {
mIsLoadingZhihuList.setValue(false);
}
});
return mZhihuList;
}
@Override
public LiveData> getMoreZhihuList(String date) {
mIsLoadingZhihuList.setValue(true);
mApiZhihu.getTheDaily(mZhihuPageDate)
.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
mZhihuList.setValue(response.body().getStories());
refreshLocalZhihuList(response.body().getStories());
mZhihuPageDate = response.body().getDate();
}
mIsLoadingZhihuList.setValue(false);
}
@Override
public void onFailure(Call call, Throwable t) {
mIsLoadingZhihuList.setValue(false);
}
});
return mZhihuList;
}
@Override
public MutableLiveData isLoadingZhihuList() {
return mIsLoadingZhihuList;
}
private void refreshLocalZhihuList(List zhihuStoryList) {
if (zhihuStoryList == null || zhihuStoryList.isEmpty()) {
return;
}
AppDatabaseManager.getInstance().insertZhihuList(zhihuStoryList);
}
}
LocalDataSource
类代码如下所示,从本地数据库获取数据使用的是 Architecture Components 中的 Room
库,简单封装为 AppDatabaseManager ,向外提供统一的方法:
/**
* LocalDataSource.java
*
* Created by lijiankun on 17/7/7.
*/
public class LocalDataSource implements DataSource {
private static LocalDataSource INSTANCE = null;
private LocalDataSource() {
}
public static LocalDataSource getInstance() {
if (INSTANCE == null) {
synchronized (LocalDataSource.class) {
if (INSTANCE == null) {
INSTANCE = new LocalDataSource();
}
}
}
return INSTANCE;
}
......
@Override
public LiveData> getLastZhihuList() {
return AppDatabaseManager.getInstance().loadZhihuList();
}
@Override
public LiveData> getMoreZhihuList(String date) {
return null;
}
@Override
public LiveData isLoadingZhihuList() {
return AppDatabaseManager.getInstance().isLoadingZhihuList();
}
}
使用到的 ApiManager
用于统一管理 Retrofit 网络请求,AppDatabaseManager
则是对 Room 数据库的统一管理,关于 Retrofit 和 Room 的使用就不再多说。这样一个向外提供干净可靠 API 的数据源 DataRepository 模块则完成了,DataRepository 主要负责处理数据的获取。
至此,关于 Architecture Components 组件的介绍和实践都全部介绍完毕,本文中用于举例的 ArchitecturePractice 在 GitHub 上,欢迎 star 和 fork,也欢迎通过下面二维码下载 APK 体验,如果有什么问题欢迎指出。