RxJava2+Retrofit2+okHttp的二次封装

项目Demo已经上传至github

链接

本人android小白一枚,在学习android的过程中,我感受很深,在做了这么多练手的项目之后,我觉得android就像一块画板,画板是白纸,作为android的开发人员,要做的就是运用一切可能,去在画板上画出让人看的懂并且美观的画。说起来,我们却也属于艺术家这一行了。。。

在我看来,android是为了展示数据的,这里就要分两个步骤了,第一就是怎样去展示,这部分是相当于UI的工作,,第二就是数据的来源,数据从哪来,怎么来,来的是什么,有什么规律,这些都需要我们去考虑,一般来说,就现在的开发模式来说,数据都是存储在数据库中,后台的工作人员通过各种语言去实现对数据库的增删改查,然后将数据库中的数据进行加工处理,部署在服务器上,android端就需要通过访问服务器去获取并解析数据,这里就涉及到获取并解析数据了。不涉及到网络的应用现如今基本上是不复存在的了,一款App,没有强大的后台作为其支撑,纵然你将界面做的再花哨华丽,终究只能算是“花瓶”,“鸡肋”。

所以在开始开发一个项目之前,选取一款优秀的网络框架是非常有必要的。RxJava和Retrofit出现也很久了,之前是一直使用okHttp去封装一个请求类,这样虽然能够正确的请求到数据,但是,代码不仅复杂繁多,遇到多请求问题就会让逻辑不清晰,思维很容易进入混乱,说再多都无法表达多请求问题的恐怖,亲身经历才能够感受它的复杂。

RxJava的概念其实很模糊,我对它的理解就是一个给你方便处理异步问题的框架,到底有多方便,体会过才知道。。。

Retrofit就是对okhttp做了一层封装。把网络请求都交给给了Okhttp,我们只需要通过简单的配置就能使用retrofit来进行网络请求了,Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API,在我看来,retrofit有点像javaee里的springboot框架,通过xml文件配置好一切后,直接就可以在方法头前添加注释来执行网络请求,实在是不能再方便了。

使用方法:build.gradle文件中引入几个jar

compile 'io.reactivex.rxjava2:rxjava:2.0.7'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.5.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.trello.rxlifecycle2:rxlifecycle:2.1.0'
compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'

说再多,不如一个例子来的简单,我们现在定义好一个场景,来练练手。

假设现在有这么一组json数据

RxJava2+Retrofit2+okHttp的二次封装_第1张图片

results是一个json数据。它的请求url是http://gank.io/api/data/福利/10/1。因为本篇重点就是在讲运用网络框架去取数据,所以在这里,只要取到数据就可以了,至于怎样去展示,不是本篇重点,所以不做讨论。

1.首先观察json数据的规律,要自定义一个实体,负责将json数据解析成一个个实体类,方便去调用。

总体类:

public class BaseResult<T> {

    private int code;
    private String message;
    private T results;
    private boolean error;

    public T getResults() {
        return results;
    }

    public void setResults(T results) {
        this.results = results;
    }

    public boolean isError() {
        return error;
    }

    public void setError(boolean error) {
        this.error = error;
    }

    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;
    }
}
results中的数据类:Meizi

public class MeiZi {
    /**
     * _id : 59cd9b53421aa9727fdb25eb
     * createdAt : 2017-09-29T09:01:07.894Z
     * desc : 9-29
     * publishedAt : 2017-09-29T11:21:16.116Z
     * source : chrome
     * type : 福利
     * url : https://ws1.sinaimg.cn/large/610dc034ly1fk05lf9f4cj20u011h423.jpg
     * used : true
     * who : daimajia
     */

    private String _id;
    private String createdAt;
    private String desc;
    private String publishedAt;
    private String source;
    private String type;
    private String url;
    private boolean used;
    private String who;

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public String getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(String createdAt) {
        this.createdAt = createdAt;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getPublishedAt() {
        return publishedAt;
    }

    public void setPublishedAt(String publishedAt) {
        this.publishedAt = publishedAt;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getType() {
        return type;
    }

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

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isUsed() {
        return used;
    }

    public void setUsed(boolean used) {
        this.used = used;
    }

    public String getWho() {
        return who;
    }

    public void setWho(String who) {
        this.who = who;
    }
}
2.数据的容器准备好之后,就开始去获取数据了,使用RxJava2与Retrofit2框架去进行网络请求,我觉得定义Retrofit访问的接口

这个是最好想的,所以先编写这个接口。

public interface ApiService {
    /**
     * 网络请求超时时间毫秒
     */
    int DEFAULT_TIMEOUT = 20000;

    String HOST = "http://gank.io/";
    String API_SERVER_URL = HOST + "api/data/";


    @GET("福利/10/1")
    Observable>> getMezi();

    /**
     * @param page
     * @param number
     * @return
     */
    @Headers("Cache-Control: public, max-age=100")//设置缓存 缓存时间为100s
    @GET("everySay/selectAll.do")
    Observable>> lookBack(@Query("page") int page, @Query("rows") int number);


    @POST("upload/uploadFile.do")
    Observable uploadFiles(@Part("filename") String description,
                                          @Part("pic\"; filename=\"image1.png") RequestBody imgs1,
                                          @Part("pic\"; filename=\"image2.png") RequestBody imgs2);

    @POST("upload/uploadFile.do")
    Observable uploadFiles(@Part("filename") String description, @PartMap() Map, RequestBody> maps);

}
3.就是对自己的Retrofit去进行封装设置

(1)retrofit是基于okhttp的加强版,所以第一步去自定义一个okhttpclient,里面设置两个拦截器,一个是日志拦截器,用于对日志的筛选,还一个就是网络拦截器,用于对网络请求头的总体设置。

//日志拦截器
HttpLoggingInterceptor interceptor=new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
    @Override
    public void log(String message) {
        try {
            String text = URLDecoder.decode(message, "utf-8");
            LogUtils.e(text);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            LogUtils.e(message);
        }
    }
});
class HttpNetWorkInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (!NetUtil.checkNetWork(BaseApplication.getmContext())) {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
            LogUtils.d("no network");
        }

        Response originalResponse = chain.proceed(request);
        if (NetUtil.checkNetWork(BaseApplication.getmContext())) {
            //有网的时候读接口上的@Headers里的配置,可以在这里进行统一的设置
            String cacheControl = request.cacheControl().toString();
            return originalResponse.newBuilder()
                    .header("Cache-Control", cacheControl)
                    .removeHeader("Pragma")
                    .build();
        } else {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=2419200")
                    .removeHeader("Pragma")
                    .build();
        }
    }
}
(2)创建一个okhttpclient

//初始化一个OKhttpClient
OkHttpClient okHttpClient=new OkHttpClient.Builder()
        .addInterceptor(interceptor)
        .readTimeout(8, TimeUnit.SECONDS)
        .connectTimeout(8,TimeUnit.SECONDS)
        .addNetworkInterceptor(new HttpNetWorkInterceptor())
        .cache(cache)
        .build();
(3)创建一个Retrofit

Gson gson=new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();
Retrofit retrofit=new Retrofit.Builder()
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .baseUrl(ApiService.API_SERVER_URL)
        .build();

service=retrofit.create(ApiService.class);
(4)创建一个观察者基类,在其中进行对请求错误的封装和对进度条显示与消失的封装。

public abstract class DefaultObserver<T extends BaseResult> implements Observer<T> {

    private Activity activity;
    //  Activity 是否在执行onStop()时取消订阅
    private boolean isAddInStop = false;
    private CommonDialogUtils dialogUtils;
    public DefaultObserver(Activity activity) {
        this.activity = activity;
        dialogUtils=new CommonDialogUtils();
        dialogUtils.showProgress(activity);
    }

    public DefaultObserver(Activity activity, boolean isShowLoading) {
        this.activity = activity;
        dialogUtils=new CommonDialogUtils();
        if (isShowLoading) {
            dialogUtils.showProgress(activity,"Loading...");
        }
    }
    @Override
    public void onSubscribe(@NonNull Disposable d) {

    }

    @Override
    public void onNext(@NonNull T t) {
        dismissProgress();
        if (!t.isError()) {
            onSuccess(t);
        } else {
            onFail(t);
        }
    }

    private void dismissProgress(){
        if(dialogUtils!=null){
            dialogUtils.dismissProgress();
        }
    }


    @Override
    public void onError(@NonNull Throwable e) {
        LogUtils.e(e.getMessage());
        dismissProgress();
        if (e instanceof HttpException) {     //   HTTP错误
            onException(ExceptionReason.BAD_NETWORK);
        } else if (e instanceof ConnectException
                || e instanceof UnknownHostException) {   //   连接错误
            onException(CONNECT_ERROR);
        } else if (e instanceof InterruptedIOException) {   //  连接超时
            onException(CONNECT_TIMEOUT);
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {   //  解析错误
            onException(PARSE_ERROR);
        } else {
            onException(UNKNOWN_ERROR);
        }
    }

    @Override
    public void onComplete() {

    }

    /**
     * 请求成功
     *
     * @param response 服务器返回的数据
     */
    abstract public void onSuccess(T response);

    /**
     * 服务器返回数据,但响应码不为200
     *
     * @param response 服务器返回的数据
     */
    public void onFail(T response) {
        String message = response.getMessage();
        if (TextUtils.isEmpty(message)) {
            ToastUtils.show(R.string.response_return_error);
        } else {
            ToastUtils.show(message);
        }
    }

    /**
     * 请求异常
     *
     * @param reason
     */
    public void onException(ExceptionReason reason) {
        switch (reason) {
            case CONNECT_ERROR:
                ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT);
                break;

            case CONNECT_TIMEOUT:
                ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT);
                break;

            case BAD_NETWORK:
                ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT);
                break;

            case PARSE_ERROR:
                ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT);
                break;

            case UNKNOWN_ERROR:
            default:
                ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT);
                break;
        }
    }

    /**
     * 请求网络失败原因
     */
    public enum ExceptionReason {
        /**
         * 解析数据失败
         */
        PARSE_ERROR,
        /**
         * 网络问题
         */
        BAD_NETWORK,
        /**
         * 连接错误
         */
        CONNECT_ERROR,
        /**
         * 连接超时
         */
        CONNECT_TIMEOUT,
        /**
         * 未知错误
         */
        UNKNOWN_ERROR,
    }
}
(5)对BaseActivity的封装,通过rxlifecycle对Rxjava的生命周期进行控制

public abstract class BaseActivity extends RxAppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        init(savedInstanceState);
    }
    protected void showToast(String msg) {
        ToastUtils.show(msg);
    }

    protected abstract @LayoutRes int getLayoutId();

    protected abstract void init(Bundle savedInstanceState);
}
(6)对BaseFragment的封装

public abstract class BaseFragment extends RxFragment {

    public View rootView;
    public LayoutInflater inflater;


    @Nullable
    @Override
    public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        this.inflater = inflater;
        if (rootView == null) {
            rootView = inflater.inflate(this.getLayoutId(), container, false);
            init(savedInstanceState);
        }
        ViewGroup parent = (ViewGroup) rootView.getParent();
        if (parent != null) {
            parent.removeView(rootView);
        }
        return rootView;
    }

    protected abstract int getLayoutId();

    protected abstract void init(Bundle savedInstanceState);

    protected void showToast(String msg) {
        ToastUtils.show(msg);
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }


    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }
}
(7)请求数据的方法

public void getData() {
    IdeaApi.getSingleHolder()
            .getMezi()
            .compose(this.>>bindToLifecycle())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new DefaultObserver>>(this) {
                @Override
                public void onSuccess(BaseResult> response) {
                    List results = response.getResults();
                    showToast("请求成功,妹子个数为"+results.size());
                }
            });
}

你可能感兴趣的:(android)