第五章:paging使用

  • 效果
    在这里插入图片描述

  • 引入

	api 'com.alibaba:fastjson:1.2.70'


    //material组件
    api 'com.google.android.material:material:1.2.0-alpha06'

    //页面刷新组件
    api 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.2'
    api 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.2'

    //paging分页组件
    api 'androidx.paging:paging-runtime:2.1.2'

  • 构建列表布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <com.scwang.smartrefresh.layout.SmartRefreshLayout
            android:id="@+id/refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <com.scwang.smartrefresh.layout.footer.ClassicsFooter
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <com.scwang.smartrefresh.layout.header.ClassicsHeader
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

        </com.scwang.smartrefresh.layout.SmartRefreshLayout>

        <com.paging.study.EmptyView
            android:id="@+id/empty_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="100dp" />
    </FrameLayout>

    <data>

    </data>
</layout>
  • pading 通用配置ViewModel
package com.paging.study.viewmodel;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.paging.DataSource;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;

/**
 * pading 通用配置
 *
 * @param <T>
 */
public abstract class AbsViewModel<T> extends AndroidViewModel {
    protected PagedList.Config config;
    private DataSource dataSource;
    private LiveData<PagedList<T>> pageData;
    /**
     * 分页,当前第一页开始
     */
    protected int pageCurrent = 1;
    private MutableLiveData<Boolean> boundaryPageData = new MutableLiveData<>();

    DataSource.Factory factory = new DataSource.Factory() {
        @NonNull
        @Override
        public DataSource create() {
            if (dataSource == null || dataSource.isInvalid()) {
                dataSource = createDataSource();
            }
            return dataSource;
        }
    };

    public AbsViewModel(@NonNull Application application) {
        super(application);
        config = new PagedList.Config.Builder()
                //每次分页时加载的数量
                .setPageSize(10)
                //第一次加载的数量
                .setInitialLoadSizeHint(12)
                //这个列表一共有多少数据(一般不用)
                // .setMaxSize(100);
                //数据未出现用占位符代替
                // .setEnablePlaceholders(false)
                //距离底部多少个item的时候开始加载下一页数据(默认pageSize大小)
                //.setPrefetchDistance(2)
                .build();

        pageData = new LivePagedListBuilder(factory, config)
                //初始化数据时需要传入的key
                .setInitialLoadKey("0")
                //.setFetchExecutor()
                //监听pading数据加载的状态
                .setBoundaryCallback(callback)
                .build();
    }

    /**
     * paging分页监听
     *
     * @return
     */
    public LiveData<PagedList<T>> getPageData() {
        return pageData;
    }

    /**
     * 数据源
     *
     * @return
     */
    public DataSource getDataSource() {
        return dataSource;
    }

    /**
     * 判断列表内容状态,是否有数据
     *
     * @return
     */
    public MutableLiveData<Boolean> getBoundaryPageData() {
        return boundaryPageData;
    }

    //PagedList数据被加载 情况的边界回调callback
    //但 不是每一次分页 都会回调这里,具体请看 ContiguousPagedList#mReceiver#onPageResult
    //deferBoundaryCallbacks
    PagedList.BoundaryCallback<T> callback = new PagedList.BoundaryCallback<T>() {
        @Override
        public void onZeroItemsLoaded() {
            //新提交的PagedList中没有数据
            boundaryPageData.postValue(false);
        }

        @Override
        public void onItemAtFrontLoaded(@NonNull T itemAtFront) {
            //新提交的PagedList中第一条数据被加载到列表上
            boundaryPageData.postValue(true);
        }

        @Override
        public void onItemAtEndLoaded(@NonNull T itemAtEnd) {
            //新提交的PagedList中最后一条数据被加载到列表上
        }
    };


    protected abstract DataSource createDataSource();

    //可以在这个方法里 做一些清理 的工作
    @Override
    protected void onCleared() {
        super.onCleared();

    }
}

  • 创建列表加载页面父类(这里Fragment为例)
package com.paging.study;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.paging.study.viewmodel.AbsViewModel;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import com.scwang.smartrefresh.layout.constant.RefreshState;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import cn.yumakeji.jetpackroomstudy.databinding.LayoutRefreshViewBinding;


public abstract class AbsListFragment<T, M extends AbsViewModel<T>> extends Fragment implements OnRefreshListener, OnLoadMoreListener {

    protected Context mContext;
    protected Activity mActivity;
    private LayoutRefreshViewBinding binding;
    protected RecyclerView mRecyclerView;
    protected SmartRefreshLayout mRefreshLayout;
    protected EmptyView mEmptyView;
    protected PagedListAdapter<T, RecyclerView.ViewHolder> adapter;
    protected M mViewModel;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mContext = getContext();
        mActivity = getActivity();
        /**
         * DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
         * 区别
         * LayoutRefreshViewBinding直接可以拿到当前布局的填充器
         */
        binding = LayoutRefreshViewBinding.inflate(inflater, container, false);
        mRecyclerView = binding.recyclerView;
        mRefreshLayout = binding.refreshLayout;
        mEmptyView = binding.emptyView;

        //配置
        mRefreshLayout.setEnableRefresh(true);
        mRefreshLayout.setEnableLoadMore(true);
        mRefreshLayout.setOnRefreshListener(this);
        mRefreshLayout.setOnLoadMoreListener(this);


        adapter = getAdapter();
        mRecyclerView.setAdapter(adapter);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setItemAnimator(null);
        onCreateViewFrame(inflater, container, savedInstanceState);
        genericViewModel();
        return binding.getRoot();
    }

    protected abstract void onCreateViewFrame(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);


    private void genericViewModel() {
        //利用 子类传递的 泛型参数实例化出absViewModel 对象。
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        Type[] arguments = type.getActualTypeArguments();
        if (arguments.length > 1) {
            Type argument = arguments[1];
            Class modelClaz = ((Class) argument).asSubclass(AbsViewModel.class);
            mViewModel = (M) new ViewModelProvider(this).get(modelClaz);

            //触发页面初始化数据加载的逻辑
            mViewModel.getPageData().observe(getViewLifecycleOwner(), pagedList -> submitList(pagedList));

            //监听分页时有无更多数据,以决定是否关闭上拉加载的动画
            mViewModel.getBoundaryPageData().observe(getViewLifecycleOwner(), hasData -> finishRefresh(hasData));
        }
    }

    /**
     * 添加数据到adapter
     *
     * @param result
     */
    public void submitList(PagedList<T> result) {
        //只有当新数据集合大于0 的时候,才调用adapter.submitList
        //否则可能会出现 页面----有数据----->被清空-----空布局
        if (result.size() > 0) {
            adapter.submitList(result);
        }
        finishRefresh(result.size() > 0);
    }

    /**
     * 刷新完成
     *
     * @param hasData
     */
    public void finishRefresh(boolean hasData) {

        PagedList<T> currentList = adapter.getCurrentList();
        hasData = hasData || currentList != null && currentList.size() > 0;
        RefreshState state = mRefreshLayout.getState();
        if (state.isFooter && state.isOpening) {
            mRefreshLayout.finishLoadMore();
        } else if (state.isHeader && state.isOpening) {
            mRefreshLayout.finishRefresh();
        }

        if (hasData) {
            mEmptyView.setVisibility(View.GONE);
        } else {
            mEmptyView.setVisibility(View.VISIBLE);
        }
    }

    public abstract PagedListAdapter getAdapter();
}

  • 创建ViewModel处理类
package com.paging.study.viewmodel;

import android.annotation.SuppressLint;
import android.app.Application;
import android.util.ArrayMap;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.paging.DataSource;
import androidx.paging.ItemKeyedDataSource;

import com.paging.study.bean.Teacher;
import com.paging.study.http.PagingHttpCallback;
import com.paging.study.http.PagingHttpClient;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;


public class BlankViewModel extends AbsViewModel<Teacher.RecordsBean> {

    public BlankViewModel(@NonNull Application application) {
        super(application);
    }

    /**
     * 同步位置标记,防止paging和我们自己的分页重复
     */
    private AtomicBoolean loadAfter = new AtomicBoolean();

    @Override
    protected DataSource createDataSource() {
        return new TeacherDataSource();
    }

    /**
     * DataSource<Key,Value>数据源:
     * key 对应加载数据的条件信息,value对应数据实体类
     * <p>
     * PageKeyedDataSource<Key,Value>:
     * 适用于目标数据根据页面信息请求数据的场景
     * <p>
     * ItemKeyedDataSource<Key,Value>:
     * 适用于目标数据的加载依赖特定item的信息
     * <p>
     * PositionalDataSource<Key,Value>:
     * 适用于目标数据总数固定,通过特定的位置加载数据
     */

    class TeacherDataSource extends ItemKeyedDataSource<String, Teacher.RecordsBean> {

        @Override
        public void loadInitial(@NonNull LoadInitialParams<String> params,
                                @NonNull LoadInitialCallback<Teacher.RecordsBean> callback) {
            pageCurrent = 1;
            //加载初始化数据的
            loadData("0", callback);

        }

        @Override
        public void loadAfter(@NonNull LoadParams<String> params, @NonNull LoadCallback<Teacher.RecordsBean> callback) {
            //向后加载分页数据的
            loadData(params.key, callback);
        }

        @Override
        public void loadBefore(@NonNull LoadParams<String> params, @NonNull LoadCallback<Teacher.RecordsBean> callback) {
            callback.onResult(Collections.<Teacher.RecordsBean>emptyList());
            //能够向前加载数据的(例如初始化进入加载的是第3页,向上翻的时候加载第二第一页)
        }

        @NonNull
        @Override
        public String getKey(@NonNull Teacher.RecordsBean item) {
            //通过最后一条item的信息加载数据
            return item.getPaintingId();
        }
    }

    /**
     * 进行网络请求
     *
     * @param key
     * @param callback
     */
    private void loadData(String key, ItemKeyedDataSource.LoadCallback<Teacher.RecordsBean> callback) {
        if (!key.equals("0")) {
            loadAfter.set(true);
        }
        /**
         * 当前线程为子线程
         *
         * 这里有坑,一定要使用同步请求,因为方法执行完毕是,监听者就收到监听
         */
        Map<String, Object> map = new ArrayMap<>();
        map.put("searchContent", "");
        map.put("size", config.pageSize);
        map.put("current", pageCurrent);
        PagingHttpClient.getInstance()
                .get("home/paintingList", map, new PagingHttpCallback<Teacher>() {
                    @Override
                    protected void onNext(Teacher data) {
                        //标记分页页数
                        if (data.getRecords() != null && data.getRecords().size() > 0) {
                            pageCurrent = data.getCurrent() + 1;
                        }
                        callback.onResult(data.getRecords());
                        if (!key.equals("0")) {
                            //通过LiveData发送数据,告诉UI层 是否应该主动关闭上拉加载分页的动画
                            getBoundaryPageData().postValue(data.getRecords().size() > 0);
                            loadAfter.set(false);
                        }
                        Log.e("TTT", "data===>" + data.toString());
                    }

                    @Override
                    protected void onFail(int code, String message) {

                    }
                });
    }

    /**
     * 自己处理分页
     *
     * @param id
     * @param callback
     */
    @SuppressLint("RestrictedApi")
    public void loadAfter(String id, ItemKeyedDataSource.LoadCallback<Teacher.RecordsBean> callback) {
        if (loadAfter.get()) {
            callback.onResult(Collections.emptyList());
            return;
        }
        ArchTaskExecutor.getIOThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                loadData(id, callback);
            }
        });
    }

}

  • adapter适配器(注意JavaBean一定要重写equals方法)
package com.paging.study.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;

import com.paging.study.bean.Teacher;

import cn.yumakeji.jetpackroomstudy.databinding.LayoutTeacherTypeBinding;
import cn.yumakeji.lib_common.global.AppGlobals;

public class HomeAdapter extends PagedListAdapter<Teacher.RecordsBean, HomeAdapter.ViewHolder> {
    protected Context mContext;

    public HomeAdapter(Context context) {
        super(new DiffUtil.ItemCallback<Teacher.RecordsBean>() {
            @Override
            public boolean areItemsTheSame(@NonNull Teacher.RecordsBean oldItem, @NonNull Teacher.RecordsBean newItem) {
                //判断item是否相等(这里根据id来判断)
                return oldItem.getPaintingId() == newItem.getPaintingId();
            }

            @Override
            public boolean areContentsTheSame(@NonNull Teacher.RecordsBean oldItem, @NonNull Teacher.RecordsBean newItem) {
                //判断内容是否相等(重写equals方法)
                return oldItem.equals(newItem);
            }
        });
        this.mContext = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutTeacherTypeBinding binding = LayoutTeacherTypeBinding.inflate(LayoutInflater.from(mContext), parent, false);
        return new ViewHolder(binding.getRoot(), binding);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.binData(getItem(position));
        holder.itemView.setOnClickListener(v -> {
            Toast.makeText(AppGlobals.getApplication(), getItem(position).getPaintingName(), Toast.LENGTH_LONG).show();
        });
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        private LayoutTeacherTypeBinding mBinding;

        public ViewHolder(@NonNull View itemView, LayoutTeacherTypeBinding binding) {
            super(itemView);
            this.mBinding = binding;
        }

        public void binData(Teacher.RecordsBean item) {
            mBinding.setItem(item);
        }
    }
}

适配器布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:id="@+id/ll_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="20dp"
        android:paddingBottom="20dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="20dp"
            android:text="@{item.paintingName}"
            android:textSize="24dp"
            tools:text="名称" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#eee" />
    </LinearLayout>

    <data>

        <variable
            name="item"
            type="com.paging.study.bean.Teacher.RecordsBean" />
    </data>
</layout>
  • 主页面处理

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.paging.ItemKeyedDataSource;
import androidx.paging.PagedList;
import androidx.paging.PagedListAdapter;

import com.paging.study.AbsListFragment;
import com.paging.study.adapter.HomeAdapter;
import com.paging.study.bean.Teacher;
import com.paging.study.datasource.MutablePageKeyedDataSource;
import com.paging.study.viewmodel.BlankViewModel;
import com.scwang.smartrefresh.layout.api.RefreshLayout;

import java.util.List;

public class BlankFragment extends AbsListFragment<Teacher.RecordsBean, BlankViewModel> {


    public static BlankFragment newInstance() {
        return new BlankFragment();
    }

    @Override
    protected void onCreateViewFrame(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    }

    @Override
    public PagedListAdapter getAdapter() {
       
        //return new HomeAdapter(mContext);
        return new HomeAdapter(mContext) {
            @Override
            public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
                super.onViewAttachedToWindow(holder);
                //进入当前页面,看见了
                Log.e("TTT", "onViewAttachedToWindow===>" + holder.getLayoutPosition());
            }

            @Override
            public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
                super.onViewDetachedFromWindow(holder);
                //离开当前页面,看不见了
                Log.e("TTT", "onViewDetachedFromWindow===>" + holder.getLayoutPosition());
            }

			@Override
            public void onCurrentListChanged(@Nullable PagedList<Teacher.RecordsBean> previousList, @Nullable PagedList<Teacher.RecordsBean> currentList) {
                super.onCurrentListChanged(previousList, currentList);
                //这个方法是在我们每提交一次 pagelist对象到adapter 就会触发一次
                //每调用一次 adpater.submitlist
                //防止下拉刷新新加item显示不出来
                if (previousList != null && currentList != null) {
                    if (!currentList.containsAll(previousList)) {
                        mRecyclerView.scrollToPosition(0);
                    }
                }
            }
        };
    }

    /**
     * paging只要有一次返回味空,就停止分页了,要手动处理
     *
     * @param refreshLayout
     */
    @Override
    public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
        PagedList<Teacher.RecordsBean> currentList = adapter.getCurrentList();
        if (currentList == null || currentList.size() <= 0) {
            finishRefresh(false);
            return;
        }
        Teacher.RecordsBean bean = adapter.getCurrentList().get(adapter.getItemCount() - 1);
        mViewModel.loadAfter(bean.getPaintingId(), new ItemKeyedDataSource.LoadCallback<Teacher.RecordsBean>() {
            @Override
            public void onResult(@NonNull List<Teacher.RecordsBean> data) {
                PagedList.Config config = currentList.getConfig();
                if (data != null && data.size() > 0) {
                    //这里 咱们手动接管 分页数据加载的时候 使用MutableItemKeyedDataSource也是可以的。
                    //由于当且仅当 paging不再帮我们分页的时候,我们才会接管。所以 就不需要ViewModel中创建的DataSource继续工作了,所以使用
                    //这里主要是将list集合转化为PagedList,给paging使用,具体实现抄袭源码
                    MutablePageKeyedDataSource<Teacher.RecordsBean> dataSource = new MutablePageKeyedDataSource<>();
                    //这里要把列表上已经显示的先添加到dataSource.data中
                    //而后把本次分页回来的数据再添加到dataSource.data中
                    dataSource.data.addAll(currentList);
                    dataSource.data.addAll(data);
                    PagedList<Teacher.RecordsBean> pagedList = dataSource.buildNewPagedList(config);
                    submitList(pagedList);
                }
            }
        });
    }

    @Override
    public void onRefresh(@NonNull RefreshLayout refreshLayout) {
        //invalidate 之后Paging会重新创建一个DataSource 重新调用它的loadInitial方法加载初始化数据
        //详情见:LivePagedListBuilder#compute方法
        mViewModel.getDataSource().invalidate();
    }
}


网络请求封装(注意paging的不同)

  • 引入
	可单独引入okhttp

 //retrofit:okhttp封装的网络库
    api 'com.squareup.retrofit2:retrofit:2.9.0'
    api 'com.squareup.retrofit2:converter-gson:2.9.0'
    api 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
    api "com.squareup.okhttp3:logging-interceptor:4.7.2"
  • PagingHttpClient 网络请求核心类
package com.paging.study.http;

import android.util.Log;

import com.yumakeji.rxjava.network.interceptor.CacheIntercepter;
import com.yumakeji.rxjava.network.interceptor.HeaderInterceptor;
import com.yumakeji.rxjava.network.uitils.manager.TrustAllCerts;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.Proxy;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import cn.yumakeji.lib_common.BuildConfig;
import cn.yumakeji.lib_common.global.AppGlobals;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;

public class PagingHttpClient {
    protected OkHttpClient okHttpClient;
    protected String sBaseUrl = "https://www.xianghua.art/applets/";

    public static PagingHttpClient getInstance() {
        return SingletonHolder.sInstance;
    }

    //静态内部类
    private static class SingletonHolder {
        private static final PagingHttpClient sInstance = new PagingHttpClient();
    }

    private PagingHttpClient() {
        //缓存地址
        File cacheFile = new File(AppGlobals.getApplication().getExternalCacheDir(), "Http_Cache");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 50); //大小50Mb
        OkHttpClient.Builder builder = new OkHttpClient
                .Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .pingInterval(20, TimeUnit.SECONDS)
                .proxy(Proxy.NO_PROXY)
                //设置缓存方式、时长、地址
                .addNetworkInterceptor(new CacheIntercepter())
                .addInterceptor(new HeaderInterceptor())
                .cache(cache)
                .sslSocketFactory(TrustAllCerts.createSSLSocketFactory())
                .hostnameVerifier(new TrustAllCerts.TrustAllHostnameVerifier());
        if (BuildConfig.DEBUG) { // 判断是否为debug
            // 如果为 debug 模式,则添加日志拦截器
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(@NotNull String message) {
                    Log.i("hxg_http", message);
                }
            });
            logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
            builder.addInterceptor(logging);
        }
        okHttpClient = builder.build();
    }


    private HttpEngine.TypeEnum mMediaType = HttpEngine.TypeEnum.FORM;

    public void setMediaType(HttpEngine.TypeEnum mediaType) {
        this.mMediaType = mediaType;
    }

    /**
     * get请求
     *
     * @param url
     * @param params
     * @return
     */
    public void get(String url, Map<String, Object> params, HttpObserver observer) {
        String execute = execute(url, params, observer);
        if (execute != null) {
            observer.onSuccess(execute);
        } else {
            observer.onError(new RuntimeException("网络连接出错"));
        }
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @return
     */
    public void post(String url, Map<String, Object> params, HttpObserver observer) {
        String execute = executePost(url, params);
        if (execute != null) {
            observer.onSuccess(execute);
        } else {
            observer.onError(new RuntimeException("网络连接出错"));
        }
    }

    public void postText(String url, String stringJsonOrXml, HttpObserver observer) {
        String execute = executePostText(url, stringJsonOrXml, observer);
        if (execute != null) {
            observer.onSuccess(execute);
        } else {
            observer.onError(new RuntimeException("网络连接出错"));
        }
    }

    private String executePost(String url, Map<String, Object> params) {
        try {
            Response execute = postCall(sBaseUrl + url, params).execute();
            ResponseBody body = execute.body();
            return body.string();
        } catch (IOException e) {
            e.printStackTrace();

        }
        return null;
    }

    private String executePostText(String url, String stringJsonOrXml, HttpObserver observer) {
        try {
            Response execute = postCall(sBaseUrl + url, stringJsonOrXml).execute();
            ResponseBody body = execute.body();
            return body.string();
        } catch (IOException e) {
            e.printStackTrace();
            observer.onError(e);
        }
        return null;
    }

    /**
     * Post请求
     *
     * @param url
     * @param params
     * @return
     */
    private Call postCall(String url, Map<String, Object> params) {
        RequestBody requestBody = appendBody(params, mMediaType);
        Request.Builder builder = new Request.Builder();
        Request request = builder.post(requestBody).url(url).build();
        Call call = okHttpClient.newCall(request);
        return call;
    }

    private Call postCall(String url, String stringJsonOrXml) {
        MediaType mediaType = MediaType.parse(getTypeEnum(mMediaType));//"类型,字节码"
        RequestBody requestBody = RequestBody.create(mediaType, stringJsonOrXml);
        Request.Builder builder = new Request.Builder();
        Request request = builder.post(requestBody).url(url).build();
        Call call = okHttpClient.newCall(request);
        return call;
    }


    /**
     * 组装post请求参数body
     *
     * @param params
     * @return
     */
    private RequestBody appendBody(Map<String, Object> params, HttpEngine.TypeEnum mediaType) {
        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(getMediaType(mediaType));
        addParams(builder, params);
        return builder.build();
    }

    /**
     * 添加参数
     * 判断是否能上传文件
     */
    private void addParams(MultipartBody.Builder builder, Map<String, Object> params) {
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                Object value = params.get(key);
                if (value instanceof File) {
                    File file = (File) value;
                    builder.addFormDataPart(key, file.getName(),
                            RequestBody.create(MediaType
                                            .parse(guessMineType(file.getAbsolutePath())),
                                    file));
                } else if (value instanceof List) {
                    //代表提交的是List集合
                    try {
                        List<File> fileList = (List<File>) value;
                        for (int i = 0; i < fileList.size(); i++) {
                            //获取文件
                            File file = fileList.get(i);
                            builder.addFormDataPart(key + i, file.getName(),
                                    RequestBody.create(MediaType
                                                    .parse(guessMineType(file.getAbsolutePath())),
                                            file));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    builder.addFormDataPart(key, String.valueOf(value));
                }
            }
        }
    }

    private String guessMineType(String path) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String contentTypeFor = fileNameMap.getContentTypeFor(path);
        if (contentTypeFor == null) {
            contentTypeFor = "application/octet-stream";
        }
        return contentTypeFor;
    }

    private String getTypeEnum(HttpEngine.TypeEnum typeEnum) {
        switch (typeEnum) {
            case XML:
                return "application/xml; charset=utf-8";
            case JSON:
                return "application/json; charset=utf-8";
            case URLENCODED:
                return "application/x-www-form-urlencoded;charset=utf-8";
        }
        return "application/json; charset=utf-8";
    }

    private MediaType getMediaType(HttpEngine.TypeEnum typeEnum) {
        switch (typeEnum) {
            case MIXED:
                return MultipartBody.MIXED;
            case ALTERNATIVE:
                return MultipartBody.ALTERNATIVE;
            case DIGEST:
                return MultipartBody.DIGEST;
            case PARALLEL:
                return MultipartBody.PARALLEL;
            case FORM:
                return MultipartBody.FORM;
        }
        return MultipartBody.FORM;
    }

    /**
     * get请求
     *
     * @param url
     * @param params
     * @return
     */
    private Call getCall(String url, Map<String, Object> params) {
        String jointUrl = URLUtil.jointParams(sBaseUrl + url, params);
        Request.Builder builder = new Request.Builder();
        Request request = builder.get().url(jointUrl).build();
        Call call = okHttpClient.newCall(request);
        return call;
    }

    private String execute(String url, Map<String, Object> params, HttpObserver observer) {
        try {
            Response execute = getCall(url, params).execute();
            ResponseBody body = execute.body();
            return body.string();
        } catch (IOException e) {
            e.printStackTrace();
            observer.onError(e);
        }
        return null;
    }

}

  • HttpEngine
public class HttpEngine {
    enum TypeEnum {
        XML,
        JSON,
        URLENCODED,
        MIXED,
        ALTERNATIVE,
        DIGEST,
        PARALLEL,
        FORM
    }
}

  • URLUtil

public class URLUtil {
    /**
     * 拼接参数
     */
    public static String jointParams(String url,Map<String,Object> params){
        if (params==null||params.size()<=0){
            return url;
        }
        StringBuffer stringBuffer=new StringBuffer(url);
        if (!url.contains("?")){
            stringBuffer.append("?");
        }else {
            if (!url.endsWith("?")){
                stringBuffer.append("&");
            }
        }
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            stringBuffer.append(entry.getKey()+"="+entry.getValue()+"&");
        }
        stringBuffer.deleteCharAt(stringBuffer.length()-1);
        return stringBuffer.toString();
    }
    /**
     * 解析一个类上面的class信息
     */
    public static Class<?> analysisClazzInfo(Object object){
        Type getType = object.getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) getType).getActualTypeArguments();
        return (Class<?>) params[0];
    }
}

  • 回调
public interface HttpObserver {
    void onError(@NonNull Throwable e);

    void onSuccess(String s);
}


import com.alibaba.fastjson.JSON;
import com.yumakeji.rxjava.network.bean.Result;
import com.yumakeji.rxjava.network.error.HttpThrowable;
import com.yumakeji.rxjava.network.error.ThrowableHandler;
import com.yumakeji.rxjava.network.unify.UnifyCallback;

import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.ParameterizedType;

import io.reactivex.rxjava3.annotations.NonNull;

public abstract class PagingHttpCallback<T> implements HttpObserver {

    @Override
    public void onSuccess(String result) {
        try {
            JSONObject jsonObject = new JSONObject(result);
            Result result1 = new Result();
            result1.setCode(jsonObject.getInt("code"));
            result1.setMessage(jsonObject.getString("message"));
            result1.data = jsonObject.getString("data");

            if (!result1.isOk()) {
                UnifyCallback.handleUnifyCode(result1.getCode(), result1.getMessage());
                onFail(result1.getCode(), result1.getMessage());
                return;
            }
            //解析,获取类上面的泛型
            Class<T> dataClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                    .getActualTypeArguments()[0];
            T data = JSON.parseObject(result1.data.toString(), dataClass);
            onNext(data);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    protected abstract void onNext(T data);

    protected abstract void onFail(int code, String message);

    @Override
    public void onError(@NonNull Throwable throwable) {
        if (throwable instanceof Exception) {
            onFail(ThrowableHandler.handleThrowable(throwable));
        } else {
            onFail(new HttpThrowable(HttpThrowable.UNKNOWN, "未知错误", throwable));
        }
    }

    //应用中具体实现的是下面这个onFail方法
    private void onFail(HttpThrowable httpThrowable) {
        onFail(httpThrowable.errorType, httpThrowable.message);
    }
}


其他
DataSource

在这里插入图片描述

  • MutableItemKeyedDataSource
package com.paging.study.datasource;

import android.annotation.SuppressLint;

import androidx.annotation.NonNull;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.paging.ItemKeyedDataSource;
import androidx.paging.PagedList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 一个可变更的ItemKeyedDataSource 数据源
 * <p>
 * 工作原理是:我们知道DataSource是会被PagedList 持有的。
 * 一旦,我们调用了new PagedList.Builder<Key, Value>().build(); 那么就会立刻触发当前DataSource的loadInitial()方法,而且是同步
 * 详情见ContiguousPagedList的构造函数,而我们在当前DataSource的loadInitial()方法中返回了 最新的数据集合 data。
 * 一旦,我们再次调用PagedListAdapter#submitList()方法 就会触发差分异计算 把新数据变更到列表之上了。
 *
 * @param <Key>
 * @param <Value>
 */
@SuppressLint("RestrictedApi")
public abstract class MutableItemKeyedDataSource<Key, Value> extends ItemKeyedDataSource<Key, Value> {
    private ItemKeyedDataSource mDataSource;

    public List<Value> data = new ArrayList<>();

    public PagedList<Value> buildNewPagedList(PagedList.Config config) {
         PagedList<Value> pagedList = new PagedList.Builder<Key, Value>(this, config)
                .setFetchExecutor(ArchTaskExecutor.getIOThreadExecutor())
                .setNotifyExecutor(ArchTaskExecutor.getMainThreadExecutor())
                .build();

        return pagedList;
    }

    public MutableItemKeyedDataSource(ItemKeyedDataSource dataSource) {

        mDataSource = dataSource;
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Key> params, @NonNull LoadInitialCallback<Value> callback) {
        callback.onResult(data);
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Value> callback) {
        if (mDataSource != null) {
            //一旦 和当前DataSource关联的PagedList被提交到PagedListAdapter。那么ViewModel中创建的DataSource 就不会再被调用了
            //我们需要在分页的时候 代理一下 原来的DataSource,迫使其继续工作
            mDataSource.loadAfter(params, callback);
        }
    }

    @Override
    public void loadBefore(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Value> callback) {
        callback.onResult(Collections.emptyList());
    }

    @NonNull
    @Override
    public abstract Key getKey(@NonNull Value item);
}

  • MutablePageKeyedDataSource
package com.paging.study.datasource;

import android.annotation.SuppressLint;

import androidx.annotation.NonNull;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.paging.PageKeyedDataSource;
import androidx.paging.PagedList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 具体原理见 {@link MutableItemKeyedDataSource}
 *
 * @param <Value>
 */
@SuppressLint("RestrictedApi")
public class MutablePageKeyedDataSource<Value> extends PageKeyedDataSource<Integer, Value> {
    public List<Value> data = new ArrayList<>();

    public PagedList<Value> buildNewPagedList(PagedList.Config config) {
        PagedList<Value> pagedList = new PagedList.Builder<Integer, Value>(this, config)
                .setFetchExecutor(ArchTaskExecutor.getIOThreadExecutor())
                .setNotifyExecutor(ArchTaskExecutor.getMainThreadExecutor())
                .build();

        return pagedList;
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Value> callback) {
        callback.onResult(data, null, null);
    }

    @Override
    public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Value> callback) {
        callback.onResult(Collections.emptyList(), null);
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Value> callback) {
        callback.onResult(Collections.emptyList(), null);
    }
}

  • javabean

public class Teacher extends BaseResult {

    

    private int current;
    private int pages;
    private int size;
    private int total;
    private List<RecordsBean> records;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Teacher)) return false;
        Teacher teacher = (Teacher) o;
        return getCurrent() == teacher.getCurrent() &&
                getPages() == teacher.getPages() &&
                getSize() == teacher.getSize() &&
                getTotal() == teacher.getTotal() &&
                Objects.equals(getRecords(), teacher.getRecords());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCurrent(), getPages(), getSize(), getTotal(), getRecords());
    }

    public int getCurrent() {
        return current;
    }

    public void setCurrent(int current) {
        this.current = current;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public List<RecordsBean> getRecords() {
        return records;
    }

    public void setRecords(List<RecordsBean> records) {
        this.records = records;
    }

    public static class RecordsBean {
        

        private String createTime;
        private int difficultyIndex;
        private String direction;
        private String headImgUrl;
        private int isVip;
        private String label;
        private String paintingId;
        private String paintingName;
        private String readCount;
        private String showNew;
        private String studyAge;
        private String titlePage;
        private int type;
        private String userName;
        private String videoId;
        private String videoName;
        private List<LabelListBean> labelList;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof RecordsBean)) return false;
            RecordsBean that = (RecordsBean) o;
            return getDifficultyIndex() == that.getDifficultyIndex() &&
                    getIsVip() == that.getIsVip() &&
                    getType() == that.getType() &&
                    Objects.equals(getCreateTime(), that.getCreateTime()) &&
                    Objects.equals(getDirection(), that.getDirection()) &&
                    Objects.equals(getHeadImgUrl(), that.getHeadImgUrl()) &&
                    Objects.equals(getLabel(), that.getLabel()) &&
                    Objects.equals(getPaintingId(), that.getPaintingId()) &&
                    Objects.equals(getPaintingName(), that.getPaintingName()) &&
                    Objects.equals(getReadCount(), that.getReadCount()) &&
                    Objects.equals(getShowNew(), that.getShowNew()) &&
                    Objects.equals(getStudyAge(), that.getStudyAge()) &&
                    Objects.equals(getTitlePage(), that.getTitlePage()) &&
                    Objects.equals(getUserName(), that.getUserName()) &&
                    Objects.equals(getVideoId(), that.getVideoId()) &&
                    Objects.equals(getVideoName(), that.getVideoName()) &&
                    Objects.equals(getLabelList(), that.getLabelList());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getCreateTime(), getDifficultyIndex(), getDirection(), getHeadImgUrl(), getIsVip(), getLabel(), getPaintingId(), getPaintingName(), getReadCount(), getShowNew(), getStudyAge(), getTitlePage(), getType(), getUserName(), getVideoId(), getVideoName(), getLabelList());
        }

        public String getCreateTime() {
            return createTime;
        }

        public void setCreateTime(String createTime) {
            this.createTime = createTime;
        }

        public int getDifficultyIndex() {
            return difficultyIndex;
        }

        public void setDifficultyIndex(int difficultyIndex) {
            this.difficultyIndex = difficultyIndex;
        }

        public String getDirection() {
            return direction;
        }

        public void setDirection(String direction) {
            this.direction = direction;
        }

        public String getHeadImgUrl() {
            return headImgUrl;
        }

        public void setHeadImgUrl(String headImgUrl) {
            this.headImgUrl = headImgUrl;
        }

        public int getIsVip() {
            return isVip;
        }

        public void setIsVip(int isVip) {
            this.isVip = isVip;
        }

        public String getLabel() {
            return label;
        }

        public void setLabel(String label) {
            this.label = label;
        }

        public String getPaintingId() {
            return paintingId;
        }

        public void setPaintingId(String paintingId) {
            this.paintingId = paintingId;
        }

        public String getPaintingName() {
            return paintingName;
        }

        public void setPaintingName(String paintingName) {
            this.paintingName = paintingName;
        }

        public String getReadCount() {
            return readCount;
        }

        public void setReadCount(String readCount) {
            this.readCount = readCount;
        }

        public String getShowNew() {
            return showNew;
        }

        public void setShowNew(String showNew) {
            this.showNew = showNew;
        }

        public String getStudyAge() {
            return studyAge;
        }

        public void setStudyAge(String studyAge) {
            this.studyAge = studyAge;
        }

        public String getTitlePage() {
            return titlePage;
        }

        public void setTitlePage(String titlePage) {
            this.titlePage = titlePage;
        }

        public int getType() {
            return type;
        }

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

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getVideoId() {
            return videoId;
        }

        public void setVideoId(String videoId) {
            this.videoId = videoId;
        }

        public String getVideoName() {
            return videoName;
        }

        public void setVideoName(String videoName) {
            this.videoName = videoName;
        }

        public List<LabelListBean> getLabelList() {
            return labelList;
        }

        public void setLabelList(List<LabelListBean> labelList) {
            this.labelList = labelList;
        }

        public static class LabelListBean {
            /**
             * labelId : 1177937130604482562
             * labelName : 线描装饰
             */

            private String labelId;
            private String labelName;

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (!(o instanceof LabelListBean)) return false;
                LabelListBean that = (LabelListBean) o;
                return Objects.equals(getLabelId(), that.getLabelId()) &&
                        Objects.equals(getLabelName(), that.getLabelName());
            }

            @Override
            public int hashCode() {
                return Objects.hash(getLabelId(), getLabelName());
            }

            public String getLabelId() {
                return labelId;
            }

            public void setLabelId(String labelId) {
                this.labelId = labelId;
            }

            public String getLabelName() {
                return labelName;
            }

            public void setLabelName(String labelName) {
                this.labelName = labelName;
            }
        }
    }
}


  • BaseResult
package com.yumakeji.rxjava.network.bean;

public class BaseResult {

   private int code;
   private String message;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isOk() {
        return 200 == code;
    }
}

  • Result
public class Result<T> extends BaseResult {
    public Object data;
}

部分类参考

https://huangxiaoguo.blog.csdn.net/article/details/106525461

Demo地址:https://gitee.com/huangxiaoguo/jetpackandrxjava

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值