Android Rxjava+Retrofit网络请求框架封装(二)

Android Rxjava+Retrofit网络请求框架封装(二)_第1张图片
image

一、前言
二、BaseResponse与BaseObserver的封装
三、RxHelper调度类封装
四、Retrofit初始化封装
五、细节完善
1、服务错误信息处理
2、添加“正在加载”弹窗
3、Retorfit请求方法汇总
4、提交参数方式汇总(可忽略)
六、总结
七、Demo地址
八、内容推荐

一、前言

由于《Rxjava+Retrofit网络请求框架封装(一)》篇幅太长、防止朋友们看的太累,产生视觉疲劳。所以把基础部分和封装部分-分开写。这篇主要是实现如何更简单的去实现网络请求,提高项目后期优化和维护效率。当然有更多的好处,自己细细体会。

作者不善言语,只做粗略描述,见谅!见谅!

二、BaseResponse与BaseObserver的封装

BaseResponse是个人自定义命名的一个类,主要用来对返回数据进行抽象。

BaseObserver是对返回数据的基础数据部分进行统一处理。

为什么要对返回数据进行抽象呢?

大部分公司后台接口返回数据都遵循一定的规范:个人粗略理解分为:基础数据与需求数据

基于上一篇的基础介绍,我们可以获取到后台请求数据如下。

Android Rxjava+Retrofit网络请求框架封装(二)_第2张图片
image

简单理解就是基础数据部分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;
    }
}
image.gif

当然我们需求数据也需重新定义

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;
    }
}
image.gif

2、修改API接口返回数据类型

   // @GET(Constans.retrofit)
   // Observable getRetrofit1();
   // 把原先的Bean类分成BaseResponse基础数据与Demo需求数据两个类
    @GET(Constans.retrofit)
    Observable> getRetrofit2();
image.gif
Android Rxjava+Retrofit网络请求框架封装(二)_第3张图片
image

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);
}
image.gif

请求网络数据

 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'}
image.gif

对返回数据的处理写到这里就结束了、不知道朋友看懂没。

看不懂? 没关系,敲代码实现一下好理解

还是看不懂? 那就多敲几遍。。。。再看不懂 去把作者拉出来溜溜

Android Rxjava+Retrofit网络请求框架封装(二)_第4张图片
image

三、RxHelper调度类封装

忘了从哪里抄来的,这里简单介绍一下。

RxHelper主要是对执行线程和绑定生命周期几个方法进行封装,

Android Rxjava+Retrofit网络请求框架封装(二)_第5张图片
image

大致实现如下:

app builde配置

android {

    .......
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }
}
image.gif
/**
 * 调度类
 */
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;
        }
    }
}
image.gif

使用方式:

Android Rxjava+Retrofit网络请求框架封装(二)_第6张图片
image
compose(RxHelper.observableIO2Main(this))
image.gif

四、Retrofit初始化封装

这部分才是重点应该写在最前面,被我遗漏。尴尬---

Android Rxjava+Retrofit网络请求框架封装(二)_第7张图片
image

之前的调用方式:

Android Rxjava+Retrofit网络请求框架封装(二)_第8张图片
image

我们不可能每次要请求网络就重复去创建初始化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();
    }
}
image.gif

若未看懂单例请参考《JAVA 设计模式——单例模式》

使用方式:

ApiUrl类

Android Rxjava+Retrofit网络请求框架封装(二)_第9张图片
image

调用:

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);
                    }
                });
image.gif

执行顺序分别是:初始化Retrofit——>调用请求接口——>调用执行线程——>输出结果

到这里差不多了,我已经封装不下去了。。。 能力有限止步于此T-T 请朋友们手下留情

Android Rxjava+Retrofit网络请求框架封装(二)_第10张图片
image

五、细节完善

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;
    }
}
image.gif

请在BaseObserver类里面的onError方法里面调用

     @Override
    public void onError(Throwable e) {//服务器错误信息处理
        onFailure(e, RxExceptionUtil.exceptionHandler(e));
    }
image.gif

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;
    }
}
image.gif

使用方式:

Android Rxjava+Retrofit网络请求框架封装(二)_第11张图片
image

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);
}
image.gif

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);
    }
}
image.gif

六、总结

如若加上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);
            }
        });

image.gif

若后台返回的Demo不是个对象 而是数组咋办 不用慌

Android Rxjava+Retrofit网络请求框架封装(二)_第12张图片
image

其他相关的地方也要加上 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);
            }
        });
image.gif

输出如下:

image

附上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";

最后目录如下:

Android Rxjava+Retrofit网络请求框架封装(二)_第13张图片
image

七、Demo地址

https://github.com/DayorNight/RxjavaRetrofit2

八、内容推荐

CSDN:《Android Rxjava+Retrofit网络请求框架封装(二)》

《Android Rxjava+Retrofit网络请求框架封装(一)》​​​​​​​

《Android 仿微信全局字体大小调整》

《Android JUnit单元测试》

《Android Log日志封装》

如果你觉得我写的不错或者对您有所帮助的话

不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈

看在我花了这么多时间整理写成文章分享给大家的份上,记得手下留情哈

您的每个举动都是对我莫大的支持

Android Rxjava+Retrofit网络请求框架封装(二)_第14张图片
image

你可能感兴趣的:(Android Rxjava+Retrofit网络请求框架封装(二))