Android(安卓)Rxjava+Retrofit网络请求框架封装(二)
一、前言
二、BaseResponse与BaseObserver的封装
三、RxHelper调度类封装
四、Retrofit初始化封装
五、细节完善
1、服务错误信息处理
2、添加“正在加载”弹窗
3、Retorfit请求方法汇总
4、提交参数方式汇总(可忽略)
六、总结
七、Demo地址
八、内容推荐
一、前言
由于《Rxjava+Retrofit网络请求框架封装(一)》篇幅太长、防止朋友们看的太累,产生视觉疲劳。所以把基础部分和封装部分-分开写。这篇主要是实现如何更简单的去实现网络请求,提高项目后期优化和维护效率。当然有更多的好处,自己细细体会。
作者不善言语,只做粗略描述,见谅!见谅!
二、BaseResponse与BaseObserver的封装
BaseResponse是个人自定义命名的一个类,主要用来对返回数据进行抽象。
BaseObserver是对返回数据的基础数据部分进行统一处理。
为什么要对返回数据进行抽象呢?
大部分公司后台接口返回数据都遵循一定的规范:个人粗略理解分为:基础数据与需求数据
基于上一篇的基础介绍,我们可以获取到后台请求数据如下。
简单理解就是基础数据部分key值不会改变
而需求数据部分也就是Demo里面的数据会根据不同的需求而改变
BaseResponse就是对基础数据进行封装处理。
实现步骤:
1、根据基础数据定义BaseResponse
2、修改API接口返回数据类型
3、对基础数据统一处理
1、根据基础数据定义BaseResponse
public class BaseResponse { private int res_code; private String err_msg; private T demo; public int getRes_code() { return res_code; } public void setRes_code(int res_code) { this.res_code = res_code; } public String getErr_msg() { return err_msg; } public void setErr_msg(String err_msg) { this.err_msg = err_msg; } public T getDemo() { return demo; } public void setDemo(T demo) { this.demo = demo; }}
当然我们需求数据也需重新定义
public class Demo { @Override public String toString() { return "Demo{" + "id='" + id + '\'' + ", appid='" + appid + '\'' + ", name='" + name + '\'' + ", showtype='" + showtype + '\'' + '}'; } private String id; private String appid; private String name; private String showtype; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getShowtype() { return showtype; } public void setShowtype(String showtype) { this.showtype = showtype; }}
2、修改API接口返回数据类型
// @GET(Constans.retrofit) // Observable getRetrofit1(); // 把原先的Bean类分成BaseResponse基础数据与Demo需求数据两个类 @GET(Constans.retrofit) Observable> getRetrofit2();
3、对基础数据统一处理
/** * 创建Base抽象类实现Observer */public abstract class BaseObserver implements Observer> { private static final String TAG = "BaseObserver"; @Override public void onSubscribe(Disposable d) { Log.e(TAG, "onSubscribe: " ); } @Override public void onNext(BaseResponse response) { //在这边对 基础数据 进行统一处理 举个例子: if(response.getRes_code()==200){ onSuccess(response.getDemo()); }else{ onFailure(null,response.getErr_msg()); } } @Override public void onError(Throwable e) { Log.e(TAG, "Throwable: " + e.getMessage()); } @Override public void onComplete() { Log.e(TAG, "onComplete: " ); } public abstract void onSuccess(T demo); public abstract void onFailure(Throwable e,String errorMsg);}
请求网络数据
retrofit.create(ApiUrl.class) .getRetrofit2() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) //绑定生命周期 .compose(bindUntilEvent(ActivityEvent.DESTROY)) .subscribe(new BaseObserver(){ @Override public void onSuccess(Demo demo) { Log.e(TAG, "onSuccess: "+demo); } @Override public void onFailure(Throwable e, String errorMsg) { Log.e(TAG, "onFailure: "+errorMsg); } });//打印结果: onSuccess: Demo{id='1001', appid='1021', name='sss', showtype='text'}
对返回数据的处理写到这里就结束了、不知道朋友看懂没。
看不懂? 没关系,敲代码实现一下好理解
还是看不懂? 那就多敲几遍。。。。再看不懂 去把作者拉出来溜溜
三、RxHelper调度类封装
忘了从哪里抄来的,这里简单介绍一下。
RxHelper主要是对执行线程和绑定生命周期几个方法进行封装,
大致实现如下:
app builde配置
android { ....... compileOptions { targetCompatibility 1.8 sourceCompatibility 1.8 }}
/** * 调度类 */public class RxHelper { public static ObservableTransformer observableIO2Main(final Context context) { return upstream -> { Observable observable = upstream.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); return composeContext(context, observable); }; } public static ObservableTransformer observableIO2Main(final RxFragment fragment) { return upstream -> upstream.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).compose(fragment.bindToLifecycle()); } public static FlowableTransformer flowableIO2Main() { return upstream -> upstream .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } private static ObservableSource composeContext(Context context, Observable observable) { if(context instanceof RxActivity) { return observable.compose(((RxActivity) context).bindUntilEvent(ActivityEvent.DESTROY)); } else if(context instanceof RxFragmentActivity){ return observable.compose(((RxFragmentActivity) context).bindUntilEvent(ActivityEvent.DESTROY)); }else if(context instanceof RxAppCompatActivity){ return observable.compose(((RxAppCompatActivity) context).bindUntilEvent(ActivityEvent.DESTROY)); }else { return observable; } }}
使用方式:
compose(RxHelper.observableIO2Main(this))
四、Retrofit初始化封装
这部分才是重点应该写在最前面,被我遗漏。尴尬---
之前的调用方式:
我们不可能每次要请求网络就重复去创建初始化Retrofit。所以我们需要对Retrofit进行单例封装。
import android.support.annotation.NonNull;import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;import java.util.concurrent.TimeUnit;import okhttp3.OkHttpClient;import retrofit2.Retrofit;import retrofit2.converter.gson.GsonConverterFactory;/** * Retrofit封装 */public class RetrofitUtils { private static final String TAG = "RetrofitUtils"; private static ApiUrl mApiUrl; /** * 单例模式 */ public static ApiUrl getApiUrl() { if (mApiUrl == null) { synchronized (RetrofitUtils.class) { if (mApiUrl == null) { mApiUrl = new RetrofitUtils().getRetrofit(); } } } return mApiUrl; } private RetrofitUtils(){} public ApiUrl getRetrofit() { // 初始化Retrofit ApiUrl apiUrl = initRetrofit(initOkHttp()) .create(ApiUrl.class); return apiUrl; } /** * 初始化Retrofit */ @NonNull private Retrofit initRetrofit(OkHttpClient client) { return new Retrofit.Builder() .client(client) .baseUrl(Constans.BaseUrl) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); } /** * 初始化okhttp */ @NonNull private OkHttpClient initOkHttp() { return new OkHttpClient().newBuilder() .readTimeout(Constans.DEFAULT_TIME, TimeUnit.SECONDS)//设置读取超时时间 .connectTimeout(Constans.DEFAULT_TIME, TimeUnit.SECONDS)//设置请求超时时间 .writeTimeout(Constans.DEFAULT_TIME,TimeUnit.SECONDS)//设置写入超时时间 .addInterceptor(new LogInterceptor())//添加打印拦截器 .retryOnConnectionFailure(true)//设置出现错误进行重新连接。 .build(); }}
若未看懂单例请参考《JAVA 设计模式——单例模式》
使用方式:
ApiUrl类
调用:
RetrofitUtils.getApiUrl().getDemo() .compose(RxHelper.observableIO2Main(this)) .subscribe(new BaseOberver(){ @Override public void onSuccess(Demo demo) { Log.e(TAG, "onSuccess: "+demo); } @Override public void onFailure(Throwable e, String errorMsg) { Log.e(TAG, "onFailure: "+errorMsg); } });
执行顺序分别是:初始化Retrofit——>调用请求接口——>调用执行线程——>输出结果
到这里差不多了,我已经封装不下去了。。。 能力有限止步于此T-T 请朋友们手下留情
五、细节完善
1、服务错误信息处理
BaseObserver 对请求成功数进行了统一处理 ,但并未对服务器返回错误进行处理。
这里从某个大神Copy了个工具类RxExceptionUtils来对错误信息进行处理。
具体代码如下:
import org.json.JSONException;import java.net.SocketTimeoutException;import java.net.UnknownHostException;import java.text.ParseException;import retrofit2.HttpException;/** * 异常处理 */public class RxExceptionUtil { public static String exceptionHandler(Throwable e){ String errorMsg = "未知错误"; if (e instanceof UnknownHostException) { errorMsg = "网络不可用"; } else if (e instanceof SocketTimeoutException) { errorMsg = "请求网络超时"; } else if (e instanceof HttpException) { HttpException httpException = (HttpException) e; errorMsg = convertStatusCode(httpException); } else if (e instanceof ParseException || e instanceof JSONException || e instanceof JSONException) { errorMsg = "数据解析错误"; } return errorMsg; } private static String convertStatusCode(HttpException httpException) { String msg; if (httpException.code() >= 500 && httpException.code() < 600) { msg = "服务器处理请求出错"; } else if (httpException.code() >= 400 && httpException.code() < 500) { msg = "服务器无法处理请求"; } else if (httpException.code() >= 300 && httpException.code() < 400) { msg = "请求被重定向到其他页面"; } else { msg = httpException.message(); } return msg; }}
请在BaseObserver类里面的onError方法里面调用
@Override public void onError(Throwable e) {//服务器错误信息处理 onFailure(e, RxExceptionUtil.exceptionHandler(e)); }
2、添加“正在加载”弹窗
import android.app.ProgressDialog;import android.content.Context;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.widget.Toast;import io.reactivex.disposables.Disposable;/** * Observer加入加载框 * @param */public abstract class MyObserver extends BaseObserver { private boolean mShowDialog; private ProgressDialog dialog; private Context mContext; private Disposable d; public MyObserver(Context context, Boolean showDialog) { mContext = context; mShowDialog = showDialog; } public MyObserver(Context context) { this(context,true); } @Override public void onSubscribe(Disposable d) { this.d = d; if (!isConnected(mContext)) { Toast.makeText(mContext,"未连接网络",Toast.LENGTH_SHORT).show(); if (d.isDisposed()) { d.dispose(); } } else { if (dialog == null && mShowDialog == true) { dialog = new ProgressDialog(mContext); dialog.setMessage("正在加载中"); dialog.show(); } } } @Override public void onError(Throwable e) { if (d.isDisposed()) { d.dispose(); } hidDialog(); super.onError(e); } @Override public void onComplete() { if (d.isDisposed()) { d.dispose(); } hidDialog(); super.onComplete(); } public void hidDialog() { if (dialog != null && mShowDialog == true) dialog.dismiss(); dialog = null; } /** * 是否有网络连接,不管是wifi还是数据流量 * @param context * @return */ public static boolean isConnected(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if (info == null) { return false; } boolean available = info.isAvailable(); return available; } /** * 取消订阅 */ public void cancleRequest(){ if (d!=null&&d.isDisposed()) { d.dispose(); hidDialog(); } }}
使用方式:
3、Retorfit请求方法汇总
ApiUrl类
import io.reactivex.Observable;import okhttp3.MultipartBody;import okhttp3.RequestBody;import okhttp3.ResponseBody;import retrofit2.Call;import retrofit2.http.Body;import retrofit2.http.DELETE;import retrofit2.http.Field;import retrofit2.http.FieldMap;import retrofit2.http.FormUrlEncoded;import retrofit2.http.GET;import retrofit2.http.Header;import retrofit2.http.HeaderMap;import retrofit2.http.Headers;import retrofit2.http.Multipart;import retrofit2.http.POST;import retrofit2.http.PUT;import retrofit2.http.Part;import retrofit2.http.PartMap;import retrofit2.http.Path;import retrofit2.http.Query;import retrofit2.http.QueryMap;import retrofit2.http.Streaming;import retrofit2.http.Url;public interface ApiUrl { @GET(Constans.retrofit) Call getRetrofit(); @GET(Constans.retrofit) Observable> getDemo(); /** * TODO Get请求 */ //第一种方式:GET不带参数 @GET("retrofit.txt") Observable> getUser(); @GET Observable getUser(@Url String url); @GET Observable getUser1(@Url String url); //简洁方式 直接获取所需数据 //第二种方式:GET带参数 @GET("api/data/{type}/{count}/{page}") Observable getUser(@Path("type") String type, @Path("count") int count, @Path("page") int page); //第三种方式:GET带请求参数:https://api.github.com/users/whatever?client_id=xxxx&client_secret=yyyy @GET("users/whatever") Observable getUser(@Query("client_id") String id, @Query("client_secret") String secret); @GET("users/whatever") Observable getUser(@QueryMap Map info); /** * TODO POST请求 */ //第一种方式:@Body @Headers("Accept:application/json") @POST("login") Observable postUser(@Body RequestBody body); //第二种方式:@Field @Headers("Accept:application/json") @POST("auth/login") @FormUrlEncoded Observable postUser(@Field("username") String username, @Field("password") String password); //多个参数 Observable postUser(@FieldMap Map map); /** * TODO DELETE */ @DELETE("member_follow_member/{id}") Observable delete(@Header("Authorization") String auth, @Path("id") int id); /** * TODO PUT */ @PUT("member") Observable put(@HeaderMap Map headers, @Query("nickname") String nickname); /** * TODO 文件上传 */ @Multipart @POST("upload") Observable upload(@Part("description") RequestBody description, @Part MultipartBody.Part file); //亲测可用 @Multipart @POST("member/avatar") Observable uploadImage(@HeaderMap Map headers, @Part MultipartBody.Part file); /** * 多文件上传 */ @Multipart @POST("register") Observable upload(@PartMap Map params, @Part("description") RequestBody description); //Observable upload(@Part() List parts); @Multipart @POST("member/avatar") Observable uploadImage1(@HeaderMap Map headers, @Part List file); /** * 来自https://blog.csdn.net/impure/article/details/79658098 * @Streaming 这个注解必须添加,否则文件全部写入内存,文件过大会造成内存溢出 */ @Streaming @GET Observable download(@Header("RANGE") String start, @Url String url);}
4、提交参数方式汇总(可忽略)
/** * 提交参数方式 */public class RequestUtils { /** * Get 请求demo * @param context * @param observer */ public static void getDemo(RxAppCompatActivity context, MyObserver observer){ RetrofitUtils.getApiUrl() .getDemo().compose(RxHelper.observableIO2Main(context)) .subscribe(observer); } /** * Post 请求demo * @param context * @param consumer */ public static void postDemo(RxAppCompatActivity context, String name, String password, Observer consumer){ RetrofitUtils.getApiUrl() .postUser(name,password).compose(RxHelper.observableIO2Main(context)) .subscribe(consumer); } /** * Put 请求demo * @param context * @param consumer */ public static void putDemo(RxFragment context, String access_token,Observer consumer){ Map headers = new HashMap(); headers.put("Accept","application/json"); headers.put("Authorization",access_token); RetrofitUtils.getApiUrl() .put(headers,"厦门").compose(RxHelper.observableIO2Main(context)) .subscribe(consumer); } /** * Delete 请求demo * @param context * @param consumer */ public static void deleteDemo(RxFragment context, String access_token,Observer consumer){ RetrofitUtils.getApiUrl() .delete(access_token,1).compose(RxHelper.observableIO2Main(context)) .subscribe(consumer); } /** * 上传图片 * @param context * @param observer */ public static void upImagView(RxFragment context, String access_token,String str, Observer observer){ File file = new File(str);// File file = new File(imgPath); Map header = new HashMap(); header.put("Accept","application/json"); header.put("Authorization",access_token);// File file =new File(filePath); RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), file);// RequestBody requestFile =// RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), reqFile); RetrofitUtils.getApiUrl().uploadImage(header,body).compose(RxHelper.observableIO2Main(context)) .subscribe(observer); } /** * 上传多张图片 * @param files */ public static void upLoadImg(RxFragment context,String access_token,List files, Observer observer1){ Map header = new HashMap(); header.put("Accept","application/json"); header.put("Authorization",access_token); MultipartBody.Builder builder = new MultipartBody.Builder() .setType(MultipartBody.FORM);//表单类型 for (int i = 0; i < files.size(); i++) { File file = files.get(i); RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/*"), file); builder.addFormDataPart("file", file.getName(), photoRequestBody); } List parts = builder.build().parts(); RetrofitUtils.getApiUrl().uploadImage1(header,parts).compose(RxHelper.observableIO2Main(context)) .subscribe(observer1); }}
六、总结
如若加上RequestUtils则代码中请求网络方式如下:
RequestUtils.getDemo(this, new MyObserver(this) { @Override public void onSuccess(Demo result) { tv_retrofit.setText(result.toString()); } @Override public void onFailure(Throwable e, String errorMsg) { tv_retrofit.setText(errorMsg); } });
若后台返回的Demo不是个对象 而是数组咋办 不用慌
其他相关的地方也要加上 List<>
RequestUtils.getDemoList(this, new MyObserver>(this) { @Override public void onSuccess(List result) { for (Demo demo:result){ Log.e(TAG, "onSuccess: "+demo.toString() ); } tv_retrofit.setText(result.toString()); } @Override public void onFailure(Throwable e, String errorMsg) { tv_retrofit.setText(errorMsg); } });
输出如下:
附上Url链接:
public final static String BaseUrl = "http://120.78.186.81/api/";public final static String retrofit = "values/5";public final static String retrofitList = "values";
最后目录如下:
七、Demo地址
https://github.com/DayorNight/RxjavaRetrofit2
八、内容推荐
简书:《Android Rxjava+Retrofit网络请求框架封装(二)》
《Android Rxjava+Retrofit网络请求框架封装(一)》
《Android 仿微信全局字体大小调整》
《Android JUnit单元测试》
《Android Log日志封装》
如果你觉得我写的不错或者对您有所帮助的话
不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈
看在我花了这么多时间整理写成文章分享给大家的份上,记得手下留情哈
您的每个举动都是对我莫大的支持
更多相关文章
- Android(安卓)网络学习
- Android中ContentProvider和ContentResolver使用入门
- android屏幕刷新显示机制
- Android的四大组件的作用
- 通过广播获取Android屏幕旋转事件
- Android(安卓)Bluetooth蓝牙技术基础讲解
- 自学Android(安卓)----- Activity的生命周期
- 2018-04-08 2018年Android(安卓)面试题
- Android读书笔记---View基础