美文网首页框架【库】android进阶MVP
android-简单快速封装MVP+Retrofit2.0+Rx

android-简单快速封装MVP+Retrofit2.0+Rx

作者: 瓶子漂吧 | 来源:发表于2018-10-31 18:43 被阅读333次

    1、概述

    对于MVP(Model View Presenter)大多数做开发的人都能说出一二,或者看到网上的对mvp的解释,对其意都有大概的了解,但让你真正写一套mvp框架,是不是发现无从下手?
    曾几何本人接触MVP+Retrofit2.0+Rxjava也有同样的疑惑,固知道问题所在,网上关于MVP方面文章太多,千篇一律,看起来比较费力,其实只要看懂一篇文章就够了,为此这篇文章本人写的比较详细,如果无从下手,可以看看这篇文章,本人趁工作之余,花了一定时间写了一篇,让你快速学会搭建一个自己MVP+Retrofit2.0+Rxjava框架,通用以后的项目中。
    1.1、框架好处:
    MVP框架的好处就是解耦,让代码分离,便于重构、测试,避免activity业务繁重,让代码看起来干净利落。

    2、理解MVP的构成

    View :对应于Activity,负责View的绘制以及与用户交互
    Model :就是数据源,可以理解为数据相关的模块,它不仅包含实体类的定义,还包含一切跟数据有关的操作比如数据库、网络(获取、解析、存储),项目最常见的网络请求数据操作就放在,mode层里面
    Presenter :负责完成View于Model间的交互,也就是通过presenter将mode里面的网络获取到的数据交给View(activity、或者Fragment处理),p层是交通运纽中心,P说白了就是全是java代码。

    3、mvp框架交互方式

    3.1、mvp最关键技术就在于接口的运用,将接口回调发挥的淋漓精致,同时也将activity大多数代码分离、解耦出来,让代码更清晰,更工整;
    3.2、上面说到Presenter完成View于Model间的交互通过接口,p层涉及两个重要回调:
    3.3、在P层中,使用mode对象调用接口,mode具体实现网络操作方法
    3.4、在P层中,使用view对象调用接口传递数据,在activity实现接口方法,更新UI。

    4、是时候打造自己的万能MVP框架了

    xing.jpg

    4.1、定义最基层的IView 接口用于P和v的交互;IPresenter实现该接口,P层继承IPresenter接口,使得该框架面对接口;IModel 接口是最基层用P和M交互接口

     /** 
      *Created by ljp on 2018/9/20.
      * <p>
      *  最基层负责presenter和activity交互接口
      * 这里是是一些更新Activity也就是更新view的接口,最基层,以便不同view扩展
      */
    public interface IView {
       void showDialog();
       void dissDialog();
    }    
    
     /**
      *这里是最基层的mode,因为不同的view有不同的mode,但有功能的网络操作,便可以在mode里写接
      *口,便于扩展
      */
     public interface IModel {
      }
    
    /**
     *最基层的Present接口,使得以后的具体Present都都具备这个接口属性,具备IPresenter这个接口属性,
     *便可以调用IPresenter接口方法
     */
     public interface IPresenter<VIEW extends IView> {
       void attach(VIEW view);
       void detach();
     }
    

    4.2、这里是所有实现基层IModel 、IView 、IPresenter接口,基层一切为了扩展,具体作用标注

     /**
      * Created by ljp on 2018/9/20.
      * <p>
      * model和view接口基础接口管理
      */
    public interface StudentMainContract {
    
    /**
     * mode和present交互接口,具体网络操作
     */
    interface Model extends IModel {
    
        /**
         * 学段选择数据请求接口
         */
        void doHttpStudentSection(NetWorkCallback<List<StudentBeanResponse>> netWorkCallback);
    }
    
    /**
     * 负责更新ui接口
     */
    interface View extends IView {
        /**
         * 更新学段ui接口
         */
        void initStudentSelect(List<StudentBeanResponse> beanList);
    }
    
    /**
     * view和present交互接口,view中调用,present实现
     */
    interface Presenter extends IPresenter<View> {
        /**
         * 获取学段网络接口
         */
        void getHttpStudentSectionData();
    }
    }
    

    4.3、打造万能P层,基本套路形,该P层继承了IPresenter接口,实现attach和detach方法,attach方法含义如下

    /**
     * Created by ljp on 2018/9/20.
     * <p>
     *    基层present
     */
    public abstract class BasePresenter<VIEW extends IView, MODEL extends IModel> implements   IPresenter<VIEW> {
    
    private WeakReference<VIEW> mView;
    private MODEL mModel;
    
    /**
     * @param view 对的activity上下文对象,具体activtiy(view)实现了不同的接口,进行回调对应接口方法时候用到(接口子类对象类对象调用接口方法),
     *          activty退出了,某个接口进网络操作,存在对当前上下午引用,存在内存泄漏,使用WeakReference解决。
     */
    public void attach(VIEW view) {
        mView = new WeakReference<>(view);
        mModel = createModel();
    }
    
    @CallSuper
    public void detach() {
        if (mView != null) {
            mView.clear();
            mView = null;
        }
    }
    
    
    protected VIEW getView() {
        if (mView != null) {
            return mView.get();
        }
        return null;
    }
    
    protected MODEL getModel() {
        return mModel;
    }
    
    protected abstract MODEL createModel();
    }
    

    4.4、这是基层BaseMvpActivity,泛型需要传入泛型参数IView型和IPresenter型(这里说明具体的present都实现了基层IPresenter接口),以下注意理解:
    4.4. 1、getIView()在具体view里面实现了,这里是StudentMainActivity 实现,getView()就是对应的activtiy对象;
    4.4.2、StudentMainActivity 继承了StudentMainContract.View接口、View继承IView接口,
    4.4.3、 attach 参数就是IView类型,StudentMainActivity 是IView接口子类;
    4.4.4、具体present类StudentMainPresenterImpl 就继承了StudentMainContract.Presenter接口,Presenter继承IPresenter,所有StudentMainPresenterImpl是IPresenter接口子类,便可以调用attach。

    public abstract class BaseMvpActivity<VIEW extends IView, PRESENTER extends IPresenter<VIEW>> extends Activity {
    
    private PRESENTER mPresenter;
    private LekeProgressDialog mLekeProgressDialog;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = createPresenter();
        mPresenter.attach(getIView());
        mLekeProgressDialog = new LekeProgressDialog(this);
    }
    
    
    public void showLekeDialog() {
        if (mLekeProgressDialog == null) {
            mLekeProgressDialog = new LekeProgressDialog(this);
        }
        mLekeProgressDialog.show();
    }
    
    public void dissLekeMissDialog() {
        if (mLekeProgressDialog != null) {
            mLekeProgressDialog.dismiss();
        }
        mLekeProgressDialog = null;
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detach();
        }
        dissLekeMissDialog();
    }
    
    protected PRESENTER getPresenter() {
        return mPresenter;
    }
    
    protected abstract PRESENTER createPresenter();
    
    protected abstract VIEW getIView();
    }
    

    4.5、具体使用,<StudentMainContract.View, StudentMainContract.Presenter>都是接口类型;
    之前使用IPresenter接口目的是让具体的StudentMainPresenterImpl 接口化,IPresenter接口目的仅仅就是让对应StudentMainActivity (View)调用到StudentMainPresenterImpl 里面方法(Presenter);

     /**
    * Created by ljp on 2018/9/20.
    * <p>
    *     主页
    */
       public class StudentMainActivity extends BaseMvpActivity<StudentMainContract.View, 
     StudentMainContract.Presenter> implements StudentMainContract.View {
    
    private TextView mTextView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.btn);
        mTextView = findViewById(R.id.tv);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //TODO 开始学生网络请求
                getPresenter().getHttpStudentSectionData();
            }
        });
    
    }
    
    @Override
    protected StudentMainContract.Presenter createPresenter() {
        return new StudentMainPresenterImpl();
    }
    
    @Override
    protected StudentMainContract.View getIView() {
        return this;
    }
    
    /**
     * 更新学段ui接口
     *
     * @param beanList
     */
    @Override
    public void initStudentSelect(List<StudentBeanResponse> beanList) {
        //TODO  更新UI操作
    }
    
    @Override
    public void dissDialog() {
        dissLekeMissDialog();
        mTextView.setText("网络请求加载完毕");
    }
    
    @Override
    public void showDialog() {
        showLekeDialog();
        mTextView.setText("网络请求加载完毕");
    }
    }
    

    4.6.1、具体StudentMainPresenterImpl 的编写方式,继承StudentMainContract.Presenter接口,实现StudentMainContract.Presenter接口方法,activtiy需要做什么操作,这里是请求学段,在对应activity中调用getHttpStudentSectionData()请求学段方法;
    4.6.2、 getView()上面说了返回的是activity对象,该acitivty继承view接口,view接口负责Present和activity中uI更新的,所以 getView().showDialog是打开加载匡加载UI;
    4.6.3、 getModel().doHttpStudentSection是调用mode接口方法,具体网络请求操作实现在mode里面;
    4.6.4、接口成功回调接口个人差异不同,有不同编写方式。

    /**
    * Created by ljp on 2018/9/20.
    * <p>
    * 具体实现presenter
    */
     public class StudentMainPresenterImpl extends BasePresenter<StudentMainContract.View, 
    StudentMainContract.Model> implements StudentMainContract.Presenter {
    /**
     * 获取学段网络接口
     */
    @Override
    public void getHttpStudentSectionData() {
        getView().showDialog();
        getModel().doHttpStudentSection( new NetWorkCallback<List<StudentBeanResponse>>() {
            @Override
            public void onSucceed(List<StudentBeanResponse> studentBeanResponses) {
                if (getView() != null) {
                    getView().dissDialog();
                    getView().initStudentSelect(studentBeanResponses);
                }
            }
    
            @Override
            public void onFailed(Throwable error) {
                if (getView() != null) {
                    getView().dissDialog();
                }
            }
        });
    }
    
    @Override
    protected StudentMainContract.Model createModel() {
        return new StudentMainModelImpl((Context) getView());
    }
    }
    

    4.7、这里就是具体的mode,具体网络操作在这里实现,请求完成后,回调到Present中,在Present中拿到数据,使用View接口回调更新Activity.

     /**
     * Created by ljp on 2018/9/20.
    * <p>
    * 具体实现model
    */
    public class StudentMainModelImpl implements StudentMainContract.Model {
    
    protected RxHttpUtils rxHttpUtils;
    
    public StudentMainModelImpl(Context context) {
        rxHttpUtils = new RxHttpUtils(context);
    }
    
    /**
     * 学段选择数据请求接口
     *
     * @param netWorkCallback
     */
    @Override
    public void doHttpStudentSection(final NetWorkCallback<List<StudentBeanResponse>> netWorkCallback) {
    
        //TODO 这里的接口地址是本人随便写的,所以会请求失败这里用延迟表示网络加赞
        HttpRx.normal(rxHttpUtils.getSearchStudnt())
                .subscribe(new LekeObserver<List<StudentBeanResponse>>() {
                    @Override
                    public void onSucced(List<StudentBeanResponse> responses) {
                        if (netWorkCallback != null) {
                            netWorkCallback.onSucceed(responses);
                        }
                    }
    
                    @Override
                    public void onFailed(final Throwable error) {
                        if (netWorkCallback != null) {
                            new Handler().postDelayed(new Runnable(){
                                public void run() {
                                    netWorkCallback.onFailed(error);
                                }
                            }, 2000);
                        }
                    }
                });
    
    }}
    

    好了,mvp套路基本就是这样,如果使用mvc的话,activity将有大量的冗余代码,使用mvp解耦,代码清晰,便于维护。

    6、上面对MVP框架进行封装,接下来对Retrofi2.0进行快速封装

    6.1、第一步编写Retrofi2.0接口方法,AP地址、具题编写注解方式、和传参方式参考参考网上资料。

     /**
      * Created by ljp on 2018/9/12.
      * <p>
      * Retrofit 接口
      */
    public interface RetrofitApi {
    
      String API = "http://api.leke.cn/xxx/w/";//TODO api地址  具体拼接方式网上查询资料
    /**
     * 学段请求
     *
     * @return 学段名称
     */
      @GET("invoke.htm?_s=learn&_m=getStudentAllStages")
      Observable<Result<List<StudentBeanResponse>>> getStudySectionData();
    }
    

    6.2、第二步创建RetrofitApi的实例,

     /**
     * Created by ljp on 2018/9/12.
     */
    public class RetrofitHelper {
    
    private Context mCntext;
    private static RetrofitHelper instance = null;
    private Retrofit mRetrofit = null;
    
    /**
     * 单例
     * @param context
     * @return
     */
    public static RetrofitHelper getInstance(Context context){
        if (instance == null){
            instance = new RetrofitHelper(context);
        }
        return instance;
    }
    
    private RetrofitHelper(Context context){
        mCntext = context;
        resetApp();
    }
    
    /**
     * Retrofit初始化,传入URL地址
     */
    private void resetApp() {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(RetrofitApi.API)
                .client(getOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
    }
    
    /**
     *  OkHttpClient
     * 这里我们可以传入我们自己的头部名称信息在addHeader添加
     */
    private  OkHttpClient getOkHttpClient() {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        Request.Builder builder1 = request.newBuilder();
                        Request build = builder1.addHeader("xxx","xxx").build();
                        return chain.proceed(build);
                    }
                }).retryOnConnectionFailure(true).build();
        return client;
    }
    
    /**
     *
     * @return RetrofitService接口对象
     * 这里就拿到我们需要RetrofitApi对象
     */
    public RetrofitApi getRetrofitService(){
        return mRetrofit.create(RetrofitApi.class);
    }
    

    }
    6.3、创建工具类使用Retrofit调用接口方法

    /**
      * Created by ljp on 2018/9/12.
     * <p>
     * 网络操作工具类
     */
    public class RxHttpUtils {
    
    private RetrofitApi mRetrofitService;
    
    public RxHttpUtils(Context context) {
        this.mRetrofitService = RetrofitHelper.getInstance(context).getRetrofitService();
    }
    
    public Observable<Result<List<StudentBeanResponse>>> getSearchStudnt() {
        return mRetrofitService.getStudySectionData();
    }
    }
    

    6.4、开始使用封装的retrofit,如上代码中的具体mode-StudentMainModelImpl,由于我对retrofit最后一步进行封装,上面StudentMainModelImpl中doHttpStudentSection方法等同下面doHttpStudentSectionTwo方法,这个方法,关键需要加入两个调度方法:

    .subscribeOn(Schedulers.io()):I/O操作(读写文件,读写数据库,网络请求等)所使用的scheduler,内部实现一个无上限的线程池,可以重用空闲的线程,因此要比newThread()更有效率,请尽量不要将计算工作放入io()中,避免创建不必要的线程。

    .observeOn(AndroidSchedulers.mainThread()): 指定在android的主线程中执行操作

     public void doHttpStudentSectionTwo(final NetWorkCallback<List<StudentBeanResponse>> 
    netWorkCallback) {
       rxHttpUtils.getSearchStudnt()
                 .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new LekeObserver<Result<List<StudentBeanResponse>>>() {
                    @Override
                    public void onSucced(Result<List<StudentBeanResponse>> listResult) {
                        
                    }
    
                    @Override
                    public void onFailed(Throwable error) {
    
                    }
                });
    }
    

    框架结构图:


    mvp_retrofit_rxjava.png

    相关文章

      网友评论

        本文标题:android-简单快速封装MVP+Retrofit2.0+Rx

        本文链接:https://www.haomeiwen.com/subject/pxsatqtx.html