Android:Retrofit + RxJava MVP 架构实际应用

前言

目前 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

> extends BaseActivity implements IBaseView { //主Presenter protected P mPresenter; //多个Presenter时候需要的容器 private ArraySet 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 void addToPresenters(T presenter) { presenter.attachView(this); mPresenters.add(presenter); } }

  • BasePresenter:Presenter 基类,里面包含 Presenter 需要公共方法,例如统一处理内存泄漏问题。Presenter 我坚决不把 ContextActivityFragment之类的)传进来,要使用 Context 就用 ApplicationContext 。带个 Context 可能有些方便,但是我觉得很影响我的单元测试。
public abstract class BasePresenter {

    private V mView;

    //Disposable容器,收集Disposable,主要用于内存泄漏管理
    private CompositeDisposable mDisposables;

    protected V getView() {
        return mView;
    }

    /**
     * @param view 绑定View
     */
    @SuppressWarnings("unchecked")
    public  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 getModel(Class 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 getApiService(Class 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 extends DisposableSingleObserver {

    @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> delegate =
                (CallAdapter>) retrofit.nextCallAdapter(this, returnType, annotations);
        return new CallAdapter() {
            @Override
            public Type responseType() {
                return delegate.responseType();
            }

            @Override
            public Object adapt(@NonNull Call 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, ? 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 create(final Class 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 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。
完整例子

图片发自App
  • 目录结构
    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 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 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() {
            @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 observer) {
        return getApiService().getUser(userName).subscribeWith(observer);
    }

}
  • ApiService: 接口管理
public interface ApiService {

    @GET("users/{username}")
    Single getUser(@Path("username") String userName);

    @GET("orgs/{org}")
    Single getOrg(@Path("org") String org);

}

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

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

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

public abstract class HttpResultObserver2 extends HttpResultObserver> {

    @Override
    public void onSuccess(Result 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 泛型类型为 >

Single> uploadImage(...)

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

总结

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

你可能感兴趣的:(Android:Retrofit + RxJava MVP 架构实际应用)