本文使用JetPack的ViewModel,LiveData, DataBinding, Room, Paging等技术实践。
内容如下:
一. 一两句话和一两个图总结 LiveData作用、ViewModel作用、DataBinding作用
二、Demo实践
1. 准备、Demo效果和Demo结构、开发步骤
2. Paging2 + LiveData + ViewModel + Room 分页 加载本地数据实践(Java实现)
3. Paging2 + LiveData+ ViewModel + Retrofit 分页 加载网络数据实践(Java实现)
4. Paging3 + RxJava + ViewModel + Retrofit 分页加载网络数据实践(Java实现)
一. 一两句话和一两个图总结 LiveData作用、ViewModel作用、DataBinding作用:
LiveData作用
(1)实际上就是一个观察者模式的数据容器,当数据改变时,通知UI刷新;
(2)数据LiveData<T>是观察者,组件activity, fragment是被观察者;
(3)LiveData能感知activity, fragment组件的生命周期,当activity,fragment销毁时,会自动清除引用,不必担心内存泄漏。而其他观察者回调的库需要自己管理生命周期。
(4)当activity或者fragment处于活动状态时如started,resumed,liveData才会通知。
(5)LiveData通常和ViewModel一起使用, LiveData<T>作为ViewModel的一个成员变量。
LiveData原理总结:https://blog.csdn.net/xiaobaaidaba123/article/details/123030339
ViewModel作用
(1)将activity, fragment里关于数据操作的逻辑抽离出来,封装到ViewModel中,所以ViewMoel 持有一个成员变量LiveData<T>。
(2)数据的操作包括什么呢? a. 从DB和缓存读取数据,显示到UI; b. 通过网络到后台拉取数据,持久化到本地,更新DB和缓存,通知UI刷新。
(3)因此ViewModel 应该持有一个 成员变量Repository(相当于一个管理类, 命名可以命名为其他如XXXManager),做(2)的事情。 而组件activity, fragment应该持有一个成员变量ViewModel , 如图所示
图片来源LiveData + ViewModel + Room (Google 官文)+Demo - 简书
(4)ViewModel的生命周期如图, 可以看出 横竖屏切换,activity onDestroy后重新onCreate, ViewModel 还是原来的对象,没有被重新创建。
图片来源https://developer.android.com/topic/libraries/architecture/viewmodel
(5)ViewModel 不能持有activity, fragment的引用,否则会导致内存泄漏, ViewMode中如果要使用context,我们通常定义XXXViewModel 继承 基类AndroidViewModel就好了。
(6)ViewModel可以用于fragment之间通信。
Databinding作用
(1) 和UI双向绑定
(2) 在build.gradle 的android下 声明
dataBinding {
enabled = true
}
后, 在XML布局文件定义一个variable来引用我们的数据实体类。如:
<variable name="poetry" type="com.mikel.projectdemo.jetpack.service.model.Poetry"/>
在XML具体的控件可以直接访问数据里的字段 如
android:text="@{poetry.title}"
(3)Android Studio 会根据XML的文件名生成一个Binding类,如
fragment_poetry_detail.xml ----- > FragmentPoetryDetailBindingImpl
同时也生成相应的setXXX方法, 如在xml定义的variable变量:
variable name = "poetry" ----> setPoetry()------> 完成了Poetry和UI绑定
(4) binding调用setXXX方法 实现数据同步到UI
private void observeViewModelLocal(final PoetryViewModel viewModel) {
viewModel.getmPoetryObservableLocal().observe(this, new Observer<Poetry>() {
@Override
public void onChanged(Poetry poetry) {
Utils.printPoetryInfo(poetry);//打日志
if (poetry != null) {
mFragmentPoetryDetailBinding.setIsLoading(false);// binding调用setXXX方法后,数据同步到UI
mFragmentPoetryDetailBinding.setPoetry(poetry);// binding调用setXXX方法后,数据同步到UI
}
}
});
}
------------------------------------------------二 Demo实践------------------------------------------------
2.1. 准备、Demo效果和Demo结构、开发步骤
2.1.1 准备
用到开源的api如下:
获取唐朝古诗词:
https://api.apiopen.top/getTangPoetry?page=1&count=20
来源免费开放接口API(转载至有梦想的程序丶猿)_php_M的博客-CSDN博客
但是最近该免费API 由于被大量使用暂时失效了,本Demo采用模拟网络服务返回的方式,同样达到想要的网络请求-响应效果。
2.1.2 Demo效果和Demo结构:
2.1.3 开发步骤
1)在Build.gradle中添加依赖
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.mikel.projectdemo"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
/**
* 使用databinding
*/
buildFeatures {
dataBinding = true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
//添加okhttp依赖
implementation 'com.squareup.okhttp3:okhttp:4.4.0'
//添加retrofit依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'//支持RxJavaCallAdapterFactory
//添加rxjava2依赖
implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//paging2
// implementation "androidx.paging:paging-runtime:2.1.0"
//paging3
implementation "androidx.paging:paging-runtime:3.0.1"
implementation "androidx.paging:paging-rxjava2:3.0.1"
//添加room依赖
// add for room
implementation "android.arch.persistence.room:runtime:1.1.1"
// room 配合 RxJava
implementation "android.arch.persistence.room:rxjava2:1.1.1"
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
//ui recyclerView依赖添加
implementation 'androidx.recyclerview:recyclerview:1.2.0'
}
2)创建实体类Poetry
Portry.java:
/**
* 实体类—唐诗
*/
@Entity(tableName = "poetry_table") //数据库表名poetry_table
public class Poetry {
@NonNull
@ColumnInfo
@PrimaryKey(autoGenerate = true) //主键
public int poetry_id;
@NonNull
@ColumnInfo
public String title; //标题
@NonNull
@ColumnInfo //列
public String content; //内容
@NonNull
@ColumnInfo //列
public String authors; // 作者
public Poetry() {
}
@Override
public String toString() {
return "Poetry:" +
"id=" + poetry_id +
"title =" + title +
", content =" + content +
", author =" + authors;
}
public int getPoetry_id() {
return poetry_id;
}
public void setPoetry_id(int poetry_id) {
this.poetry_id = poetry_id;
}
@NonNull
public String getTitle() {
return title;
}
public void setTitle(@NonNull String title) {
this.title = title;
}
@NonNull
public String getContent() {
return content;
}
public void setContent(@NonNull String content) {
this.content = content;
}
@NonNull
public String getAuthors() {
return authors;
}
public void setAuthors(@NonNull String authors) {
this.authors = authors;
}
}
3) 数据库层使用room框架
a. 实体类 Poetry 使用@Entity标注,并且指定数据库的表名 poetry_table;
poetry_id作为主键用@PrimaryKey标注;
title, content和authors作为数据库表的列用@ColumnInfo标注。
b. 定义DAO类,主要负责封装 增删改查 数据库的接口。
注意:Poetry接口类需要用@Dao标注 ,每一个接口使用@Query + SQL查询语句标注
PoetryDao.java:
@Dao // DAO接口
public interface PoetryDao {
/**
* 查询所有
* @return
*/
@Query("SELECT * FROM poetry_table")
List<Poetry> getAllPoetries();
/**
* 分页查询数据库
* @param startId
* @param size
* @return
*/
@Query("SELECT * FROM poetry_table LIMIT :startId, :size")
List<Poetry> getSpecificPoetries(int startId, int size);
@Query("SELECT * FROM poetry_table WHERE title = :name")
Poetry getPoetryByName(String name);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<Poetry> poetries);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Poetry poetry);
@Query("DELETE FROM poetry_table")
void deleteAll();
}
c. 定义继承RoomDatabase的AppDatabase,负责初始化数据库对象。
注意:当后续要更改数据库字段的时候,需要实现方法migrate()
AppDatabase.java:
@Database(entities = {Poetry.class, RemoteKey.class}, version = 1, exportSchema = false) // 声明版本号1
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract PoetryDao poetryDao();
public abstract RemoteKeyDao remoteKeyDao();
public static AppDatabase getInstance(Context context) {
if(INSTANCE == null) {//单例设计模式 双重校验
synchronized (AppDatabase.class) {
if(INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class,
"mikel_room.db").
addMigrations(AppDatabase.MIGRATION_1_2).// 修改数据库字段时更新数据库
allowMainThreadQueries().//允许在主线程读取数据库
build();
}
}
}
return INSTANCE;
}
/**
* 数据库变动添加Migration
*/
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
// todo 如果有修改数据库的字段 可以使用database.execSQL() 同时 修改数据库的version
}
};
}
总结a , b, c 步骤描述了room框架的使用。
4)绑定使用DataBinding,异步通信使用 ViewModel + LiveData
首先需要在build.gradle的android下 添加
dataBinding {
enabled = true
}
以详情页的 布局文件为例 fragment_poetry_detail.xml ,其对应一个ViewModel类 PoetryViewModel
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<!--databinding 使用isLoading 代表一个布尔变量-->
<variable name="isLoading" type="boolean" />
<!--databinding 使用poetry可以直接访问Poetry里的字段-->
<variable name="poetry" type="com.mikel.projectdemo.jetpack.service.model.Poetry"/>
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/loadingTips"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:text="loading......"
android:textAlignment="center"
app:visibleGone="@{isLoading}"/>
<LinearLayout
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:padding="5dp"
android:paddingTop="16dp"
android:orientation="vertical"
app:visibleGone="@{!isLoading}">
<ImageView
android:id="@+id/imageView"
android:layout_width="150dp"
android:layout_height="125dp"
android:src="@drawable/image" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="25sp"
android:text="@{poetry.title}"
android:textAlignment="center"
android:paddingBottom="5dp"
android:gravity="center_horizontal" />
<TextView
android:id="@+id/project_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="@{poetry.content}"/>
<TextView
android:id="@+id/languages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:text="@{poetry.authors}"/>
</LinearLayout>
</FrameLayout>
</ScrollView>
</layout>
如上图,xml布局代码所示,data标签下
<variable name="poetry" type="com.mikel.projectdemo.jetpack.service.model.Poetry"/>
意思是:使用poetry可以直接访问实体类Poetry里的字段, 如android:text="@{poetry.title}"
涉及到数据的操作逻辑应该都封装到ViewModel中 , 随着项目代码逻辑增多,数据操作的逻辑还可以封装到数据仓库类 可以命名为XXXManager。
PoetryViewModel.java类如下:
/**
* 使用同一个obserable监听 只会响应最后的一个
* 这里区分本地的和network的
*/
// MutableLiveData继承liveData, 提供了setValue和postValue方法。liveData 是抽象类
private MutableLiveData<Poetry> mPoetryObservableLoacl= new MutableLiveData<>();
private MutableLiveData<Poetry> mPoetryObservableNetwork = new MutableLiveData<>();
private final String poetryID;
public PoetryViewModel(Application application, String poetryID) {
super(application);
this.poetryID = poetryID;
}
/**
* 读DB或者缓存
* 根据具体的名字获取Poetry
*/
public Poetry loadDataInfo(String name) {
Utils.printMsg(" poetry detail load info");
Poetry poetry = AppDatabase.getInstance(this.getApplication()).poetryDao().getPoetryByName(name);
mPoetryObservableLoacl.setValue(poetry);// setValue 需要在主进程调用
return poetry;
}
/**
* 发送网络请求
* 真实的请求逻辑封装到了RetrofitManager
* 根据名称搜索某个poetry
* @param name
*/
public void requestSearchPoetry(String name) {
mPoetryObservableNetwork = RetrofitManager.getInstance(this.getApplication()).searchPoetry(name);
}
/**
* 返回LiveData 对象
* @return
*/
public LiveData<Poetry> getmPoetryObservableNetwork() {
return mPoetryObservableNetwork;
}
public LiveData<Poetry> getmPoetryObservableLocal() {
return mPoetryObservableLoacl;
}
public static class Factory extends ViewModelProvider.NewInstanceFactory {
@NonNull
private final Application application;
private final String poetryID;
public Factory(@NonNull Application application, String poetryID) {
this.application = application;
this.poetryID = poetryID;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
//noinspection unchecked
return (T) new PoetryViewModel(application, poetryID);
}
}
}
这里遇到了一个问题, 如果同一个MutableLiveData,读本地数据后setValue 和 网络回包setValue, Observer.onChanged只响应最后setValue的那一次。 所以这里区分
private MutableLiveData<Poetry> mPoetryObservableLocal= new MutableLiveData<>();
private MutableLiveData<Poetry> mPoetryObservableNetwork = new MutableLiveData<>();
5)网络层使用Retrofit框架
a. 请求接口的定义
public interface PagingNetWorkApiService {
public static String HTTPS_API_OPEN_URL = "https://api.apiopen.top/";
//获取唐诗列表Rxjava
@GET("getTangPoetry")
Observable<ResultList<Poetry>> fetchPoetryListForRxJava(@Query("page") String page, @Query("count") String count);
@GET("getTangPoetry")
Single<ResultList<Poetry>> fetchPoetryListForPaging3(@Query("page") String page, @Query("count") String count);
//根据名称搜索唐诗
@GET("searchPoetry")
Observable<ResultList<Poetry>> searchPoetry(@Query("name") String name);
}
? 后面带有参数 使用@Query, 如果参数是在URL路径中, 使用 @Path
Retrofit可以和Rxjava结合 返回Observable对象,也能和LiveData结合返回LiveData对象
后文分别介绍
Paging2 + LiveData + ViewModel + Room 分页加载本地数据(Java实现)
Paging2 + LiveData + ViewModel + Retrofit 分页加载网络数据(Java实现)
Paging3 + RxJava+ ViewModel + Retrofit 分页加载网络数据(Java实现)
2.2 Paging2 + LiveData + ViewModel + Room 分页 加载本地数据实践(Java实现)
网络上有不少帖子使用Paging框架分页加载本地数据,但是并不是真正的分页,而是一次性加载数据库的数据后,仅仅是在UI上进行分页。
笔者将从 逻辑+UI 分页加载本地数据库。
DAO关键查询接口:(传递参数startId:起始item项id,和size :分页大小)
/**
* 分页查询数据库
* @param startId
* @param size
* @return
*/
@Query("SELECT * FROM poetry_table LIMIT :startId, :size")
List<Poetry> getSpecificPoetries(int startId, int size);
Paging2本地分页数据源LocalPageKeyedDataSource 实现
public class LocalPageKeyedDataSource extends PageKeyedDataSource<Integer, Poetry> {
public static final int PAGE_SIZE = 3;
private PoetryDao mPoetryDao;
public LocalPageKeyedDataSource(PoetryDao poetryDao) {
mPoetryDao = poetryDao;
}
@Override
public void loadInitial(@NonNull @NotNull LoadInitialParams<Integer> params, @NonNull @NotNull LoadInitialCallback<Integer, Poetry> callback) {
/**
* 初始化只从数据库查首页 pageKey = 0, 下一页pageKey = 1
*/
List<Poetry> firstPageList = mPoetryDao.getSpecificPoetries(0, PAGE_SIZE);
Log.w(Constants.TAG, " load initial ");
Utils.printPoetryInfo(firstPageList);
callback.onResult(firstPageList, null, 1);
}
@Override
public void loadBefore(@NonNull @NotNull LoadParams<Integer> params, @NonNull @NotNull LoadCallback<Integer, Poetry> callback) {
Log.w(Constants.TAG, " load before param key = " + params.key);
}
@Override
public void loadAfter(@NonNull @NotNull LoadParams<Integer> params, @NonNull @NotNull LoadCallback<Integer, Poetry> callback) {
int startID = params.key * params.requestedLoadSize;
Log.w(Constants.TAG, " load after start id = " + startID + " size= " + params.requestedLoadSize);
/**
* 每次只从数据库里 查一页数据
*/
List<Poetry> dataList = mPoetryDao.getSpecificPoetries(startID, params.requestedLoadSize);
Utils.printPoetryInfo(dataList);
if (dataList != null) {
callback.onResult(dataList, params.key + 1);
}
}
}
viewmodel实现:
public class PoetryListViewModelForPaging2Local extends AndroidViewModel {
PoetryDao mPortryDao;
private LiveData<PagedList<Poetry>> localLiveDataPageListPoetry;
private LocalPageKeyedDataSourceFactory mLocalPageKeyedDataSourceFactory;
public PoetryListViewModelForPaging2Local(Application application) {
super(application);
mPortryDao = AppDatabase.getInstance(this.getApplication()).poetryDao();
//本地数据源
mLocalPageKeyedDataSourceFactory = new LocalPageKeyedDataSourceFactory(mPortryDao);
localLiveDataPageListPoetry = new LivePagedListBuilder<>(mLocalPageKeyedDataSourceFactory,
LocalPageKeyedDataSource.PAGE_SIZE).build();
}
public LiveData<PagedList<Poetry>> getLocalLiveDataPageListPoetry() {
return localLiveDataPageListPoetry;
}
}
2.3. Paging2 + LiveData+ ViewModel + Retrofit 分页 加载网络数据实践(Java实现)
Paging2网络分页数据源NetPagedKeyedDataSource.java实现
public class NetPagedKeyedDataSource extends PageKeyedDataSource<Integer, Poetry> {
public static final int PAGE_SIZE = 3;
private int CUR_PAGE = 0;
private Context mContext;
public NetPagedKeyedDataSource(Context context) {
this.mContext = context;
}
@Override
public void loadInitial(@NonNull @NotNull LoadInitialParams<Integer> params, @NonNull @NotNull LoadInitialCallback<Integer, Poetry> callback) {
Log.w(Constants.TAG, "network load intial ");
CUR_PAGE = 0;
/**
* todo
* 由于网络接口失效,这里构建测试数据 模拟服务端的返回,
* 正常情况下直接使用下面被注释掉的代码 RetrofitManager.getInstance(mContext).fetchPoetryList
*/
// RetrofitManager.getInstance(mContext).fetchPoetryList(String.valueOf(0), String.valueOf(PAGE_SIZE), new RetrofitManager.ReqCallBack() {
// @Override
// public void updateData(List<Poetry> dataList) {
// Utils.printPoetryInfo(dataList);
// callback.onResult(dataList, null, 1);
// }
// });
buildNetTestData(String.valueOf(0), new RetrofitManager.ReqCallBack() {
@Override
public void updateData(List<Poetry> dataList) {
Utils.printPoetryInfo(dataList);
callback.onResult(dataList, null, 1);
}
});
}
@Override
public void loadBefore(@NonNull @NotNull LoadParams<Integer> params, @NonNull @NotNull LoadCallback<Integer, Poetry> callback) {
}
@Override
public void loadAfter(@NonNull @NotNull LoadParams<Integer> params, @NonNull @NotNull LoadCallback<Integer, Poetry> callback) {
CUR_PAGE = params.key;
int startID = params.key * params.requestedLoadSize;
Log.w(Constants.TAG, "network load after start id = " + startID + " size= " + params.requestedLoadSize);
/**
* todo
* 由于网络接口失效,这里构建测试数据 模拟服务端的返回,
* 正常情况下直接使用下面被注释掉的代码 RetrofitManager.getInstance(mContext).fetchPoetryList
*/
// RetrofitManager.getInstance(mContext).fetchPoetryList(String.valueOf(params.key),
// String.valueOf(params.requestedLoadSize), new RetrofitManager.ReqCallBack() {
// @Override
// public void updateData( List<Poetry> dataList) {
// Utils.printPoetryInfo(dataList);
// if (dataList != null) {
// callback.onResult(dataList, params.key + 1);
// }
// }
// });
buildNetTestData(String.valueOf(params.key), new RetrofitManager.ReqCallBack() {
@Override
public void updateData(List<Poetry> dataList) {
Utils.printPoetryInfo(dataList);
if (dataList != null) {
callback.onResult(dataList, params.key + 1);
}
}
});
}
private void buildNetTestData(String page, RetrofitManager.ReqCallBack callback) {
List<Poetry> poetries = new ArrayList<>();
for(int i=0; i<3; i++){
long time = System.currentTimeMillis() + i;
Poetry poetry = new Poetry();
poetry.poetry_id = (int)time;
poetry.title = "NetworkResponse_页" + page + "_no." + i
+"_" + RepositoryManager.CACHE_TITLE[i] + "_" + time;
poetry.content= RepositoryManager.CACHE_CONTENT[i];
poetry.authors= RepositoryManager.CACHE_AUTHOR[i];
poetries.add(poetry);
}
//回调数据
callback.updateData(poetries);
}
}
由于网络接口失效,这里构建测试数据 模拟服务端的请求-响应。
viewmodel实现:
public class PoetryListViewModelForPaging2Network extends AndroidViewModel {
private LiveData<PagedList<Poetry>> netLiveDataPageListPoetry;
private NetPagedKeyedDataSourceFactory mNetPagedKeyedDataSourceFactory;
public PoetryListViewModelForPaging2Network(Application application) {
super(application);
//网络数据源
mNetPagedKeyedDataSourceFactory = new NetPagedKeyedDataSourceFactory(getApplication());
netLiveDataPageListPoetry = new LivePagedListBuilder<>(mNetPagedKeyedDataSourceFactory,
LocalPageKeyedDataSource.PAGE_SIZE).build();
}
public LiveData<PagedList<Poetry>> getNetLiveDataPageListPoetry() {
return netLiveDataPageListPoetry;
}
}
2.4. Paging3 + RxJava + ViewModel + Retrofit 分页加载网络数据实践(Java实现)
Paging3网络分页数据源PoetryListPagingSource.java实现
public class PoetryListPagingSource extends RxPagingSource<Integer, Poetry> {
@NonNull
private PagingNetWorkApiService mPagingNetWorkApiService;
private int nextPageKey = 0;
public PoetryListPagingSource(@NonNull PagingNetWorkApiService pagingNetWorkApiService) {
mPagingNetWorkApiService = pagingNetWorkApiService;
}
@NotNull
@Override
public Single<LoadResult<Integer, Poetry>> loadSingle(
@NotNull LoadParams<Integer> params) {
Integer nextPageNumber = params.getKey();
if (nextPageNumber == null) {
nextPageNumber = 0;
}
nextPageKey = nextPageNumber;
Log.d(Constants.TAG, "Paging3框架,PagingSource loadSingle call, page = " + nextPageKey);
/**
* todo
* 由于网络接口失效,这里构建测试数据 模拟服务端的返回,
* 正常情况下直接使用下面被注释掉的代码mPagingNetWorkApiService.fetchPoetryListForPaging3
*/
// return mPagingNetWorkApiService.fetchPoetryListForPaging3(String.valueOf(nextPageKey),
// String.valueOf(Constants.PAGING_PAGE_SIZE))
// .subscribeOn(Schedulers.io())
// .map(this::toLoadResult)
// .onErrorReturn(LoadResult.Error::new);
ResultList<Poetry> testResultList = buildTestResultList(String.valueOf(nextPageKey));
return Single.just(toLoadResult(testResultList));
}
private LoadResult<Integer, Poetry> toLoadResult(
@NonNull ResultList<Poetry> resultList) {
return new LoadResult.Page<>(
resultList.getData(),
null, // Only paging forward.
nextPageKey + 1,
LoadResult.Page.COUNT_UNDEFINED,
LoadResult.Page.COUNT_UNDEFINED);
}
@Nullable
@Override
public Integer getRefreshKey(@NotNull PagingState<Integer, Poetry> state) {
Integer anchorPosition = state.getAnchorPosition();
if (anchorPosition == null) {
return null;
}
LoadResult.Page<Integer, Poetry> anchorPage = state.closestPageToPosition(anchorPosition);
if (anchorPage == null) {
return null;
}
Integer prevKey = anchorPage.getPrevKey();
if (prevKey != null) {
return prevKey + 1;
}
Integer nextKey = anchorPage.getNextKey();
if (nextKey != null) {
return nextKey - 1;
}
return null;
}
/**
* 模拟服务端返回,构造分页测试数据
* @param page
* @return
*/
private ResultList<Poetry> buildTestResultList(String page) {
ResultList<Poetry> resultList = new ResultList<>();
List<Poetry> poetries = new ArrayList<>();
for(int i=0; i<3; i++){
long time = System.currentTimeMillis() + i;
Poetry poetry = new Poetry();
poetry.poetry_id = (int)time;
poetry.title = "NetworkResponse_第" + page + "页_第" + i
+"条数据_" + RepositoryManager.CACHE_TITLE[i] + "_" + time;
poetry.content= RepositoryManager.CACHE_CONTENT[i];
poetry.authors= RepositoryManager.CACHE_AUTHOR[i];
poetries.add(poetry);
}
resultList.setCode(200);
resultList.setMessage("Test Data Success");
resultList.setResult(poetries);
return resultList;
}
}
由于网络接口失效,这里构建测试数据 模拟服务端的请求-响应。
viewmodel实现:
public class PoetryListViewModelForPaging3 extends ViewModel {
Pager<Integer, Poetry> mPager;
PoetryListPagingSource mPoetryListPagingSource;
//rxjava observable
Observable<PagingData<Poetry>> mPoetryObservable;
public PoetryListViewModelForPaging3(Context context) {
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(this);
/**
* 数据源
*/
mPoetryListPagingSource = new PoetryListPagingSource(RetrofitManager.getInstance(context).getPagingNetWorkApiService());
/**
* Pager :分页大管家, 使用网络数据源构造
*/
mPager = new Pager<Integer, Poetry>(new PagingConfig(Constants.PAGING_PAGE_SIZE), () -> mPoetryListPagingSource);
/**
* PagingRx.getObservable
*/
mPoetryObservable = PagingRx.getObservable(mPager);
PagingRx.cachedIn(mPoetryObservable, viewModelScope);
}
public Observable<PagingData<Poetry>> getPoetryObservable() {
return mPoetryObservable;
}
}
Demo地址:
GitHub - mikelhm/MikelProjectDemo: Personal Android Demo
该工程主要是个人学习和实践 android 技术 的 Demo 集成
- MVVM: ViewModel+LiveData+DataBinding+Retrofit+Room+Paging+RxJava 总结与实践(Java实现)
MVVM: ViewModel+LiveData+DataBinding+Retrofit+Room+Paging+RxJava 总结与实践(Java实现)_xiaobaaidaba123的专栏-CSDN博客 - android 嵌套ViewPager + Fragment实现仿头条UI框架Demo
android 嵌套ViewPager + Fragment实现仿头条UI框架Demo_xiaobaaidaba123的专栏-CSDN博客 - Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放
https://blog.csdn.net/xiaobaaidaba123/article/details/120630087
-------------------------------------------------------------------------------------------------------------------------------
参考:
Android架构组件(二)——LiveData_sd_zhuzhipeng的专栏-CSDN博客 Android架构组件(二)——LiveData
LiveData + ViewModel + Room (Google 官文)+Demo - 简书 LiveData + ViewModel + Room (Google 官文)+Demo
Mvvm模式: Databinding 与 ViewModel+LiveData+Repository - 简书Mvvm模式: Databinding 与 ViewModel+LiveData+Repository
Android DataBinding (一) 基本用法 - 简书 databinding用法