现在主流都是kotlin 了吧,毕竟 kotlin 是谷歌的亲儿子。谷歌在这方面都是非常鸡贼的,
当年 HttpClient 和httpconnection 之争,硬是最后 httpconnection 胜出,但最终败于okhttp,不过鸡贼的谷歌,迟早弄掉okhttp的, RXJAVA 不就被谷歌用livedata 弄掉了嘛。再来从新走一轮 paging3的流程:
以WanAndroid的接口为例,接口地址为:https://www.wanandroid.com/article/list/1/json,数据源的代码如下。
先定义API:
public interface WanAndroidApi {
@GET("article/list/{page}/json")
Call<ArticleBean> getArticleBean(@Path("page") int page);
}
再定义 RetrofitClient ,这个简单,不复述了。
定义一个Paging3DataSource:
public class Paging3DataSource extends PagingSource<Integer, DatasBean> {
@Nullable
@Override
public Object load(@NonNull LoadParams<Integer> loadParams, @NonNull Continuation<? super LoadResult<Integer, DatasBean>> continuation) {
int page = 0;
if (loadParams.getKey() != null) {
page = loadParams.getKey();
}
//获取网络数据
ArticleBean result = getbean(page);
//须要加载的数据
List<DatasBean> datas = result.getData().getDatas();
//如果能够往上加载更多就设置该参数,否则不设置
String prevKey = null;
//加载下一页的key 如果传null就阐明到底了
int nextKey = 0;
if (result.getData().getCurPage() == result.getData().getPageCount()) {
nextKey = 0;
} else {
nextKey = page + 1;
}
return new LoadResult.Page<>(datas, prevKey, nextKey);
}
public ArticleBean getbean(int page) {
//这里主要是使用了future.get() 阻塞了线程,直到有结果才返回,避免为空。
Call<ArticleBean> observable = RetrofitClient.getInstance().createApi(WanAndroidApi.class).getA(page);
ArticleBean bean = ExecutorUtil.submit(new Callable<ArticleBean>() {
@Override
public ArticleBean call() throws Exception {
return observable.execute().body();
}
});
return bean;
}
}
ExecutorUtil工具类:
public class ExecutorUtil {
private static ExecutorService executor = Executors.newCachedThreadPool();
public static void execute(Runnable runnable) {
executor.execute(runnable);
}
public static <T> T submit(Callable<T> callable) {
Future<T> future = executor.submit(callable);
try {
return future.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
后面是标准流程了。
定义Paging3ViewModel :
public class Paging3ViewModel extends ViewModel {
PagingConfig pagingConfig = new PagingConfig(20, 3);
public LiveData<PagingData<DatasBean>> getArticleData() {
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(this);
Pager<Integer, DatasBean> pager = new Pager<>(pagingConfig, () -> new Paging3DataSource());
LiveData<PagingData<DatasBean>> cachedResult = PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);
return cachedResult;
}
}
定义Paging3Adapter:
public class Paging3Adapter extends PagingDataAdapter<DatasBean, Paging3Adapter.ViewHolder> {
public Paging3Adapter() {
super(itemCallback);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycleview, parent, false);
return new Paging3Adapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
DatasBean bean = getItem(position);
if (bean != null) {
holder.desc.setText(bean.getDesc());
holder.time.setText(String.valueOf(bean.getPublishTime()));
holder.type.setText(bean.getType()+"-");
holder.auth.setText(bean.getAuthor());
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Loge.e(bean.toString());
}
});
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView desc;
TextView time;
TextView type;
TextView auth;
public ViewHolder(View itemView) {
super(itemView);
desc = itemView.findViewById(R.id.desc);
time = itemView.findViewById(R.id.time);
type = itemView.findViewById(R.id.type);
auth = itemView.findViewById(R.id.auth);
}
}
public static DiffUtil.ItemCallback<DatasBean> itemCallback = new DiffUtil.ItemCallback<DatasBean>() {
@Override
public boolean areItemsTheSame(@NonNull DatasBean oldItem, @NonNull DatasBean newItem) {
return oldItem.getId() == newItem.getId();
}
@SuppressLint("DiffUtilEquals")
@Override
public boolean areContentsTheSame(@NonNull DatasBean oldItem, @NonNull DatasBean newItem) {
return oldItem.equals(newItem);
}
};
}
最后是fragment 使用:
public class Paging3_Fragment extends BaseFragment<Paging3FragmentBinding> {
private final String TAG = "MainActivity";
private Paging3Adapter adapter;
@Override
protected int getLayoutId() {
return R.layout.paging3_fragment;
}
@Override
protected void initView() {
initRecyclerView();
}
private void initRecyclerView() {
adapter = new Paging3Adapter();
binding.recyclerViewPaging3Test6.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
binding.recyclerViewPaging3Test6.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerViewPaging3Test6.setAdapter(adapter);
Paging3ViewModel viewModel = new ViewModelProvider(this).get(Paging3ViewModel.class);
viewModel.getArticleData().observe(this, pagingData -> adapter.submitData(getLifecycle(), pagingData));
}
@Override
protected void initData() {
}
}
其实上文参考了,https://lequ7.com/guan-yu-javaandroidjetpack-jia-gou-zu-jian-jiu-zhi-paging.html 作者,但是他没有给出 Paging3DataSource 获取数据的实现,主要是子线程获取数据,到主线程那里。我还找到了一个其他的实现方法。改写一个
/**
* 实现可以直接返回数据类的核心类
*/
public class DirectCallAdapterFactory extends CallAdapter.Factory {
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
final Type responseType = getResponseType(returnType);
return new CallAdapter<Object, Object>() {
public Type responseType() {
return responseType;
}
public Object adapt(Call<Object> call) {
// todo 可以在这里判断接口数据格式
// Loge.e(Thread.currentThread().getName());
// return call.execute().body();
return ExecutorUtil.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
return call.execute().body();
}
});
}
};
}
private Type getResponseType(Type type) {
if (type instanceof WildcardType) {
return ((WildcardType) type).getUpperBounds()[0];
}
return type;
}
}
在RetrofitClient上增加一个转换即可:
public RetrofitClient() {
mRetrofit=new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(new DirectCallAdapterFactory()) //重点是这里,
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.client(getClient())
.build();
}
另外 API,也改一下,直接返回 实体bean 即可:
public interface WanAndroidApi {
@GET(“article/list/{page}/json”)
Call getA(@Path(“page”) int page);
@GET("article/list/{page}/json")
ArticleBean getArticleBean(@Path("page") int page);
}
这样取数据的时候,就可以直接的使用了
@Override
protected void initData() {
ArticleBean articleBean=RetrofitClient.getInstance().createApi(WanAndroidApi.class).getArticleBean(0);
Loge.e(articleBean.toString());
}
直接返回实体bean也很方便.
上面的 Paging3DataSource是不完善的,下面补充一个完善的Paging3DataSource实现:
public class Paging3DataSource_03 extends ListenableFuturePagingSource<Integer, DatasBean> {
private ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
@NonNull
@Override
public ListenableFuture<LoadResult<Integer, DatasBean>> loadFuture(@NonNull LoadParams<Integer> loadParams) {
Integer nextPageNumber = loadParams.getKey();
if (nextPageNumber == null) {
nextPageNumber = 0;//从第0页开始加载
}
Integer finalNextPageNumber = nextPageNumber;
ListenableFuture<LoadResult<Integer, DatasBean>> listenableFuture = Futures.transform(executorService.submit(new Callable<List<DatasBean>>() {
@Override
public List<DatasBean> call() throws Exception {
Loge.e(Thread.currentThread().getName() + ":当前线程");
Call<ArticleBean> call = RetrofitClient.getInstance().createApi(WanAndroidApi.class).getA(finalNextPageNumber);
ArticleBean articleBean = call.execute().body();
return articleBean.getData().getDatas();
}
}), new Function<List<DatasBean>, LoadResult<Integer, DatasBean>>() {
@Nullable
@Override
public LoadResult<Integer, DatasBean> apply(@Nullable List<DatasBean> input) {
return new LoadResult.Page<>(input, finalNextPageNumber == 0 ? null : finalNextPageNumber - 1, input.isEmpty() ? null : finalNextPageNumber + 1);
}
}, executorService);
ListenableFuture<LoadResult<Integer, DatasBean>> partialLoadResultFuture = Futures.catching(
listenableFuture, Exception.class,
LoadResult.Error::new, executorService);
return Futures.catching(partialLoadResultFuture,
Exception.class, LoadResult.Error::new, executorService);
}
}