一步步搭建Retrofit+RxJava+MVP网络请求框架(一)

来源:互联网 发布:电脑智能机器人软件 编辑:程序博客网 时间:2024/05/16 02:00

之前做的项目一直都是直接用retrofit直接进行网络请求,基本就是封装一个RetrofitApiService的单例,然后在Activity或者fragment中对它进行调用,其实使用起来也非常方便,但是当项目越来越大,这样的网络请求会越来越多,Activity中的任务会越来越重,Activity本身还承担很多其他的任务,这样Activity的代码就会越来越多,可读性也比较差。于是就会想到封装与解耦,正好又看到现在RxJava+MVP很流行,于是自己也好好学习了一把,于是自己也结合自己之前的项目进行改版,采用RxJava和MVP进行封装。自己在这里记录下心得,封装完成后,确实感觉RxJava+MVP很好用,在初次接触的时候,会觉得他们不光没有增强代码的可读性和复用性,还多了一大堆复杂代码。

一、什么是RxJava和MVP 

我对他们的理解就两个字,异步。android中带的Handel和asynctask想必大家都用过很多了。RxJava是采用异步的思想设计的一个框架,这里就不多讲,大家自行去查下它的资料。MVP是一种项目的框架,与之对应的有常用的MVC。没有引入MVP之前的网络请求方式是,activity或者Fragment(View)中调用retrofit发起网络请求(也是异步的),然后获取到数据后(Model),异步更新到View中。这里的retrofit相当与控制器(Controller),但是控制器处理数据的大部分代码会加载到View中,增加了View的负担。现在增加Presenter(主持人)来管理model和View,View不再直接和Model打交道,比如在网路请求时,等Presenter获取完数据后,再发布给Activity(View)进行数据展示。这样一来就可以减轻Activity的压力。

二、封装实例

首先,展示一下封装好之后的项目的层级结构。 
1、先创建一个RetrofitApiService.java

package com.xdw.retrofitrxmvpdemo.http;import com.xdw.retrofitrxmvpdemo.model.UserInfo;import retrofit2.http.GET;import retrofit2.http.Query;import rx.Observable;/** * Created by 夏德旺 on 2017/12/8. */public interface RetrofitApiService {    @GET("userinfo")    Observable<UserInfo> getUserInfo(@Query("uid") int uid);}

这里就是把原生的retrofit中的Call换成了RxJava中的Observable。

2、封装RetrofitUtil

package com.xdw.retrofitrxmvpdemo.http;import android.content.Context;import com.google.gson.GsonBuilder;import com.xdw.retrofitrxmvpdemo.constant.UrlConstant;import okhttp3.OkHttpClient;import retrofit2.Retrofit;import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;import retrofit2.converter.gson.GsonConverterFactory;/** * Created by 夏德旺 on 2017/12/8. */public class RetrofitUtil {    private Context mCntext;    //声明Retrofit对象    private Retrofit mRetrofit;    //声明RetrofitApiService对象    private RetrofitApiService retrofitApiService;    OkHttpClient client = new OkHttpClient();    GsonConverterFactory factory = GsonConverterFactory.create(new GsonBuilder().create());    //由于该对象会被频繁调用,采用单例模式,下面是一种线程安全模式的单例写法    private volatile static RetrofitUtil instance;    public static RetrofitUtil getInstance(Context context){        if (instance == null) {            synchronized (RetrofitUtil.class) {                if (instance == null) {                    instance = new RetrofitUtil(context);                }            }        }        return instance;    }    private RetrofitUtil(Context mContext){        mCntext = mContext;        init();    }    //初始化Retrofit    private void init() {        mRetrofit = new Retrofit.Builder()                .baseUrl(UrlConstant.BASE_URL)                .client(client)                .addConverterFactory(factory)                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                .build();        retrofitApiService = mRetrofit.create(RetrofitApiService.class);    }    public RetrofitApiService getRetrofitApiService(){        return retrofitApiService;    }}



3、封装DataManager,这里和原生的retrofit的封装一样该类用来管理RetrofitApiService中对应的各种API接口,当做Retrofit和presenter中的桥梁,Activity就不用直接和retrofit打交道了

package com.xdw.retrofitrxmvpdemo.manager;import android.content.Context;import com.xdw.retrofitrxmvpdemo.http.RetrofitApiService;import com.xdw.retrofitrxmvpdemo.http.RetrofitUtil;import com.xdw.retrofitrxmvpdemo.model.UserInfo;import rx.Observable;/** * Created by 夏德旺 on 2017/12/8. *///该类用来管理RetrofitApiService中对应的各种API接口,// 当做Retrofit和presenter中的桥梁,Activity就不用直接和retrofit打交道了public class DataManager {    private RetrofitApiService mRetrofitService;    private volatile static DataManager instance;    private DataManager(Context context){        this.mRetrofitService = RetrofitUtil.getInstance(context).getRetrofitApiService();    }    //由于该对象会被频繁调用,采用单例模式,下面是一种线程安全模式的单例写法    public static DataManager getInstance(Context context) {        if (instance == null) {            synchronized (DataManager.class) {                if (instance == null) {                    instance = new DataManager(context);                }            }        }        return instance;    }    //将retrofit的业务方法映射到DataManager中,以后统一用该类来调用业务方法    //以后再retrofit中增加业务方法的时候,相应的这里也要添加,比如添加一个getOrder    public Observable<UserInfo> getUserInfo(int uid){        return mRetrofitService.getUserInfo(uid);    }}


4、创建一个Presenter接口,这里就引入了Presenter这个东西了,后面我们根据具体的业务创建对应的实例去实现该接口。该接口主要面向数据,后面还会创建一个PresentView接口(面向View的),然后通过

BindPresentView方法将这2个接口关联起来,从而实现对数据和Activity的管理。

package com.xdw.retrofitrxmvpdemo.presenter;import com.xdw.retrofitrxmvpdemo.pv.PresentView;/** * Created by 夏德旺 on 2017/12/8. */public interface Presenter {    //Presenter初始化    void onCreate();    //销毁    void onDestroy();    //绑定视图    void BindPresentView(PresentView presentView);}

5、定义一个实现Presenter的基础类BasePresenter,后续具体功能类继承于它,主要是为了在该类中写一些共用方法,比如
CompositeSubscription的创建与销毁。(CompositeSubscription干嘛用的可以查阅RxJava的资料

package com.xdw.retrofitrxmvpdemo.presenter;import com.xdw.retrofitrxmvpdemo.pv.PresentView;import rx.subscriptions.CompositeSubscription;/** * Created by 夏德旺 on 2017/12/8. *///定义一个Presenter的基础类,后续具体功能类继承于它public class BasePresenter implements Presenter {    //声明一个CompositeSubscription对象,注意是protected修饰符,便于子类进行调用    protected CompositeSubscription mCompositeSubscription;    @Override    public void onCreate() {        //在基础类中对CompositeSubscription进行初始化,子类中就不用再写一次        //子类如果需要对onCreate进行重写,记得先调用super.onCreate();        mCompositeSubscription = new CompositeSubscription();    }    @Override    public void onDestroy() {        //释放CompositeSubscription,否则会造成内存泄漏        if (mCompositeSubscription.hasSubscriptions()){            mCompositeSubscription.unsubscribe();        }    }    @Override    public void BindPresentView(PresentView presentView) {        //与具体视图进行绑定,留个子类进行扩展    }}


6、创建一个接口PresentView,面向视图View的接口,和前面的Prenseter配合使用


package com.xdw.retrofitrxmvpdemo.pv;/** * Created by 夏德旺 on 2017/12/8. *///面向视图View的接口,和前面的Prenseter配合使用public interface PresentView {    //定义一个最基础的接口,里面就包含一个出错信息的回调    //因为大多数时候报错的时候都是采用一条信息提示    //如果需要负责的报错接口,请重载onError,是重载不是重写    void onError(String result);}

7、之前还已经创建了2个类,这里补充下,一个是UrlConstant(用来存放常量的,这里用来存放Base Url),另外一个是数据模型类UserInfo

UrlConstant.java:

package com.xdw.retrofitrxmvpdemo.constant;/** * Created by 夏德旺 on 2017/12/8. */public class UrlConstant {    /**     * 域名:     * 调试:http://10.1.1.192     */    public static final String BASE_URL="http://10.1.1.192";}

UserInfo.java:

package com.xdw.retrofitrxmvpdemo.model;/** * Created by 夏德旺 on 2017/12/8. */public class UserInfo {    private String username;    private int age;    public UserInfo(String username, int age) {        this.username = username;        this.age = age;    }    public String getUsername() {        return username;    }    public int getAge() {        return age;    }    @Override    public String toString() {        return "UserInfo{" +                "username='" + username + '\'' +                ", age=" + age +                '}';    }}


8、下面就需要将具体的业务加入到Presenter中了,先写一个UserInfoPv继承自PresentView接口,这个是根据具体业务添加。

package com.xdw.retrofitrxmvpdemo.pv;import com.xdw.retrofitrxmvpdemo.model.UserInfo;/** * Created by 夏德旺 on 2017/12/8. */public interface UserInfoPv extends PresentView {    //对基础接口PresentView进行扩展,添加onSuccess回调    //因为该回调与具体的业务对应,所以不能写到基础接口里面    //比如UserInfo的回调就创建一个UserInfoPv的接口,如果新增一个Order的业务,    //则新增一个OrderPv的接口    void onSuccess(UserInfo userInfo);}


9、写一个UserInfoPresenter继承BasePresenter类,在该类中实现数据的请求,并且将该类与业务对应的PresentView进行绑定,这里是和UserInfoPv绑定

package com.xdw.retrofitrxmvpdemo.presenter;import android.content.Context;import com.xdw.retrofitrxmvpdemo.manager.DataManager;import com.xdw.retrofitrxmvpdemo.model.UserInfo;import com.xdw.retrofitrxmvpdemo.pv.PresentView;import com.xdw.retrofitrxmvpdemo.pv.UserInfoPv;import rx.Observer;import rx.android.schedulers.AndroidSchedulers;import rx.schedulers.Schedulers;/** * Created by 夏德旺 on 2017/12/8. *///该类是具体业务presenter,如需增加另一个业务,比如Order//则可以再创建一个OrderPresenterpublic class UserInfoPresenter extends BasePresenter {    private Context mContext;    private UserInfoPv mUserInfoPv;    private UserInfo mUserInfo;    public UserInfoPresenter (Context context){        this.mContext = context;    }    @Override    public void BindPresentView(PresentView presentView) {        mUserInfoPv = (UserInfoPv)presentView;    }    //在presenter中实现业务逻辑,此处会调用前面封装好的retrofit的东西    //将处理结果绑定到对应的PresentView实例,这样Activity和PresentView实例绑定好之后,    //Activity->PresentView->Presenter->retrofit的关系就打通了    public void getUserInfo(int uid){        super.mCompositeSubscription.add(DataManager.getInstance(mContext).getUserInfo(uid)                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Observer<UserInfo>() {                    @Override                    public void onCompleted() {                        if (mUserInfo != null){                            mUserInfoPv.onSuccess(mUserInfo);                        }                    }                    @Override                    public void onError(Throwable e) {                        e.printStackTrace();                        mUserInfoPv.onError("请求失败!!");                    }                    @Override                    public void onNext(UserInfo userInfo) {                        mUserInfo = userInfo;                    }                })        );    }}

10、好了,retrofit与rxjava,presenter相关的东西都封装好了,接下来轮到之前苦逼的Activity出场了,在Activity中展示presenter传递过来的数据。

package com.xdw.retrofitrxmvpdemo.activity;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;import com.xdw.retrofitrxmvpdemo.R;import com.xdw.retrofitrxmvpdemo.model.UserInfo;import com.xdw.retrofitrxmvpdemo.presenter.UserInfoPresenter;import com.xdw.retrofitrxmvpdemo.pv.UserInfoPv;public class MainActivity extends AppCompatActivity {    private TextView text;    private Button button;    //定义需要调用的presenter对象    private UserInfoPresenter mUserInfoPresenter =new UserInfoPresenter(this);    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        text = (TextView)findViewById(R.id.text);        button = (Button)findViewById(R.id.button);        //在Activity创建的时候同时初始化presenter,这里onCreater不是指的创建presenter对象,        // 而是做一些presenter的初始化操作,名字应该取名init更好理解点,我这里就不重命名了        mUserInfoPresenter.onCreate();        //将presenter和PresentView进行绑定,实际上就是将presenter和Activity视图绑定,        //这个是MVP模式中V与P交互的关键        mUserInfoPresenter.BindPresentView(mUserInfoPv);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //点击按钮触发presenter里面的方法                mUserInfoPresenter.getUserInfo(1);            }        });    }    //采用内部类方法定义presentView对象,该对象用来将Activity和presenter进行绑定    //绑定了以后主线程中就可以通过回调来获取网络请求的数据    private UserInfoPv mUserInfoPv = new UserInfoPv(){        @Override        public void onSuccess(UserInfo userInfo) {            text.setText(userInfo.toString());        }        @Override        public void onError(String result) {            Toast.makeText(MainActivity.this,result, Toast.LENGTH_SHORT).show();        }    };    //在Activity销毁的时候,一定要对CompositeSubscription进行释放,否则会造成内存泄漏    //释放操作封装到了presenter的ondestroy方法中    @Override    protected void onDestroy(){        super.onDestroy();        mUserInfoPresenter.onDestroy();    }}


补充下:截图中的Result这个类是我实际项目中用到的,这里简化了下业务逻辑,就没用到它。

Result.java:

package com.xdw.retrofitrxmvpdemo.model;/** * Created by 夏德旺 on 2017/12/8. */public class Result<T> {    private int code;    private String msg;    private T data;    public Result(int code, String msg, T data) {        this.code = code;        this.msg = msg;        this.data = data;    }    public int getCode() {        return code;    }    public String getMsg() {        return msg;    }    public T getData() {        return data;    }    @Override    public String toString() {        return "Result{" +                "code=" + code +                ", msg='" + msg + '\'' +                ", data=" + data +                '}';    }}

最后说明下,可能有的人觉得本来之前直接在Activity中写retrofit很方便,现在却加了这么多代码,视乎变得更麻烦了。但是自己仔细想想设计模式,当业务变得越来越复杂的时候,这种封装就会变得越来越有意义。

列举下之后像该项目中扩展业务的步骤,比如加一个订单功能。

操作步骤:

1、添加对应的model类Order

2、RetrofitApiService中添加对应的网络请求api

3、将新添加的api映射到DataManager中

4、添加业务对应的PrensentView实例OrderPv

5、添加业务对应的Presenter实例OrderPresenter

6、在需要该业务的UI线程(Activity或Fragment)中调用具体业务对应的Presenter

看着新添加一个业务的过程是不是很多?但是逻辑很清晰,扩展很容易,写代码不是说代码量越少,代表代码写的越好越有效率。

之后还会对该框架进一步封装,加入拦截器,网络请求等待框等功能的封装。

之补充下我这里测试用的服务端源码,就一个index.php文件:

<?php header("Content-Type: application/json; charset=utf-8");$uid=$_GET['uid'];if($uid==1){$arr['code']=200;$arr['msg']='ok';$data['username']='xiadewang';$data['age']=30;$arr['userinfo']=$data;echo json_encode($data,JSON_UNESCAPED_UNICODE);}


代码链接:

http://download.csdn.net/download/aaaabbbb1019/10151399


阅读全文
0 0
原创粉丝点击