0%

Android Architecture Components学习


Architecture Components 是在 2017 年 Google I/O 大会上,Google 官方推出的一个构建 Android 应用架构的库。它可以帮你避免在 Android 应用开发中常见的一些问题,比如:内存泄露,管理组件生命周期等等,帮助你去构建一个健壮,易测,可维护的应用。Now available in preview目前的版本还是一个预览版,但是我们依然可以去了解一下。

Google认为在 Android 中,应用间的这种跳跃切换行为很普遍,应用程序必须把这些问题都正确的处理好。移动设备的硬件资源是很有限的,在任何时候系统都可能杀掉一些应用去释放一些资源给新的应用。那么你的应用组件的创建和销毁是不完全可控的,它可能在任何时候由于用户或者系统的行为而触发。应用组件的生命周期也不是完全由你掌控的,所以不应该存储一些数据或者状态在应用组件中,应用组件之间也不应该彼此依赖。


架构原则

  1. 应用中的关注点分离。不应该在一个Activity或者Fragment中写所有的代码。任何与 UI 或者交互无关的代码都不应该存在这些类中。保证他们尽可能的职责单一化将会使我们避免很多生命周期相关的问题。因为Android系统可能会随时由于用户的行为或者系统状态(比如剩余内存过低)而销毁你的应用组件,减少组件之间的依赖可以提供一个稳定的用户体验。
  2. 应该采用Model驱动UI的方式,最好是一个可持久化的模型。满足以下两个条件:
    • 如果系统销毁我们的应用,用户不会为此而导致丢失数据。
    • 在网络状况不好甚至断网的情况下,我们的应用仍然可以继续工作。

Model是专门负责为我们的应用处理和存储数据的。完全独立于View和其他应用中的组件,所以不存在生命周期相关的问题。保证UI部分的代码足够简单,没有业务逻辑,使Model管理数据的职责明确,这样更容易测试,而且稳定性更高。

框架介绍

生命周期的处理

在 android.arch.lifecycle包中提供了生命周期感知的组件的类和接口,这些组件可以根据 Activity/Fragment 的生命周期自动调整它的行为。

  • LifecycleOwner:是一个具有单一方法的接口。如果一个类实现了此接口,则该类中需要持有一个Lifecycle对象,并通过 LifecycleOwner#getLifecycle()方法返回该对象。代码如下:
    1
    2
    3
    4
    5
    private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
    @Override
    public LifecycleRegistry getLifecycle() {
    return mRegistry;
    }
    如果我们需要在Activity的生命周期的某些状态中对一些工具类做对应的处理,按以前的写法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyListener {
public MyListener(Callback callback) {
// ...
}

void start() {
// ...
}

void stop() {
// ...
}

void enable(){
// ...
}
public interface Callback{
void onLocation(long longtitude ,long latitude);
}
}

class MyActivity extends AppCompatActivity {
private MyListener myListener;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListener = new MyListener(new MyListener.Callback() {
@Override
public void onLocation(long longtitude, long latitude) {
Log.d(TAG, "onLocation: " +longtitude+","+latitude);
}
});
}

public void onStart() {
super.onStart();
myListener.start();
}

public void onStop() {
super.onStop();
myListener.stop();
}
}

定义一个位置监听类MyLocationListener,定义它在MyActivity生命周期的onStart和onStop中需要执行的方法,start()和stop(),接着在MyActivity的对应状态中调用。

如果我们让工具类实现LifecycleObserver接口,我们需要注入MyActivity的生命周期对象Lifecycle,那么我们只需要增加注解就可以实现在对应状态调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MyListener implements LifecycleObserver{
public static final String TAG="MyListener";

private boolean enabled = false;
private Callback callback;
private Lifecycle lifecycle;
public MyListener(Lifecycle lifecycle, Callback callback) {
this.lifecycle=lifecycle;
this.callback =callback;
}

@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start(){
Log.d(TAG, "start: ");
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop(){
Log.d(TAG, "stop: ");

}


public void enable() {
enabled = true;
if(lifecycle.getCurrentState().isAtLeast(Lifecycle.State.INITIALIZED)){
getLocation();
}
}

//模拟位置获取
private void getLocation(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long longtitude = (long) (Math.random()*100);
long latitude = (long) (Math.random()*100);
callback.onLocation(longtitude,latitude);
}

public interface Callback{
void onLocation(long longtitude ,long latitude);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyActivity extends LifecycleActivity {
private MyListener myListener;
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListener = new MyListener(this, getLifecycle(), new MyListener.Callback() {
@Override
public void onLocation(long longtitude, long latitude) {
Log.d(TAG, "onLocation: " +longtitude+","+latitude);
}
});
getLifecycle().addObserver(myListener);
myListener.enable();
}
}

在onStart中调用就增加@OnLifecycleEvent(Lifecycle.Event.ON_START)的注解,不用在MyActivity中调用了。
MyActivity继承了LifecycleActivity,而LifecycleActivity已经实现了LifecycleOwner的接口,这里我们需要通过getLifecycle()传入MyActivity的生命周期对象Lifecycle到工具类中即可。
工具类获得Lifecycle对象后可对MyActivity的周期状态进行判断,Lifecycle.State.INITIALIZED这个代表生命周期所有者的初始化状态。
当然还有其他状态CREATED DESTROYED RESUMED STARTED

目前这个库只是一个beta版,提供的api一直都在变动,这里就通过一个入门的小例子来学习一下。

这个小例子是展示知乎的一个资讯列表。

目前有两个可用的免费api

https://news-at.zhihu.com/api/4/news/latest

https://news-at.zhihu.com/api/4/news/before/{date}

先创建一个知乎activity,这个相当于mvp架构的v层,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public class ZhihuActivity extends LifecycleActivity{

private static final String TAG = "ZhihuActivity";
private SwipeRefreshLayout refreshLayout = null;
private ZhihuListAdapter adapter = null;
private ProgressBar loadMorebar = null;
private View rootview = null;

private ZhihuListViewModel zhihuListViewModel = null;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zhihu);
initView();
subscribeUI();
}

//初始化view
private void initView(){
rootview=findViewById(R.id.rl_zhihu_root);
loadMorebar=findViewById(R.id.bar_load_more_zhihu);
refreshLayout=findViewById(R.id.refresh);
RecyclerView recyclerView = findViewById(R.id.rv_zhihu_list);

LinearLayoutManager layoutManager = new LinearLayoutManager(this);
adapter = new ZhihuListAdapter(this);
adapter.setClickListener(new OnItemClickListener<ZhihuStory>() {
@Override
public void onClick(ZhihuStory zhihuStory) {

}
});

recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addOnScrollListener(new ZhihuOnScrollListener());

refreshLayout.setOnRefreshListener(new ZhihuSwipeListener());
refreshLayout.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);

}

private void subscribeUI() {
// 通过 ViewModelProviders 创建对应的 ZhihuListViewModel 对象
ZhihuListViewModel.Factory factory = new ZhihuListViewModel
.Factory(getApplication()
, Injection.getDataRepository(getApplication()));
zhihuListViewModel = ViewModelProviders.of(this, factory).get(ZhihuListViewModel.class);
//注册知乎列表数据的监听,当有改变时刷新列表
zhihuListViewModel.getZhihuList().observe(this, new Observer<List<ZhihuStory>>() {
@Override
public void onChanged(@Nullable List<ZhihuStory> stories) {
if (stories == null || stories.size() <= 0) {
return;
}
Log.i(TAG,"size is " + stories.size());
adapter.setStoryList(stories);
}
});
//注册加载更多的监听
zhihuListViewModel.isLoadingZhihuList().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean aBoolean) {
if (aBoolean == null) {
return;
}
Log.i(TAG,"state " + aBoolean);
refreshLayout.setRefreshing(false);
loadMorebar.setVisibility(aBoolean ? View.VISIBLE : View.INVISIBLE);
}
});
zhihuListViewModel.refreshZhihusData();
}

/**
* 下拉刷新回调
*/
private class ZhihuSwipeListener implements SwipeRefreshLayout.OnRefreshListener {
@Override
public void onRefresh() {
adapter.clearStoryList();
zhihuListViewModel.refreshZhihusData();
}
}

/**
* 上拉加载更多回调
*/
class ZhihuOnScrollListener extends RecyclerView.OnScrollListener {

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
LinearLayoutManager layoutManager = (LinearLayoutManager)
recyclerView.getLayoutManager();
int lastPosition = layoutManager
.findLastCompletelyVisibleItemPosition();
if (lastPosition == adapter.getItemCount() - 1) {
// 上拉加载更多数据
zhihuListViewModel.loadNextPageZhihu(lastPosition,getApplicationContext());
}
}
}
}

recyclerView的adapter很简单,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class ZhihuListAdapter extends RecyclerView.Adapter<ZhihuListAdapter.ZhihuViewHolder> {

private OnItemClickListener<ZhihuStory> clickListener = null;

private Context mContext = null;

private List<ZhihuStory> mStoryList = null;

public ZhihuListAdapter(Context context) {
mStoryList = new ArrayList<>();
mContext = context;
}

public void setClickListener(OnItemClickListener<ZhihuStory> listener){
clickListener = listener;
}
@Override
public ZhihuViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ZhihuViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.zhihu_list_item, parent, false));
}

@Override
public void onBindViewHolder(ZhihuViewHolder holder, int position) {
final ZhihuStory zhihuStory = mStoryList.get(position);
holder.tv_zhihu_title.setText(zhihuStory.getTitle());
holder.tv_zhihu_time.setText(zhihuStory.getGa_prefix());
holder.ll_zhihu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(clickListener != null){
clickListener.onClick(zhihuStory);
}
}
});
Glide.with(mContext)
.load(zhihuStory.getImages()[0])
.centerCrop()
.into(holder.img_zhihu);
}

@Override
public int getItemCount() {
return mStoryList.size();
}

//数据添加后刷新列表
public void setStoryList(List<ZhihuStory> storyList) {
if (storyList == null || storyList.size() == 0) {
return;
}
mStoryList.addAll(storyList);
notifyDataSetChanged();
}

//清空当前数据
public void clearStoryList() {
mStoryList.clear();
notifyDataSetChanged();
}

class ZhihuViewHolder extends RecyclerView.ViewHolder {

public View ll_zhihu;
public TextView tv_zhihu_title;
public TextView tv_zhihu_time;
public ImageView img_zhihu;

private ZhihuViewHolder(View itemView) {
super(itemView);
ll_zhihu = itemView.findViewById(R.id.ll_zhihu);
tv_zhihu_title = itemView.findViewById(R.id.tv_zhihu_title);
tv_zhihu_time = itemView.findViewById(R.id.tv_zhihu_time);
img_zhihu = itemView.findViewById(R.id.img_zhihu);
}
}
}

对应的item监听代码如下:

1
2
3
public interface OnItemClickListener<T> {
void onClick(T t);
}

对应的布局文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_zhihu"
android:layout_width="match_parent"
android:layout_height="113dp">

<ImageView
android:id="@+id/img_zhihu"
android:layout_width="112dp"
android:layout_height="112dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_gravity="center_vertical"
android:contentDescription="@null"
android:paddingBottom="16dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:src="@mipmap/ic_launcher"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="112dp"
android:layout_toEndOf="@+id/img_zhihu"
android:layout_toRightOf="@+id/img_zhihu"
android:orientation="vertical">

<TextView
android:id="@+id/tv_zhihu_title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:paddingTop="24dp"/>

<TextView
android:id="@+id/tv_zhihu_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:paddingLeft="16dp"
android:paddingStart="16dp"/>
</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_alignParentBottom="true"
android:layout_marginLeft="112dp"
android:layout_marginStart="112dp"
android:background="@color/lightGrey"/>
</RelativeLayout>

这里创建了ZhihuListViewModel,相当于mvp架构的p层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

public class ZhihuListViewModel extends AndroidViewModel {

// 请求接口中查询的日期参数
private MutableLiveData<String> requestPageDate = new MutableLiveData<>();
// 知乎列表的数据
private final LiveData<List<ZhihuStory>> mZhihuList;
// 数据源对象
private DataRepository dataRepository = null;

private ZhihuListViewModel(Application application, final DataRepository dataRepository) {
super(application);
this.dataRepository = dataRepository;
// 使用 Transformations.switchMap() 方法,当 View 改变 requestPageDate 参数的值时,则进行 zhihu 列表数据的请求
mZhihuList = Transformations.switchMap(requestPageDate, new Function<String, LiveData<List<ZhihuStory>>>() {
@Override
public LiveData<List<ZhihuStory>> apply(String input) {
return dataRepository.getZhihuList(input);
}
});
}

/**
* 获取 Zhihu 列表数据
*
* @return Zhihu 列表数据
*/
public LiveData<List<ZhihuStory>> getZhihuList() {
return mZhihuList;
}

/**
* 数据请求状态由 DataRepository 控制,包括下拉刷新和上拉加载更多
*
* @return 是否在进行数据请求
*/
public LiveData<Boolean> isLoadingZhihuList() {
return dataRepository.isLoadingZhihuList();
}

/**
* 下拉刷新,获取最新的 Zhihu 列表数据
*/
public void refreshZhihusData() {
requestPageDate.setValue("today");
}

/**
* 上拉加载更多时,获取 Zhihu 历史列表数据
*
* @param positon 表示列表滑动到最后一项
*/
public void loadNextPageZhihu(int positon, Context context) {
if (!Util.isNetworkConnected(context)) {
return;
}
requestPageDate.setValue(String.valueOf(positon));
}

public static class Factory extends ViewModelProvider.NewInstanceFactory {

@NonNull
private final Application application;

private final DataRepository dataRepository;

public Factory(@NonNull Application application, DataRepository dataRepository) {
this.application = application;
this.dataRepository = dataRepository;
}

@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new ZhihuListViewModel(application, dataRepository);
}
}
}

初始化时,对requestPageDate做监听,当requestPageDate的值有变化时,调用getZhihuList刷新列表的数据。列表刷新时回调中会调用refreshZhihusData(),此时会改变requestPageDate的值,也就会调用getZhihuList。列表加载更多时回调中会调用loadNextPageZhihu(int positon, Context context) ,也会改变requestPageDate的值,同样会调用getZhihuList。

接下来是DataRepository,数据源管理类,对应mvp的m层。

通过一个类注入数据源对象

1
2
3
4
5
6
public class Injection {
public static DataRepository getDataRepository(Application application) {
return DataRepository.getInstance(RemoteDataSource.getInstance(),
LocalDataSource.getInstance(application), application);
}
}

DataRepository单例类对网络数据源和本地数据源做统一管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class DataRepository {

private final DataSource remoteDataSource;//网络数据源
private final DataSource localDataSource;//本地数据源
private static DataRepository INSTANCE = null;
private static Application application = null;

private DataRepository(@NonNull DataSource remoteDataSource,
@NonNull DataSource localDataSource) {
this.remoteDataSource = remoteDataSource;
this.localDataSource = localDataSource;
}

static DataRepository getInstance(@NonNull DataSource remoteDataSource,
@NonNull DataSource localDataSource,
Application app) {
if (INSTANCE == null) {
synchronized (DataRepository.class) {
if (INSTANCE == null) {
INSTANCE = new DataRepository(remoteDataSource, localDataSource);
application = app;
}
}
}
return INSTANCE;
}

//获取知乎列表的数据
public LiveData<List<ZhihuStory>> getZhihuList(@NonNull String date) {
//联网时使用网络数据源
if (Util.isNetworkConnected(application.getApplicationContext())) {
//第一页获取最近的知乎资讯列表
if (date.equals("today")) {
return remoteDataSource.getLastZhihuList();
}
//获取更多知乎资讯
else {
return remoteDataSource.getMoreZhihuList();
}
}
//断网时使用本地数据源
else {
if (date.equals("today")) {
return localDataSource.getLastZhihuList();
} else {
return localDataSource.getMoreZhihuList();
}
}
}

//加载状态
public LiveData<Boolean> isLoadingZhihuList() {
//联网时返回网络数据源加载状态
if (Util.isNetworkConnected(application.getApplicationContext())) {
return remoteDataSource.isLoadingZhihuList();
}
//断网时返回本地数据源加载状态
else {
return localDataSource.isLoadingZhihuList();
}
}
}

判断网络状态工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Util {
public static boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
}

网络数据源和本地数据源的统一接口

1
2
3
4
5
public interface DataSource {
LiveData<List<ZhihuStory>> getLastZhihuList();//加载最近的资讯列表
LiveData<List<ZhihuStory>> getMoreZhihuList();//列表加载更多资讯
LiveData<Boolean> isLoadingZhihuList();//获取加载状态
}

网络数据源单例类对网络请求做统一管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class RemoteDataSource  implements DataSource {
private static RemoteDataSource INSTANCE = null;
private final MutableLiveData<Boolean> isLoadingZhihulist; //列表加载标记
private final MutableLiveData<List<ZhihuStory>> zhihuList; //列表数据
private String requestDate;//请求日期
private final ApiZhihu apiZhihu;//请求api对象

{
isLoadingZhihulist = new MutableLiveData<>();
zhihuList = new MutableLiveData<>();
}

private RemoteDataSource() {
apiZhihu = ApiManager.getInstance().getApiZhihu();
}

public static RemoteDataSource getInstance() {
if (INSTANCE == null) {
synchronized (RemoteDataSource.class) {
if (INSTANCE == null) {
INSTANCE = new RemoteDataSource();
}
}
}
return INSTANCE;
}

@Override
public LiveData<List<ZhihuStory>> getLastZhihuList() {
isLoadingZhihulist.setValue(true);//加载开始
apiZhihu.getLatestNews()
.enqueue(new Callback<ZhihuData>() {
@Override
public void onResponse(Call<ZhihuData> call, Response<ZhihuData> response) {
if (response.isSuccessful()) {
zhihuList.setValue(response.body().getStories());
refreshLocalZhihuList(response.body().getStories());
requestDate = response.body().getDate();
}
isLoadingZhihulist.setValue(false);//加载结束
}

@Override
public void onFailure(Call<ZhihuData> call, Throwable t) {
isLoadingZhihulist.setValue(false);//加载结束
}
});
return zhihuList;
}

@Override
public LiveData<List<ZhihuStory>> getMoreZhihuList() {
isLoadingZhihulist.setValue(true);//加载开始
apiZhihu.getTheDaily(requestDate)
.enqueue(new Callback<ZhihuData>() {
@Override
public void onResponse(Call<ZhihuData> call, Response<ZhihuData> response) {
if (response.isSuccessful()) {
zhihuList.setValue(response.body().getStories());
refreshLocalZhihuList(response.body().getStories());
requestDate = response.body().getDate();
}
isLoadingZhihulist.setValue(false);//加载结束
}

@Override
public void onFailure(Call<ZhihuData> call, Throwable t) {
isLoadingZhihulist.setValue(false);//加载结束
}
});
return zhihuList;
}

//返回加载状态
@Override
public MutableLiveData<Boolean> isLoadingZhihuList() {
return isLoadingZhihulist;
}

//刷新本地数据
private void refreshLocalZhihuList(List<ZhihuStory> zhihuStoryList) {
if (zhihuStoryList == null || zhihuStoryList.isEmpty()) {
return;
}
AppDatabaseManager.getInstance().insertZhihuList(zhihuStoryList);
}
}

每次网络请求后,都需要刷新本地数据源的数据,ApiManager对api做统一管理,提供获取Retrofit网络请求对象的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ApiManager {
//全局url
private static final String ZHIHU_URL = "https://news-at.zhihu.com/";

private static ApiManager INSTANCE;
//Retrofit网络请求对象
private static ApiZhihu apiZhihu;

private ApiManager() {
}

public static ApiManager getInstance() {
if (INSTANCE == null) {
synchronized (ApiManager.class) {
if (INSTANCE == null) {
INSTANCE = new ApiManager();
}
}
}
return INSTANCE;
}

//获取Retrofit对象,为空时初始化实例
public ApiZhihu getApiZhihu() {
if (apiZhihu == null) {
synchronized (ApiManager.class) {
if (apiZhihu == null) {
apiZhihu = new Retrofit.Builder()
.baseUrl(ZHIHU_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiZhihu.class);
}
}
}
return apiZhihu;
}
}

定义Retrofit网络请求接口,代码如下:

1
2
3
4
5
6
7
public interface ApiZhihu {
@GET("api/4/news/latest")
Call<ZhihuData> getLatestNews();

@GET("/api/4/news/before/{date}")
Call<ZhihuData> getTheDaily(@Path("date") String date);
}

本地数据源单例类做统一管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class LocalDataSource implements DataSource {

private static LocalDataSource INSTANCE = null;

private LocalDataSource(Context context) {
AppDatabaseManager.getInstance().createDB(context);
}

public static LocalDataSource getInstance(Context context) {
if (INSTANCE == null) {
synchronized (LocalDataSource.class) {
if (INSTANCE == null) {
INSTANCE = new LocalDataSource(context);
}
}
}
return INSTANCE;
}

@Override
public LiveData<List<ZhihuStory>> getLastZhihuList() {
return AppDatabaseManager.getInstance().loadZhihuList();
}

@Override
public LiveData<List<ZhihuStory>> getMoreZhihuList() {
return null;
}


@Override
public LiveData<Boolean> isLoadingZhihuList() {
return AppDatabaseManager.getInstance().isLoadingZhihuList();
}
}

AppDatabaseManager单例类是数据库的管理工具类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class AppDatabaseManager {
private static final String DATABASE_NAME = "google-architecture-study-db";
private final MutableLiveData<Boolean> mIsLoadingZhihuList;
private final MutableLiveData<List<ZhihuStory>> mZhihuList;
private static AppDatabaseManager INSTANCE = null;
private AppDatabase mDB = null;

{
mIsLoadingZhihuList = new MutableLiveData<>();
mZhihuList = new MutableLiveData<>();
}

private AppDatabaseManager() {
}

public static AppDatabaseManager getInstance() {
if (INSTANCE == null) {
synchronized (AppDatabaseManager.class) {
if (INSTANCE == null) {
INSTANCE = new AppDatabaseManager();
}
}
}
return INSTANCE;
}

//创建数据库
public void createDB(Context context) {
new AsyncTask<Context, Void, Void>() {
@Override
protected Void doInBackground(Context... params) {
Context context = params[0].getApplicationContext();
mDB = Room.databaseBuilder(context,
AppDatabase.class, DATABASE_NAME).build();
return null;
}
}.execute(context.getApplicationContext());
}

//刷新本地知乎列表数据
public void insertZhihuList(final List<ZhihuStory> zhihuStoryList) {
new AsyncTask<Void, Void, Void>(){
@Override
protected Void doInBackground(Void... voids) {
mDB.beginTransaction();
try {
mDB.zhihuDao().insertZhihuList(zhihuStoryList);
mDB.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
mDB.endTransaction();
}
return null;
}
}.execute();
}

//知乎列表数据查询
public LiveData<List<ZhihuStory>> loadZhihuList() {
mIsLoadingZhihuList.setValue(true);
new AsyncTask<Void, Void, List<ZhihuStory>>() {
@Override
protected List<ZhihuStory> doInBackground(Void... voids) {
List<ZhihuStory> results = new ArrayList<>();
mDB.beginTransaction();
try {
results.addAll(mDB.zhihuDao().loadAllZhihus());
mDB.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
mDB.endTransaction();
}
return results;
}

@Override
protected void onPostExecute(List<ZhihuStory> aVoid) {
super.onPostExecute(aVoid);
mIsLoadingZhihuList.setValue(false);
mZhihuList.setValue(aVoid);
}

@Override
protected void onCancelled(List<ZhihuStory> aVoid) {
super.onCancelled(aVoid);
mIsLoadingZhihuList.setValue(false);
}
}.execute();
return mZhihuList;
}

//获取加载状态
public MutableLiveData<Boolean> isLoadingZhihuList() {
return mIsLoadingZhihuList;
}
}

初始化数据库room

1
2
3
4
5
@Database(entities = {ZhihuStory.class}, version = 1, exportSchema = false)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract ZhihuDao zhihuDao();
}

定义数据库room相关接口

1
2
3
4
5
6
7
8
@Dao
public interface ZhihuDao {
@Query("SELECT * FROM zhihustorys")
List<ZhihuStory> loadAllZhihus();

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertZhihuList(List<ZhihuStory> zhihuStories);
}

时间字符串拼接与切割转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Converters {
@TypeConverter
public static String fromTimestamp(String[] arrays) {
StringBuilder stringBuilder = new StringBuilder();
for (String s : arrays) {
stringBuilder.append(s).append(",");
}
int length = stringBuilder.toString().length();
if (length > 0) {
stringBuilder = stringBuilder.deleteCharAt(length - 1);
}
return stringBuilder.toString();
}

@TypeConverter
public static String[] dateToTimestamp(String string) {
return string.split(",");
}
}

涉及到的dto,知乎资讯内层dto,需要增加一些注解,这里对应数据库的zhihustorys的表,主键为id,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@Entity(tableName = "zhihustorys")
public class ZhihuStory {

@PrimaryKey
private String id;

private int type;

private String ga_prefix;

private String title;

private String[] images;

public ZhihuStory() {
}

public ZhihuStory(ZhihuStory zhihuStory) {
this.id = zhihuStory.getId();
this.type = zhihuStory.getType();
this.ga_prefix = zhihuStory.getGa_prefix();
this.title = zhihuStory.getTitle();
this.images = zhihuStory.getImages();
}

public String[] getImages() {
return images;
}

public void setImages(String[] images) {
this.images = images;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public int getType() {
return type;
}

public void setType(int type) {
this.type = type;
}

public String getGa_prefix() {
return ga_prefix;
}

public void setGa_prefix(String ga_prefix) {
this.ga_prefix = ga_prefix;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

知乎资讯外层dto,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ZhihuData {
private String date;

private List<ZhihuStory> stories;

private List<ZhihuStory> top_stories;

public String getDate() {
return date;
}

public void setDate(String date) {
this.date = date;
}

public List<ZhihuStory> getStories() {
return stories;
}

public void setStories(List<ZhihuStory> stories) {
this.stories = stories;
}

public List<ZhihuStory> getTop_stories() {
return top_stories;
}

public void setTop_stories(List<ZhihuStory> top_stories) {
this.top_stories = top_stories;
}
}

以上是基于Architecture Components 架构的展示知乎资讯列表的所有代码。

总结:Architecture Components的架构总体上看起来还是很像MVP+Rxjava,M层对应DataRepository,V层对应activity,P层对应AndroidViewModel。AndroidViewModel中注册了请求参数改变的监听,在回调中发起数据请求,V层调用P层是通过AndroidViewModel的方法修改对应的请求参数。activity中注册了AndroidViewModel中的列表数据改变监听,在回调中刷新列表控件,P层得到结果刷新V层,也是通过改变AndroidViewModel中的列表数据来触发回调刷新。所以P层和V层都没有抽象出接口。M层通过统一的数据源管理类分别管理网络数据源和本地数据源。网络数据源通过Retrofit做网络请求,本地数据源主要通过数据库存储实现,定义了一个room数据库工具类做统一管理。个人觉得就目前的版本而言,如果原本就是MVP+Rxjava这种架构的完全可以不用重构了。