搭建自己的MVP框架

来源:互联网 发布:marry u软件下载 编辑:程序博客网 时间:2024/05/21 11:36

最近公司做的项目用到了MVP,期间查了很多资料,也看了github上别人搭的MVP框架,基本用到dagger2,也有的用到了别的技术,各种门类的都用,通过对比,选出了比较简易的搭建框架,用到的主要技术就是Retrofit2+RxJava2,期间也学了不少RxJavaRetrofit的高级用法,比如服务器返回的Responsetoken时效性,失败重试机制,compose的用法,感觉RxjavaRetrofit还是很强大的。由于有的涉及到业务代码,就不方便贴出。在此记录下我们自己的框架搭建教程,方便以后回顾使用。


1.)项目net目录结构

这里写图片描述

我将项目公用的代码抽出为一个netmodule,可以方便以后重复使用。项目的目录包名

  • app 存放的是一些全局变量,比如我们使用retrofitbase_url

  • mvp.base mvp的抽象提出到base中,方便不同activityfragment使用

  • recyclerview 列表抽象封装,仿照BRVAH 的封装

  • retrofit_encapsulation retrofit的封装

  • rx_encapsulation rxjava的封装

  • type 泛型的实例化

接下来一一介绍这几个包名下的封装


2.) app

这个是仿照慕课网,这门课的部分封装,其实与mvp的封装没多大关系,但是这种封装可以统一管理多个全局变量的问题,当公司有多条业务线时,我们可以统一存储。

主要有三个类,AppConstant, ConfigKeys,Configurator

ConfigKeys

public enum ConfigKeys {    API_HOST,    APPLICATION_CONTEXT,    CONFIG_READY,    HANDLER,}

存放的是全局变量的引用,枚举方便引用。

ConfigKeys

public class Configurator {    private static final HashMap<Object, Object> ANT_CONFIGS = new HashMap<>();    private static final Handler HANDLER = new Handler();    public Configurator() {        ANT_CONFIGS.put(ConfigKeys.CONFIG_READY, false);    }    public static Configurator getInstance() {        return Holder.INSTANCE;    }    private static class Holder {        private static final Configurator INSTANCE = new Configurator();    }    public final Configurator withApiHost(String apiHost) {        ANT_CONFIGS.put(ConfigKeys.API_HOST, apiHost);        return this;    }    final Configurator withApplicationContext(Context context) {        ANT_CONFIGS.put(ConfigKeys.APPLICATION_CONTEXT, context.getApplicationContext());        return this;    }    private void checkConfiguration() {        final boolean isReady = (boolean) ANT_CONFIGS.get(ConfigKeys.CONFIG_READY);        if (!isReady) {            throw new RuntimeException("Configuration is not ready,call configure");        }    }    /**     * 配置完成     */    public final void configure() {        ANT_CONFIGS.put(ConfigKeys.CONFIG_READY, true);    }    @SuppressWarnings("unused")    final <T> T getConfiguration(Object key) {        checkConfiguration();        final Object value = ANT_CONFIGS.get(key);        if (value == null) {            throw new NullPointerException(key.toString() + " IS NULL");        }        return (T) ANT_CONFIGS.get(key);    }}

Hashmap来作为容器,存放全局变量,由于要存放的类型不是固定的类型,使用了Object来作为值得存储类型。

AppConstant

public class AppConstant {    public static Configurator init(Context context) {        Configurator.getInstance()                .withApplicationContext(context);        return Configurator.getInstance();    }    public static Configurator getConfigurator() {        return Configurator.getInstance();    }    @SuppressWarnings("unused")    public static <T> T getConfiguration(Object key) {        return getConfigurator().getConfiguration(key);    }    public static Context getApplicationContext() {        return getConfigurator().getConfiguration(ConfigKeys.APPLICATION_CONTEXT);    }    public static String getApiHost() {        return getConfigurator().getConfiguration(ConfigKeys.API_HOST);    }

我们每次要去获取值时候,直接通过AppConstant来获取,比如获取存放的BaseUrl,可以这样调用 AppConstant.getApiHost(),每次获取只需要从AppConstant获取

既然讲了获取全局变量的值当然要讲怎么存值。我们在使用这个包下面的东西时只需要这样使用

public class App extends Application{    String HOST = "http://news-at.zhihu.com/api/4/";  //自己公司的域名地址    @Override    public void onCreate() {        super.onCreate();        AppConstant.init(this)                .withApiHost(HOST)                .configure();    }}

在Application中做统一存储


3.) mvp.base

这个包下面是所有mvp抽象的封装,所以东西还是挺多的。

主要有 BaseView, BasePresenter, BaseMVPActivity, BaseModel, BaseActivity几个类,

BaseView

public interface BaseView {    Context getContext();    void dealMsgError(String msg);}

所有view接口的公用回调,这里如果不做处理,那么其他地方都要写,所以这里写,其他接口只需要继承这个接口就行了。

BaseModel

这个接口是一个空实现,只是为了抽象解耦的作用。

BasePresenter

public abstract class BasePresenter<V extends BaseView, M extends BaseModel> {    protected V mView;    protected M mModel;    protected RxManager rxManager = new RxManager();    public void attachVM(V view, M model) {        mView = view;        mModel = model;    }    public void detachVM(){        rxManager.clear();        mView = null;        mModel = null;    }}

这个抽象类里面用到了两个泛型,是为了规范其实现类,同时这个类的实现是给ActivityFragment实现和attach。同时这个类里面还用到了RxManager,主要就是对CompositeDisposable的包装,管理RxJava的生命周期,当页面结束时,自动取消RxJava的事件传递。

BaseMVPActivity

public abstract class BaseMVPActivity<P extends BasePresenter, M extends BaseModel> extends BaseActivity implements BaseView{    protected P mPresenter;    protected M mModel;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        mPresenter = TUtil.getT(this, 0);        mModel = TUtil.getT(this, 1);        mPresenter.attachVM(this, mModel);        super.onCreate(savedInstanceState);    }    @Override    protected void onDestroy() {        super.onDestroy();        if(mPresenter != null){            mPresenter.detachVM();        }    }    @Override    public Context getContext() {        return mContext;    }    @Override    public void dealMsgError(String msg) {    }}

这个类是对Activity中使用mvp的封装,我们如果使用只需要继承这个抽象的acitivity就行了,同时指定需要的泛型具体实现,在BaseMVPActivity中自动实例化泛型的参数。同时在抽象类中实现BaseView,可以避免在具体的activity中的空实现问题。

BaseActivity 就是不使用mvp的抽象,如果我们页面没有使用网络请求,可以不必实现BaseMVPActivity


4.) recyclerview

这个包路径是对recyclerview的封装,这个只是纯粹为了简化recyclerview的使用,与mvp无关,就不多说了,代码后面贴出,直接看代码。


5) retrofit_encapsulation

这个是对网络请求的抽象,我第一次封装时,没有考虑到多个BaseUrl的问题,Retrofit的创建都写死了,后来看了这门课Retrofit 从入门封装到源码解析,参照他的教程重新改了一遍。这个包下主要有两个类。分别是RetrofitManagerRetrofitNet

RetrofitManager

public class RetrofitManager {    private static Context mContext;    private OkHttpClient mOkHttpClient;    private static class InnerHolder {        private static RetrofitManager INSTACE = new RetrofitManager();    }    /**     * 需要context时候调用,可以在Application中调用     * @param context     */    public static void init(Context context) {        //防止内存泄露        mContext = context.getApplicationContext();    }    public static RetrofitManager getInstance() {        return InnerHolder.INSTACE;    }    public Retrofit newRetrofit(String url) {        // 拿到Retrofit实例        return new Retrofit.Builder()                .baseUrl(url)                //引入Gson解析库 ,就可以直接以实体的形式拿到返回值                .addConverterFactory(GsonConverterFactory.create())                //加入我们自定义的Gson解析库,就可以更友好的处理错误                //.addConverterFactory(GsonConverterFactory.create())                .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))                //将我们客制化的OkHttp实例传入                .client(mOkHttpClient)                .build();    }    private RetrofitManager() {        //在构造方法里 最终是为了得到一个单例的OkhttpClient实例        OkHttpClient.Builder builder = new OkHttpClient.Builder();        //加入自己业务需要的拦截器        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {            @Override            public void log(String message) {                Log.i("OkHttp", message);            }        });        //缓存设置        //构建缓存位置        mOkHttpClient = builder.addInterceptor(loggingInterceptor).build();    }}

基本抄的那个视频的代码,主要就是实例化Retrofit,同时我们可以在实例化Retrofit的同时,加入Okhttp拦截器,比如很常用的日志拦截器HttpLoggingInterceptor,由于Retrofit默认不返回Json,加入这个拦截器,可以极大提高我们调试技巧。同时也可以加入其他拦截器,可以根据业务自己判定。同时可以根据自己业务加入自定义的Gson解析。比如这样的结构

{    errorCode:1,    msg:"成功",    data:{        .....    }}

我们只需要data里面的内容,这时候我们就可以自定义Gson解析器,其实也就是改造GsonConverterFactory的代码而已。

RetrofitNet

public class RetrofitNet {    private static Retrofit mRetrofit;    public static <T> T getService(Class<T> clazz) {        return retrofit().create(clazz);    }    private static Retrofit retrofit() {        if (mRetrofit == null) {            mRetrofit = RetrofitManager.getInstance().newRetrofit(AppConstant.getApiHost());        }        return mRetrofit;    }}

通过这个去获取Retrofit的实例,同时去构建Service

一般我们去获取Retrofit的原则是这样的

  • 一个BaseUrl一个Retrofit实例

  • 多个Retrofit共用一个OkHttp实例


6.) rx_encapsulation

对于RxJava的生命周期处理

RxManager

public class RxManager {    private CompositeDisposable disposable = new CompositeDisposable();    public void add(Disposable d){        disposable.add(d);    }    public void clear(){        disposable.dispose();        disposable = null;    }}

当生命周期结束,自动取消RxJava事件传递


7.) type

这个包下面的类是为了实例化Presenter和Model

public class TUtil {    public static <T> T getT(Object o, int i) {        try {            return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (ClassCastException e) {            e.printStackTrace();        }        return null;    }}

利用反射去实例化Presenter和Model,来达到不用在Activity中手动实例化的目的。

8.) 使用

a. Contract规范View和Presenter,减少接口的创建

public interface HttpContract {    interface View extends BaseView{        void getWelecomeDataSuccess(WelcomeBean welcomeBean);        void getDailyList(DailyListBean respBean);    }    abstract class Presenter extends BasePresenter<View, HttpModel>{        public abstract void getWelecomData(String res);        public abstract void getDailyList();    }}

b. Model

public class HttpModel implements BaseModel{    public Observable<DailyListBean> getDailyList(){        return RetrofitNet.getService(HttpService.class).getDailyList();    }}

统一调用HttpService的方法。

c.Presenter

public class HttpPresenter extends HttpContract.Presenter{    @Override    public void getDailyList() {        rxManager.add(mModel.getDailyList().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<DailyListBean>() {            @Override            public void accept(DailyListBean dailyListBean) throws Exception {                mView.getDailyList(dailyListBean);            }        }, new Consumer<Throwable>() {            @Override            public void accept(Throwable throwable) throws Exception {                mView.dealMsgError(throwable.getMessage());            }        }));    }}

d. Activity中指定泛型参数,统一实例化

public class MVPActivity extends BaseMVPActivity<HttpPresenter, HttpModel> implements HttpContract.View//使用 mPresenter.getDailyList();

基本使用也就这多了。

9. 结尾

其实还有好多没写出来,一是自己能力不够,二是对RxJava掌握还是不够好,以后会写点Retrofit的源码,加深下自己网络框架方面的学习。

源码地址