Android:Retrofit + RxJava MVP 架构

作者: FindUById | 来源:发表于2018-09-20 17:25 被阅读94次

    前言

    目前 MVP 架构在 Android app 开发中非常流行。通过 谷歌官方 例子和个人的一些理解形成自己的 Retrofit + RxJava 的 MVP 架构,并且用这实际开发当中。看此文需要一定的 Retrofit 和 RxJava 基础,对 mvp-clean 有一定了解。

    MVP 简单介绍

    • M:Model,数据处理层,获取数据、整理数据等;
    • V:View,展示数据层,ActivityFragmentDialog 等都可以充当这个角色;
    • P:Presenter,逻辑处理层,处理数据和 UI 的关系、处理来自 V 层逻辑等;

    下图分析:
    V 层和 M 层直接关联了吗?并没有,这是 MVP 和 MVC 一个很大的区别。V 层和 M 层是通过 P 层联系起来的。反映到代码中就是 P 持有 V 实例和 M 实例。

    MVP

    具体实现

    1. 总体包目录

    • base 包:一些 base 基类,BaseFragmentBaseMvpFragmentBaseActivityBaseMvpFragment 差不多。
    • net 包:网络请求和处理相关的类在此包下。
    • utils 包:需要用到的简单工具类。
    image.png

    2. 关键类的说明

    • BaseMvpActivity:封装了公共方法的 Activity,用了处理项目里 Activity一般都会使用到的方法,例如配合 Presenter 统一处理内存泄漏问题。注意:BaseMvpActivity Presenter 泛型是主 Presenter,其他的为次 Presenter,需要在 getPresenter() 中初始化,并且添加到 Presenter 集合中。
    public abstract class BaseMvpActivity<P extends BasePresenter<? extends IBaseView>> extends BaseActivity implements IBaseView {
    
        //主Presenter
        protected P mPresenter;
        //多个Presenter时候需要的容器
        private ArraySet<BasePresenter> mPresenters = new ArraySet<>(4);
    
        @Override
        protected void init(@Nullable Bundle savedInstanceState) {
            initLoading();
            mPresenter = getPresenter();
            addToPresenters(mPresenter);
            initView();
            initListener();
            initData();
        }
    
        @Override
        protected void onDestroy() {
            for (BasePresenter presenter : mPresenters) {
                presenter.detachView();
            }
            mPresenters.clear();
            super.onDestroy();
        }
    
        @Override
        public void showLoading() {
    
        }
    
        @Override
        public void showLoading(String msg) {
    
        }
    
        @Override
        public void hideLoading() {
    
        }
    
        @Override
        public void showMsg(String msg) {
            toastS(msg);
        }
    
        /**
         * 初始化Presenter,其他多个Presenter也在该方法创建并调用addToPresenters加入到集合
         * @return 主Presenter
         */
        protected abstract P getPresenter();
    
        /**
         * 根据具体项目需求创建loading
         */
        protected void initLoading() {
    
        }
    
        /**
         * 初始化View
         */
        protected void initView(){
    
        }
    
        /**
         * 初始化Listener
         */
        protected abstract void initListener();
    
        /**
         * 初始化数据
         */
        protected abstract void initData();
    
        /**
         * 把其他的Presenter添加到Presenters集合里
         * 这样会自动绑定View和管理内存释放
         */
        protected <T extends BasePresenter> void addToPresenters(T presenter) {
            presenter.attachView(this);
            mPresenters.add(presenter);
        }
    
    }
    
    
    • BasePresenter:Presenter 基类,里面包含 Presenter 需要公共方法,例如统一处理内存泄漏问题。Presenter 我坚决不把 ContextActivityFragment之类的)传进来,要使用 Context 就用 ApplicationContext 。带个 Context 可能有些方便,但是我觉得很影响我的单元测试。
    public abstract class BasePresenter<V extends IBaseView> {
    
        private V mView;
    
        //Disposable容器,收集Disposable,主要用于内存泄漏管理
        private CompositeDisposable mDisposables;
    
        protected V getView() {
            return mView;
        }
    
        /**
         * @param view 绑定View
         */
        @SuppressWarnings("unchecked")
        public <T extends IBaseView> void attachView(T view) {
            this.mView = (V) view;
            mDisposables = new CompositeDisposable();
        }
    
        /**
         * 解绑关联
         */
        public void detachView() {
            mDisposables.clear();
            mDisposables = null;
            mView = null;
        }
    
        /**
         * @param disposable 添加Disposable到CompositeDisposable
         *                   通过解除disposable处理内存泄漏问题
         */
        protected boolean addDisposable(Disposable disposable) {
            if (isNullOrDisposed(disposable)) {
                return false;
            }
            return mDisposables.add(disposable);
        }
    
        /**
         * @param d 判断d是否为空或者dispose
         * @return true:一次任务未开始或者已结束
         */
        protected boolean isNullOrDisposed(Disposable d) {
            return d == null || d.isDisposed();
        }
    
        /**
         * @param d 判断d是否dispose
         * @return true:一次任务还未结束
         */
        protected boolean isNotDisposed(Disposable d) {
            return d != null && !d.isDisposed();
        }
    
        /**
         * 获取 Model 实例
         */
        protected <M extends IBaseModel> M getModel(Class<M> clazz) {
            return ModelManager.getInstance().create(clazz);
        }
    
    }
    
    • HttpManager:网络访问管理,例如 baseUrl 配置;
    public final class HttpManager {
    
        private Retrofit mRetrofit;
        private String mBaseUrl;
        private OkHttpClient mOkHttpClient;
        private Boolean debug = true;
    
        private static final Logger LOG = Logger.getLogger(HttpManager.class.getName());
    
        private HttpManager() {
        }
    
        public static HttpManager getInstance() {
            return Holder.INSTANCE;
        }
    
        /**
         * @param mBaseUrl 设置BaseUrl
         *                 放在第一位设置
         */
        public HttpManager setBaseUrl(String mBaseUrl) {
            this.mBaseUrl = mBaseUrl;
            return Holder.INSTANCE;
        }
    
        /**
         * 设置OkHttpClient
         */
        public HttpManager setOkHttpClient(OkHttpClient okHttpClient) {
            this.mOkHttpClient = okHttpClient;
            return Holder.INSTANCE;
        }
    
        /**
         * @param retrofit 设置retrofit
         *                 放在最后设置
         */
        public void setRetrofit(Retrofit retrofit) {
            this.mRetrofit = retrofit;
        }
    
        /**
         * debug
         */
        public HttpManager setDebug(Boolean debug) {
            this.debug = debug;
            return Holder.INSTANCE;
        }
    
        /**
         * @return mRetrofit.create(clazz)
         */
        public <T> T getApiService(Class<T> clazz) {
            return mRetrofit.create(clazz);
        }
    
        /**
         * 自带创建retrofit
         */
        public Retrofit createRetrofit() {
            Retrofit.Builder builder = new Retrofit.Builder()
                    .baseUrl(mBaseUrl)
                    .client(mOkHttpClient)
                    .addConverterFactory(ScalarsConverterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(ObserveOnMainCallAdapterFactory.createMainScheduler())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()));
            return builder.build();
        }
    
        /**
         * @return OkHttpclient
         */
        public OkHttpClient createDefaultClient() {
            ...
        }
    
        private static class Holder {
            private static final HttpManager INSTANCE = new HttpManager();
        }
    
        /**
         * info 等级log
         */
        public static class InterceptorLogInfo implements HttpLoggingInterceptor.Logger {
            @Override
            public void log(@NonNull String message) {
                LOG.log(Level.INFO, message);
            }
        }
    
    }
    
    • HttpResultObserver:DisposableSingleObserver 的子类,用来接收网络数据回调;
    public abstract class HttpResultObserver<T> extends DisposableSingleObserver<T> {
    
        @Override
        public void onSuccess(T t) {
            //dispose 一次任务
            dispose();
            onResult(t);
        }
    
        @Override
        public void onError(Throwable e) {
            //dispose 一次任务
            dispose();
            onFailure(e);
        }
    
        /**
         * @param t 获取结果
         */
        protected abstract void onResult(T t);
    
        /**
         * @param e 获取结果失败
         */
        protected abstract void onFailure(Throwable e);
    
    }
    
    • ObserveOnMainCallAdapterFactory:指定数据回调到主线程。配合 Retrofit 使用的。
    public final class ObserveOnMainCallAdapterFactory extends CallAdapter.Factory{
    
        private final Scheduler mScheduler;
    
        public ObserveOnMainCallAdapterFactory(Scheduler scheduler) {
            this.mScheduler = scheduler;
        }
    
        @Nullable
        @Override
        public CallAdapter<?, ?> get(@NonNull Type returnType, @NonNull Annotation[] annotations, @NonNull Retrofit retrofit) {
            Class<?> rawType = getRawType(returnType);
            if (rawType != Single.class) {
                return null;
            }
            final CallAdapter<Object, Single<?>> delegate =
                    (CallAdapter<Object, Single<?>>) retrofit.nextCallAdapter(this, returnType, annotations);
            return new CallAdapter<Object, Object>() {
                @Override
                public Type responseType() {
                    return delegate.responseType();
                }
    
                @Override
                public Object adapt(@NonNull Call<Object> call) {
                    Single<?> s = delegate.adapt(call);
                    return s.observeOn(mScheduler);
                }
            };
        }
    
        /**
         * 在android主线程处理下游数据
         */
        public static CallAdapter.Factory createMainScheduler() {
            return new ObserveOnMainCallAdapterFactory(AndroidSchedulers.mainThread());
        }
    }
    
    • ModelManager:创建管理 Model,这里我把 Model 看做是数据仓库,用来提供直观数据(比如提供 User 数据)的。Model 是通过 ModelManager 创建的。每个 Model 只创建一次。因为 Model 相对比较独立而且不需要频繁创建销毁。
    public final class ModelManager {
    
        private final ConcurrentHashMap<Class<? extends IBaseModel>, ? extends IBaseModel> DATA_CACHE;
    
        private ModelManager() {
            DATA_CACHE = new ConcurrentHashMap<>(8);
        }
    
        /**
         * @return ModelManager单例实例
         */
        public static ModelManager getInstance() {
            return Holder.INSTANCE;
        }
    
        private static class Holder {
            private static final ModelManager INSTANCE = new ModelManager();
        }
    
        /**
         * 创建获取 Model 层实例
         * @param clazz IBaseModel 子类 class
         */
        @SuppressWarnings("unchecked")
        public <M extends IBaseModel> M create(final Class<M> clazz) {
    
            IBaseModel model = DATA_CACHE.get(clazz);
            if (model != null) {
                return (M) model;
            }
    
            synchronized (DATA_CACHE) {
                model = DATA_CACHE.get(clazz);
                if (model == null) {
                    try {
                        Constructor<M> constructor = clazz.getDeclaredConstructor();
                        constructor.setAccessible(true);
                        model = constructor.newInstance();
                    } catch (... e) {
                    ....
                    }
                }
            }
            return (M) model;
        }
    
    }
    

    更多代码细节可以去 我的GitHub 看下。

    3. 使用方式

    以在一个 Activity 请求 GitHub 两个接口为例,一个接口请求个人用户信息,一个接口请求公司用户信息,所以例子将模拟两个模块,user 和 orgs 。使用过程中你会发现一个View 可以有多个 Presenter,一个 Activity 可以实现多个 View 接口。其实还不止这些,其实 Presenter 也可以用有多个 Model。
    完整例子

    Android:Retrofit + RxJava MVP 架构实际应用
    • 目录结构
      XxContract 为模板创建生成,可以在 Setting > Editor > File and Code Templates > Files 设置。我的模板如下:
    package ${PACKAGE_NAME};
    #parse("File Header.java")
    public interface ${NAME}{
        interface View extends IBaseView{
        }
        interface Presenter{   
        }
    }
    
    目录结构
    • 在合适的位置初始化 HttpManager
    public class SimpleApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            HttpManager.getInstance()
                    .setBaseUrl("https://api.github.com/")
                    .setDebug(BuildConfig.DEBUG)
                    .setOkHttpClient(HttpManager.getInstance().createDefaultClient())
                    .setRetrofit(HttpManager.getInstance().createRetrofit());
        }
    
    }
    
    • MainActivity: 一般主页可能会有多个 Presenter,这里有两个 Presenter
    public class MainActivity extends BaseMvpActivity<UserPresenter> implements UserContract.View, OrgContract.View {
          ...
        //公司信息Presenter(次Presenter)
        private OrgPresenter mOrgPresenter;
    
        private ProgressDialog mLoading;
    
        @Override
        protected int getLayoutId() {
            return R.layout.activity_main;
        }
    
        @Override
        protected void initLoading() {
            mLoading = new ProgressDialog(this);
        }
    
        @Override
        protected UserPresenter getPresenter() {
            mOrgPresenter = new OrgPresenter();
            addToPresenters(mOrgPresenter);
            return new UserPresenter();
        }
    
        @Override
        protected void initView() {
           ...
        }
    
        @Override
        protected void initListener() {
    
            //点击获取个人信息
            btnClick.setOnClickListener(new android.view.View.OnClickListener() {
                @Override
                public void onClick(android.view.View v) {
                    mPresenter.getUser("togallop");
                }
            });
    
            //点击获取用户信息
            btnClick2.setOnClickListener(new android.view.View.OnClickListener() {
                @Override
                public void onClick(android.view.View v) {
                    mOrgPresenter.getOrg("google");
                }
            });
        }
    
        @Override
        public void showUser(String msg) {
            tvMsg.setText(msg);
        }
    
        @Override
        public void showOrg(String org) {
            tvMsg.setText(org);
        }
    
        @Override
        public void showLoading(String msg) {
            mLoading.setMessage(msg);
            if (!mLoading.isShowing()) {
                mLoading.show();
            }
        }
    
        @Override
        public void showLoading() {
            if (!mLoading.isShowing()) {
                mLoading.show();
            }
        }
    
        @Override
        public void showMsg(String msg) {
            toastS(msg);
        }
    
        @Override
        public void hideLoading() {
            if (mLoading.isShowing()) {
                mLoading.dismiss();
            }
        }
    }
    
    
    • UserPresenter:联结 UserContract.ViewUserModel,在 Presenter 里,addDisposable(disposable) 主要作用是用来防止内存泄漏的,如果网络请求还没结束,页面已经关闭,则可能造成内存泄漏,通过 addDisposable(disposable) 管理,可以切断 P 和 V 的关联,从而防止内存泄漏,也可以取消网络请求。
    public class UserPresenter extends BasePresenter<UserContract.View> implements UserContract.Presenter {
    
        private UserModel mModel;
    
        public UserPresenter() {
            mModel = getModel(UserModel.class);
        }
    
        @Override
        public void getUser(String userName) {
    
            //做一些判断
            if (TextUtils.isEmpty(userName)) {
                getView().showMsg("用户名不能为空");
                return;
            }
    
            //显示loading
            getView().showLoading("正在加载...");
            Disposable disposable = mModel.getUser(userName, new HttpResultObserver<String>() {
                @Override
                protected void onResult(String s) {
                    //结果回调显示
                    getView().showUser(s);
                    getView().hideLoading();
                }
    
                @Override
                protected void onFailure(Throwable e) {
                    //获取数据是失败回调处理
                    getView().showMsg(e.getMessage());
                    getView().hideLoading();
                }
            });
            addDisposable(disposable);
        }
    }
    
    • UserMode: 就是一个用户相关的模块,和用户相关的请求都可以写在这里
    public class UserModel extends BaseModel {
    
        public Disposable getUser(String userName, HttpResultObserver<String> observer) {
            return getApiService().getUser(userName).subscribeWith(observer);
        }
    
    }
    
    • ApiService: 接口管理
    public interface ApiService {
    
        @GET("users/{username}")
        Single<String> getUser(@Path("username") String userName);
    
        @GET("orgs/{org}")
        Single<String> getOrg(@Path("org") String org);
    
    }
    

    对于有后台有固定返回格式的的数据,也可以统一处理。比如返回结果类似这样的:

    {
        code:200
        msg:"success"
        data:{}
    }
    

    那么可以这么处理,创建回调泛型类

    public abstract class HttpResultObserver2<T> extends HttpResultObserver<Result<T>> {
    
        @Override
        public void onSuccess(Result<T> t) {
            switch (t.code) {
                case 200:
                    onSuccess(t.data);
                    break;
                default: {
                    HttpResultException e = new HttpResultException(t.code, t.msg);
                    toast(e.getMsg());
                    onFailure(HttpError.RESULT_ERROR, e);
                    e.printStackTrace();
                    break;
                }
            }
        }
    
        @Override
        public void onError(Throwable t) {
            onFailure(error, (Exception) t);
        }
    
        /**
         * 请求成功回调
         *
         * @param result 回调数据
         */
        public abstract void onResult(T result);
    
        /**
         * 请求失败回调
         *
         * @param error,自定义异常
         * @param e     失败异常信息
         */
        public abstract void onFailure(HttpError error, @NonNull Exception e);
    
    }
    

    APIService:Single 泛型类型为 <Result<xxx>>

    Single<Result<String>> uploadImage(...)
    

    使用方式和上面介绍的一样。

    总结

    MVP 架构是死的,具体实现是活的;任何离开具体业务的代码都是不现实的,我的这部分代码只是一个参考,可以根据具体需求在我的这份代码上扩展,实现自己的需求。举些例子:要统一处理异常信息,可以继承 HttpResultObserver 统一处理异常;处理内存泄漏也可以有很多方式,RxLifecycle/Reference/LifeCycle 等;不一定要用 Single,用 Flowable/Maybe 等都行,看具体需要,网络请求是一次性的,所以我用 Single,处理也方便。总而言之,最好的不一定适合你,适合你的才是最好的。

    相关文章

      网友评论

      • 无心下棋:共用presenter的有。多个presenter共用activity的这是第一个。
        FindUById:@无心下棋 不想写重复代码所以好多个presenter

      本文标题:Android:Retrofit + RxJava MVP 架构

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