Android Architecture ComponentsAndroid架构组件work

Android 应用架构组件(Architecture Comp

2017-08-10  本文已影响431人  lijiankun24

Architecture Components 是在 2017 年 Google I/O 大会上,Google 官方推出的一个构建 Android 应用架构的库。它可以帮你避免在 Android 应用开发中常见的一些问题,比如:内存泄露,管理组件生命周期等等。本文将介绍如何利用 Architecture Components 库开发一个实际的 Android 应用 ArchitecturePractice,欢迎 fork 和 star。

本文主要分为以下两部分:

  1. 介绍 Architecture Components 库;
  2. 如何使用 Architecture Components 库开发应用。

关于 Architecture Components

应用开发者所面对的问题

在资源有限的移动设备中,在任何时候,系统都有可能为新的应用杀死一些原来的应用,那么在原来应用中的组件(Activity、Fragment、Service等等)也会被销毁,这些组件的生命周期不受开发者控制,而是由系统控制的,所以不要在应用程序组件中存储任何应用数据和状态,并且应用程序组件之间相互不要依赖。

常见的构建原则

如果不可以在应用程序组件中存储应用数据和状态,那么该如何构建应用呢?这儿有两条常见的构建原则:

主要内容

处理生命周期

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

并不是只有 Activity 和 Fragment 才可以实现 LifecycleOwner 接口的,任何和 Activity/Fragment 生命周期有关系的类都可以实现此接口。通过实现此接口,该类完全是生命周期可感知的,只需要对它进行初始化,它就可以进行自己的初始化和清理操作,而不受其 Activity/Fragment 的管理。详细可以参看官方文档说明:LifecycleOwner 实践

LiveData

LiveData 是一个数据持有类,它持有一个值并且该值可以被观察。不同于普通的可观察者,LiveData 遵从应用组件的生命周期,这样 Observer 便可以指定一个其应该遵循的 Lifecycle

如果 Observer 所依附的 Lifecycle 处于 STARTED 或者 RESUMED 状态,则 LiveData 认为 Observer 处于活跃状态。

可以感知组件生命周期的 LiveData 给我们提供了一种可能:可以在多个 ActivityFragment 之间共享它。

使用 LiveData 会有以下几个优势:

有时候,也许想在 LiveData 被下发到 Observer 之前,改变 LiveData 的值,或者是基于当前的 LiveData 下发另一个不同的 LiveData 值。Lifecycle 包中的 Transformations 可以实现这样的功能。

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
private LiveData<User> getUser(String id) {
  ...;
}
LiveData<String> userId = ...;
LiveData<User> 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<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> 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-lifecycle.png
ViewModel vs SavedInstanceState

Room Persistence Library

Room 在 SQLite 之上提供了一个抽象层,以便在利用 SQLite 全部功能的同时也可以流畅发访问数据库。
在 Room 中有非常重要的三个类:

注解的类应该是一个继承了 RoomDatabase 的抽象类。在运行时,可以通过 Room.databaseBuilder() 或者 Room.inMemoryDatabaseBuilder() 方法获取单例。

Room 中的三大组件与应用程序中其他部分的关系如下图所示:

room_architecture.png

关于 Room Persistence Library 更加详细的内容,请参阅 Room Persistence Library 官方说明文档。

Architecture Components 的使用

添加组件到项目

添加 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"

具体应用

假如在项目中有类似于下面知乎列表这样的一个页面:

zhihu_list.jpg

关于该页面有如下两个接口:

  // 请求最新的知乎列表(下拉刷新)
  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 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl_zhihu_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_zhihu"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_zhihu_list"
            android:name="com.lijiankun24.architecturepractice.fragment.GirlFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="LinearLayoutManager"
            tools:context=".ui.fragment.GirlListFragment"
            tools:listitem="@layout/fragment_girl_list_item"/>
    </android.support.v4.widget.SwipeRefreshLayout>

    <!-- ProgressBar颜色更改  http://www.voidcn.com/blog/dongbeitcy/article/p-5781104.html -->
    <ProgressBar
        android:id="@+id/bar_load_more_zhihu"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:indeterminate="true"
        android:indeterminateTint="@color/colorPrimaryDark"
        android:indeterminateTintMode="src_atop"/>
</RelativeLayout>

fragment_zhihu_list.xml 布局文件比较简单,不需要多讲。

ZhihuListFragment.java 代码如下所示:

/**
 * ZhihuListFragment.java
 * <p>
 * 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<ZhihuStory> mZhihuOnItemClickListener =
            new OnItemClickListener<ZhihuStory>() {
                @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<List<ZhihuStory>>() {
            @Override
            public void onChanged(@Nullable List<ZhihuStory> stories) {
                if (stories == null || stories.size() <= 0) {
                    return;
                }
                L.i("size is " + stories.size());
                mAdapter.setStoryList(stories);
            }
        });
        mListViewModel.isLoadingZhihuList().observe(this, new Observer<Boolean>() {
            @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<List<ZhihuStory>>LiveData<Boolean>

ViewModel 控制层

ZhihuListViewModel 类中需要三个 LiveData 类型的属性。LiveData 类型的数据和 RxJava 中的 Observables 工作模式类似,当 LiveData 持有的数据发生变化时,通知观察者。如下面代码所示:

public class ZhihuListViewModel extends AndroidViewModel {

    // 请求接口中查询的日期参数
    private MutableLiveData<String> mZhihuPageDate = new MutableLiveData<>();

    // Zhihu 列表的数据
    private final LiveData<List<ZhihuStory>> mZhihuList;

    // 是否正在进行网络请求的状态参数
    private final LiveData<Boolean> mIsLoadingZhihuList;

    ......

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

    public LiveData<List<ZhihuStory>> getZhihuList() {
        return mZhihuList;
    }

    public LiveData<Boolean> 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 extends ViewModel> T create(Class<T> 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
 * <p>
 * Created by lijiankun on 17/7/30.
 */

public class ZhihuListViewModel extends AndroidViewModel {

    // 请求接口中查询的日期参数
    private MutableLiveData<String> mZhihuPageDate = new MutableLiveData<>();

    // Zhihu 列表的数据
    private final LiveData<List<ZhihuStory>> 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<String, LiveData<List<ZhihuStory>>>() {
            @Override
            public LiveData<List<ZhihuStory>> apply(String input) {
                return mDataRepository.getZhihuList(input);
            }
        });
    }

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

    /**
      * 数据请求状态由 DataRepository 控制,包括下拉刷新和上拉加载更多
      *
      * @return 是否在进行数据请求
      */
    public LiveData<Boolean> 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 extends ViewModel> T create(Class<T> modelClass) {
            return (T) new ZhihuListViewModel(mApplication, mGirlsDataRepository);
        }
    }
}

Model 数据层

正如在 ViewModel 控制层中介绍的,ArchitecturePractice 中所有的数据均由 DataRepository 类中获取。在 DataRepository 中有两个数据源:本地数据库和远端服务器,如果有网,则从服务器获取最新数据,并保存在本地数据库中;如果没有网,则从本地数据库中加载数据并显示。则 DataRepository 的初步实现是这样的:


/**
 * DataRepository.java
 * <p>
 * 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<List<ZhihuStory>> 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<Boolean> isLoadingZhihuList() {
        if (Util.isNetworkConnected(sApplication.getApplicationContext())) {
            return mRemoteDataSource.isLoadingZhihuList();
        } else {
            return mLocalDataSource.isLoadingZhihuList();
        }
    }
}

其中的 DataSource 表示获取数据的抽象层,如下所示:

public interface DataSource {

    ......

    /**
     * Zhihu 相关方法
     */
    LiveData<List<ZhihuStory>> getLastZhihuList();

    LiveData<List<ZhihuStory>> getMoreZhihuList(String date);

    LiveData<Boolean> isLoadingZhihuList();
}

此外还有两个非常重要的类:RemoteDataSourceLocalDataSource,这两个类分别实现了 DataSource 接口。
RemoteDataSource 类代码如下所示,从远端服务器获取数据使用的是 Retrofit,并且对网络请求进行简单封装,由 ApiManager 统一向外提供网络请求接口:


/**
 * RemoteDataSource.java
 * <p>
 * Created by lijiankun on 17/7/7.
 */

public class RemoteDataSource implements DataSource {

    private static RemoteDataSource INSTANCE = null;

    private final MutableLiveData<Boolean> mIsLoadingZhihuList;

    private final MutableLiveData<List<ZhihuStory>> 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<List<ZhihuStory>> getLastZhihuList() {
        mIsLoadingZhihuList.setValue(true);
        mApiZhihu.getLatestNews()
                .enqueue(new Callback<ZhihuData>() {
                    @Override
                    public void onResponse(Call<ZhihuData> call, Response<ZhihuData> 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<ZhihuData> call, Throwable t) {
                        mIsLoadingZhihuList.setValue(false);
                    }
                });
        return mZhihuList;
    }

    @Override
    public LiveData<List<ZhihuStory>> getMoreZhihuList(String date) {
        mIsLoadingZhihuList.setValue(true);
        mApiZhihu.getTheDaily(mZhihuPageDate)
                .enqueue(new Callback<ZhihuData>() {
                    @Override
                    public void onResponse(Call<ZhihuData> call, Response<ZhihuData> 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<ZhihuData> call, Throwable t) {
                        mIsLoadingZhihuList.setValue(false);
                    }
                });
        return mZhihuList;
    }

    @Override
    public MutableLiveData<Boolean> isLoadingZhihuList() {
        return mIsLoadingZhihuList;
    }

    private void refreshLocalZhihuList(List<ZhihuStory> zhihuStoryList) {
        if (zhihuStoryList == null || zhihuStoryList.isEmpty()) {
            return;
        }
        AppDatabaseManager.getInstance().insertZhihuList(zhihuStoryList);
    }
}

LocalDataSource 类代码如下所示,从本地数据库获取数据使用的是 Architecture Components 中的 Room 库,简单封装为 AppDatabaseManager ,向外提供统一的方法:

/**
 * LocalDataSource.java
 * <p>
 * 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<List<ZhihuStory>> getLastZhihuList() {
        return AppDatabaseManager.getInstance().loadZhihuList();
    }

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

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

使用到的 ApiManager 用于统一管理 Retrofit 网络请求,AppDatabaseManager 则是对 Room 数据库的统一管理,关于 Retrofit 和 Room 的使用就不再多说。这样一个向外提供干净可靠 API 的数据源 DataRepository 模块则完成了,DataRepository 主要负责处理数据的获取。


至此,关于 Architecture Components 组件的介绍和实践都全部介绍完毕,本文中用于举例的 ArchitecturePractice 在 GitHub 上,欢迎 star 和 fork,也欢迎通过下面二维码下载 APK 体验,如果有什么问题欢迎指出。我的工作邮箱:jiankunli24@gmail.com

QR.png

参考资料:

Android Architecture Components 官方文档

Google 官方推出应用开发架构指南 -- Hevin

译 Architecture Components 之 Guide to App Architecture -- zly394

上一篇下一篇

猜你喜欢

热点阅读