一、前言
二、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;
}
}
使用方式:
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
八、内容推荐
CSDN:《Android Rxjava+Retrofit网络请求框架封装(二)》
《Android Rxjava+Retrofit网络请求框架封装(一)》
《Android 仿微信全局字体大小调整》
《Android JUnit单元测试》
《Android Log日志封装》
如果你觉得我写的不错或者对您有所帮助的话
不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈
看在我花了这么多时间整理写成文章分享给大家的份上,记得手下留情哈
您的每个举动都是对我莫大的支持