框架【库】android进阶MVP

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

2018-10-31  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读