安卓开发过程中,网络请求与下拉刷新分页列表的控件几乎可以说是必不可少的,但是每次开发一款产品都要重新开发,肯定是不可取的,那么最好是可以自己整理一个开发框架,那么以后开发,直接引入项目即可
网络框架的封装,从httpclient,到xutils,再到volley,再到okhttp,每次整合都发现多多少少的不足,目前自己觉得最成熟的一个也就是retrofit+okhttp3+rxjava的组合,rxjava不懂的推荐看大神的深入浅出rxjava,retrofit的使用自己网上搜咯
下拉刷新列表的实现,之前自己有基于listview整合的控件,用的到可以参考这里listview 实现下拉刷新上拉加载更多,现在有recyclerview整合的控件,看这里recyclerview实现下拉刷新自动加载更多
现在来整合retrofit+okhttp3+rxjava+recyclerview,丢掉handler与ancyctask,什么xutils,volley,httpclient都是浮云,老夫写代码只用retrofit,2333333333333333!
1.一不小心handler就造成了内存泄漏,程序关闭了,内存却无法释放
2.一不小心ancyctask的线程池模式,会让我们的请求按顺序执行完毕才行,即使页面退出,任务会继续执行,当快速关闭快速打开有网络请求的页面时,会等ancyctask所有未执行完毕的请求执行完以后才会执行当前的请求,这里就需要每次页面关闭就取消未执行完毕的请求
3.要为每个请求进行错误捕捉以及处理
4.请求时弹出进度提示,以及如何取消一个请求
那么利用这个整合的框架,所有问题就不再是问题
必备的库
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'io.reactivex:rxjava:1.2.6'
compile 'io.reactivex:rxandroid:1.2.1'
首先来看一下,如何使用retrofit+okhttp3+rxjava,进行非列表数据型网络请求
效果图么么,必须有
SubscriberOnNextListener> getTopMovieOnNext = new SubscriberOnNextListener>() {
@Override
public void onNext(List subjects) {
ToastUtil.ToastCenter("请求数据成功!");
Intent it=new Intent(mActivity,DetailActivity.class);
it.putExtra("str",subjects.toString());
mActivity.startActivity(it);
}
};
ProgressSubscriber> subscriber = new ProgressSubscriber>(getTopMovieOnNext, mActivity, true, true);
NetWorks.getInstance().Test250(subscriber, 0, 10);//网络请求
就是这么简单,如果有异常,将会在ProgressSubscriber中统一处理
ProgressSubscriber.java
/**
* 用于在Http请求开始时,自动显示一个ProgressDialog
* 在Http请求结束是,关闭ProgressDialog
* 调用者自己对请求数据进行处理
*/
public class ProgressSubscriber extends Subscriber implements ProgressCancelListener {
private final boolean canShow;//是否显示进度条
private SubscriberOnNextListener mSubscriberOnNextListener;
private ProgressDialogHandler mProgressDialogHandler;
private Context context;
/**
*
* @param mSubscriberOnNextListener
* @param context
* @param canShow 是否显示进度条
* @param canCancel 是否可以取消网络请求(只要progressbar消失,请求就被取消了)
*/
public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context, boolean canShow, boolean canCancel) {
this.mSubscriberOnNextListener = mSubscriberOnNextListener;
this.context = context;
this.canShow=canShow;
mProgressDialogHandler = new ProgressDialogHandler(context, this, canCancel);
}
private void showProgressDialog(){
if (mProgressDialogHandler != null && canShow ) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
}
}
private void dismissProgressDialog(){
if (mProgressDialogHandler != null && canShow ) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
mProgressDialogHandler = null;
}
}
/**
* 订阅开始时调用
* 显示ProgressDialog
*/
@Override
public void onStart() {
showProgressDialog();
}
/**
* 完成,隐藏ProgressDialog
*/
@Override
public void onCompleted() {
dismissProgressDialog();
}
/**
* 对错误进行统一处理
* 隐藏ProgressDialog
* @param e
*/
@Override
public void onError(Throwable e) {
if (e instanceof SocketTimeoutException) {
ToastUtil.ToastCenter("网络中断,请检查您的网络状态");
} else if (e instanceof ConnectException) {
ToastUtil.ToastCenter("网络中断,请检查您的网络状态");
}else if (e instanceof HttpException){ //HTTP错误
ToastUtil.ToastCenter("网络异常,请检查您的网络状态");
e.printStackTrace();
} else if (e instanceof ApiException){
ToastUtil.ToastCenter(e.getMessage());
e.printStackTrace();
}else {
//这里可以收集未知错误上传到服务器
ToastUtil.ToastCenter("服务器忙");
e.printStackTrace();
}
dismissProgressDialog();
}
/**
* 将onNext方法中的返回结果交给Activity或Fragment自己处理
*
* @param t 创建Subscriber时的泛型类型
*/
@Override
public void onNext(T t) {
if (mSubscriberOnNextListener != null) {
mSubscriberOnNextListener.onNext(t);
}
}
/**
* 取消ProgressDialog的时候,取消对observable的订阅,同时也取消了http请求
*/
@Override
public void onCancelProgress() {
if (!this.isUnsubscribed()) {
this.unsubscribe();
}
}
}
下面来看一下NetWorks做了哪些事情,也就算是定义一些网络请求的方法
public class NetWorks extends RetrofitUtils {
private NetWorks() {
super();
}
//在访问HttpMethods时创建单例
private static class SingletonHolder {
private static final NetWorks INSTANCE = new NetWorks();
}
//获取单例
public static NetWorks getInstance() {
return SingletonHolder.INSTANCE;
}
//测试用例子
public void Test250(Subscriber> subscriber, int start, int count) {
Observable observable = service.top250(start, count).map(new HttpResultFunc>());
setSubscribe(observable, subscriber);
}
/**
* 用来统一处理Http的resultCode,并将HttpResult的Data部分剥离出来返回给subscriber
*
* @param Subscriber真正需要的数据类型,也就是Data部分的数据类型
*/
private class HttpResultFunc implements Func1, T> {
@Override
public T call(HttpResult httpResult) {
/*if (httpResult.getCount() == 0) {
throw new ApiException(100);
}*/
return httpResult.getSubjects();
}
}
}
上面NetWorks是继承于RetrofitUtils,这才是真正的网络请求框架,这里整合了gson与rxjava,okhttp3
/**
* retrofit工具类
*/
public abstract class RetrofitUtils {
private static Retrofit mRetrofit;
private static OkHttpClient mOkHttpClient;
protected static final NetService service = getRetrofit().create(NetService.class);
/**
* 获取Retrofit对象
*
* @return
*/
protected static Retrofit getRetrofit() {
if (null == mRetrofit) {
if (null == mOkHttpClient) {
mOkHttpClient = OkHttp3Utils.getOkHttpClient();
// mOkHttpClient = new OkHttpClient();
}
//Retrofit2后使用build设计模式
mRetrofit = new Retrofit.Builder()
//设置服务器路径
.baseUrl(UrlConstant.BASEURL)
//添加转化库,默认是Gson
.addConverterFactory(GsonConverterFactory.create())
//添加回调库,采用RxJava
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
//设置使用okhttp网络请求
.client(mOkHttpClient)
.build();
}
return mRetrofit;
}
/**
* 插入观察者-泛型
* @param observable
* @param observer
* @param
*/
public static void setSubscribe(Observable observable, Observer observer) {
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
}
OkhttpClient可以不用自己创建,retrofit会默认使用,这里自己创建,是为了添加一些拦截操作
public class OkHttp3Utils {
private static OkHttpClient mOkHttpClient;
//设置缓存目录
// private static File cacheDirectory = new File(MyApplication.getInstance().getApplicationContext().getCacheDir().getAbsolutePath(), "MyCache");
private static File cacheDirectory = new File(Constant.BASE_PATH, "MyCache");
private static Cache cache = new Cache(cacheDirectory, 10 * 1024 * 1024);
/**
* 获取OkHttpClient对象
*
* @return
*/
public static OkHttpClient getOkHttpClient() {
if (null == mOkHttpClient) {
//同样okhttp3后也使用build设计模式
mOkHttpClient = new OkHttpClient.Builder()
//设置一个自动管理cookies的管理器
// .cookieJar(new CookiesManager())
//没网络时的拦截器
.addInterceptor(new MyIntercepter())
//设置请求读写的超时时间
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.cache(cache)
.build();
}
return mOkHttpClient;
}
/**
* 拦截器
*/
private static class MyIntercepter implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request oldRequest = chain.request();
//gan-----start----------------以下代码为添加一些公共参数使用--------------------------
// 添加新的参数
String time = System.currentTimeMillis() / 1000 + "";
String mKey = "f6f712249f4b725fac309504d633f839";
HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host());
//.addQueryParameter("regionAId", "")
//.addQueryParameter("regionZId", "");
//.addQueryParameter("os", "android")
//.addQueryParameter("time", URLEncoder.encode(time, "UTF-8"))
//.addQueryParameter("version", "1.1.0")
//.addQueryParameter("sign", MD5.md5("key=" + mKey));
// 构建新的请求
Request newRequest = oldRequest.newBuilder()
.method(oldRequest.method(), oldRequest.body())
.url(authorizedUrlBuilder.build())
.build();
//gan-----end
if (!isNetworkReachable(MyApplication.getInstance().getApplicationContext())) {
newRequest = newRequest.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)//无网络时只从缓存中读取
.build();
}
Response response = chain.proceed(newRequest);
if (isNetworkReachable(MyApplication.getInstance().getApplicationContext())) {
int maxAge = 60 * 60; // 有网络时 设置缓存超时时间1个小时
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24 * 28; // 无网络时,设置超时为4周
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
//***************打印Log*****************************
if (Constant.DEBUG) {
String requestUrl = newRequest.url().toString(); // 获取请求url地址
String methodStr = newRequest.method(); // 获取请求方式
RequestBody body = newRequest.body(); // 获取请求body
String bodyStr = (body == null ? "" : body.toString());
// 打印Request数据
LogUtils.D("gan-retrofit-okhttp3", "requestUrl=====>" + requestUrl);
LogUtils.D("gan-retrofit-okhttp3", "requestMethod=====>" + methodStr);
LogUtils.D("gan-retrofit-okhttp3", "requestBody=====>" + bodyStr);
if (Constant.NET_DATA_SHOW) {
//打印返回数据
LogUtils.D("gan-retrofit-okhttp3", "responseBody=====>" + response.body().string());
Constant.NET_DATA_SHOW=false;
}
}
return response;
}
}
private static String bodyToString(final RequestBody request) {
try {
final RequestBody copy = request;
final Buffer buffer = new Buffer();
if (copy != null)
copy.writeTo(buffer);
else
return "";
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
/**
* 自动管理Cookies
*/
private static class CookiesManager implements CookieJar {
private final PersistentCookieStore cookieStore = new PersistentCookieStore(MyApplication.getInstance().getApplicationContext());
//在接收时,读取response header中的cookie
@Override
public void saveFromResponse(HttpUrl url, List cookies) {
if (cookies != null && cookies.size() > 0) {
for (Cookie item : cookies) {
cookieStore.add(url, item);
}
} else {
Log.i("gan", "cookie为null");
}
}
//分别是在发送时向request header中加入cookie
@Override
public List loadForRequest(HttpUrl url) {
Log.i("gan", "url为---" + url);
List cookies = cookieStore.get(url);
if (cookies.size() < 1) {
Log.i("gan", "cookies为null");
}
return cookies;
}
}
/**
* 判断网络是否可用
*
* @param context Context对象
*/
public static Boolean isNetworkReachable(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo current = cm.getActiveNetworkInfo();
if (current == null) {
return false;
}
return (current.isAvailable());
}
}
不能忘了netService,接口就是在这里定义咯
public interface NetService {
@GET("top250")
Observable>> top250(@Query("start") int start, @Query("count") int count);
}
上面简单的就完成了一个网络请求,下面来与下拉刷新分页加载控件一起使用
先看效果
再看如何使用,这里我用的是子页面,activity中用法是一样的
public class MessagePager extends ContentBasePager implements MyRecycleView.RefreshLoadMoreListener {
@BindView(R.id.message_page_recycleview)
MyRecycleView recycleView;
private CommonAdapter mAdapter;
private RecycleviewSubscriberOnNextListener> getTopMovieOnNext;
private List actAllList = new ArrayList();
private boolean isFirstIn =true;
private RecycleviewSubscriber> subscriber;
public MessagePager(AppCompatActivity activity) {
super(activity);
ButterKnife.bind(this, mRootView);
}
@Override
public void initData() {
if (isFirstIn){
initView();
recycleView.firstLoadingView("数据加载中");
isFirstIn = false;
}
}
@Override
public void outData() {
//ToastUtil.ToastCenter(mActivity,"离开HomePager");
}
@Override
public int getContentView() {
return R.layout.pager_message;
}
private void initView() {
initAdapter();//初始化适配器
recycleView.setRefreshLoadMoreListener(this);//下拉上拉加载更多监听
//prrv.setPullRefreshEnable(false);//禁用刷新
recycleView.setCanMore(false);//禁用加载更多用在setAdapter()之前
//设置布局管理
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recycleView.setLayoutManager(layoutManager);
recycleView.setAdapter(mAdapter);
//条目监听
recycleView.setOnItemClickListener(new MyRecycleView.ItemClickListener() {
@Override
public void onClick(View view, RecyclerView.ViewHolder holder, int position) {
}
@Override
public void onLongClick(View view, RecyclerView.ViewHolder holder, int position) {
ToastUtil.ToastCenter("longclick-pos = " + position);
}
});
initNetListener();
}
/**
* 初始化适配器
*/
private void initAdapter() {
mAdapter = new CommonAdapter(mActivity, R.layout.fragment_discover_cardview_item, actAllList) {
@Override
protected void convert(ViewHolder holder, Subject s, int position) {
holder.setText(R.id.activity_title, s.getTitle());
/*holder.setText(R.id.activity_date, Utils.longtimeToDayDate(a
.getStartDate())
+ "-"
+ Utils.longtimeToDayDate(a.getEndDate()));*/
holder.setText(R.id.activity_date, s.getYear());
ImageView imageView=holder.getView(R.id.img_iv);
ImageLoader.getInstance().displayImage(s.getImages().getLarge(), imageView);
}
};
}
@Override
public void onRefresh() {
//Constant.NET_DATA_SHOW=true;//开启数据打印到log
subscriber =new RecycleviewSubscriber>(getTopMovieOnNext, recycleView, R.drawable.icon_nonet, R.drawable.icon_err);
NetWorks.getInstance().Test250(subscriber, 0, 10);
}
@Override
public void onLoadMore() {
}
private void initNetListener() {
getTopMovieOnNext = new RecycleviewSubscriberOnNextListener>() {
@Override
public void onNext(List subjects) {
recycleView.setDateRefresh(actAllList, subjects, R.drawable.icon_no_order, "暂无订单");
ToastUtil.ToastCenter("刷新完成");
}
@Override
public void onErr(int drawable, String msg) {
if (actAllList.isEmpty())
recycleView.setDateRefreshErr(drawable, msg);//显示错误面板
else {
ToastUtil.ToastCenter(msg);//提示信息
recycleView.stopRefresh();//停止刷新
}
}
};
}
/**取消网络请求操作,activty中同样用在ondestroy方法中**/
@Override
public void onDestroy() {
if (subscriber!=null)subscriber.onActivityDestroy();
super.onDestroy();
}
}
页面xml
之所以可以跟recyclerview一起使用,那么就靠自定义控件MyRecyleView与RecycleviewSubscriber了
**
* 用于跟recycleview组合使用时的Subscriber
* @param
*/
public class RecycleviewSubscriber extends Subscriber {
private final MyRecycleView recycleView;
private final int noNet;
private final int onErr;
private RecycleviewSubscriberOnNextListener mSubscriberOnNextListener;
/**
*
* @param mSubscriberOnNextListener
* @param recycleView
* @param onErr 出现异常时图片
* @param noNet //无网络时的图片
*/
public RecycleviewSubscriber(RecycleviewSubscriberOnNextListener mSubscriberOnNextListener, MyRecycleView recycleView, int noNet, int onErr) {
this.mSubscriberOnNextListener = mSubscriberOnNextListener;
this.recycleView = recycleView;
this.onErr=onErr;
this.noNet=noNet;
}
/**
* 订阅开始时调用
*/
@Override
public void onStart() {
}
/**
* 完成
*/
@Override
public void onCompleted() {
}
/**
* 对错误进行统一处理
* 隐藏ProgressDialog
* @param e
*/
@Override
public void onError(Throwable e) {
if (e instanceof SocketTimeoutException) {
doErr(noNet,"网络连接超时,请检查您的网络状态");
} else if (e instanceof ConnectException) {
doErr(noNet,"网络中断,请检查您的网络状态");
}else if (e instanceof HttpException){ //HTTP错误
doErr(noNet,"网络异常,请检查您的网络状态");
e.printStackTrace();
} else if (e instanceof ApiException){
//ToastUtil.ToastCenter(e.getMessage());
doErr(onErr,e.getMessage());
e.printStackTrace();
}else {
doErr(onErr,"服务器忙");
e.printStackTrace();
}
}
/**
* 处理未知异常
*/
private void doErr(int pic ,String err) {
if (mSubscriberOnNextListener != null) {
mSubscriberOnNextListener.onErr(pic,err);
}
}
/**
* 将onNext方法中的返回结果交给Activity或Fragment自己处理
*
* @param t 创建Subscriber时的泛型类型
*/
@Override
public void onNext(T t) {
if (mSubscriberOnNextListener != null) {
mSubscriberOnNextListener.onNext(t);
}
}
/**
* activity销毁,取消对observable的订阅,同时也取消了http请求
*/
public void onActivityDestroy() {
if (!this.isUnsubscribed()) {
this.unsubscribe();
}
}
}
最后肯定demo咯
github地址
Rxjava1+retrofit2
csdn下载地址
rxjava2+retrofit2+recyclerview