Android MVP 框架搭建

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ym4189/article/details/86016276

前言

本文主要根据自身项目的使用和对MVP的理解,搭建符合自身项目情况的MVP架构。

关于MVP

  • M(Model)负责数据的请求,解析,过滤等数据操作。
  • V(View)负责处理UI,通常以Activity Fragment的形式出现。
  • P(Presenter)View Model中间件,交互的桥梁。

MVP的好处

  • 分离了UI逻辑和业务逻辑,降低了耦合。
  • Activity只处理UI相关操作,代码变得更加简洁。
  • UI逻辑和业务逻辑抽象到接口中,方便阅读及维护。
  • 把业务逻辑抽到Presenter中去,避免复杂业务逻辑造成的内存泄漏。

具体实现

1.view

IView:一般情况下,做数据请求都有显示加载框、请求成功、请求失败等操作,我们把这些共有的功能封装到IView中。

public interface IView {
    /**
     * 显示加载框
     */
    void showLoading();

    /**
     * 隐藏加载框
     */
    void dismissLoading();

    /**
     * 网络请求失败
     *
     * @param ex   异常信息
     * @param code 错误码
     * @param msg  错误信息
     */
    void onFail(Throwable ex, String code, String msg);

    /**
     * 网络错误
     */
    void onNetError();

}

2.Presenter

IPresenter:为了避免持有View的Presenter做耗时操作而引起的内存泄漏,我们的Presenter应该和宿主Activity/Fragment生命周期绑定。

public interface IPresenter<T extends IView> {
    /**
     * 绑定view
     * @param view view
     */
    void attachView(T view);

    /**
     * 分离view
     */
    void detachView();

    /**
     * 判断view是否已经销毁
     * @return true 未销毁
     */
    boolean isViewAttach();

}

BasePresenter:抽象的persenter业务处理层。关联抽象层view和抽象model。

public abstract class BasePresenter<T extends IView, K extends IModel> implements IPresenter<T> {
    protected K mModel;
    private WeakReference<T> weakReference;

    @Override
    public void attachView(T view) {
    	// 使用弱引用持有view对象,防止内存泄漏
        weakReference = new WeakReference<>(view);
        if (this.mModel == null) {
            this.mModel = createModule();
        }
    }

    @Override
    public void detachView() {
        if (isViewAttach()) {
            weakReference.clear();
            weakReference = null;
        }
        if (mModel != null) {
            mModel.unSubscribe();
            mModel = null;
        }
    }

    @Override
    public boolean isViewAttach() {
        return weakReference != null && weakReference.get() != null;
    }

    protected T getView() {
        return weakReference.get();
    }

    protected void showLoading() {
        if (isViewAttach()) {
            getView().showLoading();
        }
    }

    protected void onFail(Throwable ex, String code, String msg) {
        if (isViewAttach()) {
            getView().onFail(ex, code, msg);
        }
    }

    protected void onNetError() {
        if (isViewAttach()) {
            getView().onNetError();
        }
    }

    protected void dismissLoading() {
        if (isViewAttach()) {
            getView().dismissLoading();
        }
    }

    /**
     * 由外部创建 module
     *
     * @return module
     */
    protected abstract K createModule();

}

3.model

IModel:由于项目使用Rxjava+Retrofit2.0+Okhttp,所以我在model层对Rxjava进行绑定和解绑,防止内存泄漏。

public interface IModel {
    void unSubscribe();

    void addSubscribe(Subscription subscription);
}

BaseModel:实现对Rxjava绑定和解绑,初始化ApiService。

public class BaseModel implements IModel {
    protected ApiService mApi;
    private CompositeSubscription mCompositeSubscription;

    public BaseModel() {
        this.mApi = RetrofitHelper.getInstance().createApiService(AppConstants.BASE_SERVER_IP);
    }

    @Override
    public void unSubscribe() {
        if (mCompositeSubscription != null && !mCompositeSubscription.isUnsubscribed()) {
            mCompositeSubscription.clear();
            mCompositeSubscription.unsubscribe();
        }
    }

    @Override
    public void addSubscribe(Subscription subscription) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        mCompositeSubscription.add(subscription);
    }
}

4.BaseMvpActivity基类
通过泛型规定Presenter,并且暴露抽象方法createPresenter()给子类来创建Presenter,基类实现IView中的公共方法,减少子类代码的冗余。至于BaseMvpActivity功能根据项目业务需求进行封装。

public abstract class BaseMvpActivity<P extends BasePresenter> extends AppCompatActivity implements IView {
    protected P mPresenter;
    private Unbinder unbinder;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        unbinder = ButterKnife.bind(this);
    	// 初始化Presenter
        initPresenter();
    }

    private void initPresenter() {
        mPresenter = createPresenter();
        // 完成Presenter和view的绑定
        if (mPresenter != null) {
            mPresenter.attachView(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 将Presenter和view解绑
        if (mPresenter != null) {
            mPresenter.detachView();
            mPresenter = null;
        }
        // 解绑ButterKnife
        if (unbinder != null) {
            unbinder.unbind();
        }      
    }

    @Override
    public void showLoading() {
       // 这里实现自己的加载弹框
    }

    @Override
    public void dismissLoading() {
       // 取消弹框
    }

    @Override
    public void onFail(Throwable ex, String code, String msg) {
        // 基础的网络请求失败处理
    }

    @Override
    public void onNetError() {
        // 网络错误处理
    }

    /**
     * 页面初始化数据
     */
    protected void initData() {

    }

    /**
     * 创建Presenter
     *
     * @return Presenter
     */
    protected abstract P createPresenter();

    /**
     * 获取当前activity的id
     *
     * @return 当前xml的布局res ID
     */
    protected abstract int getLayoutId();

    /**
     * 初始化view控件
     */
    protected abstract void initView();

}

使用MVP

1.契约类Contract

通过契约类来管理Model、View、Presenter的所有接口,这样使得Presenter和View有哪些功能一目了然,维护起来也方便,同时使得View与Presenter一一对应,并有效地减少类的数目。

public interface LoginContract {

    interface View extends IView {
    	 /**
         * 登录成功
         */
        void onLoginSuccess(UserInfo data);
         /**
         * 登录失败
         */
        void onLoginFail(Throwable ex, String code, String msg);
    }

    interface Presenter {
        void login(String phone, String password);
    }

    interface Model extends IModel {
    	/**
         * 登录
         *
         * @param map 用户登陆信息
         * @param subscriber 回调
         */
        void login(Map map, Subscriber subscriber);

    }

}

2.LoginPresenter

public class LoginPresenter extends BasePresenter<LoginContract.View, LoginContract.Model> implements LoginContract.Presenter {

    @Override
    protected LoginContract.Model createModule() {
        return new LoginModel();
    }

    @Override
    public void login(String phone, String password) {
        showLoading();
        Map<String, String> params = new HashMap<>();
        params.put("phone", phone);
        params.put("password", password);
        
        mModel.login(params, new BaseSubscriber<UserInfo>(new CallBackListener<UserInfo>() {
            @Override
            public void onSuccess(String code, UserInfo data) {
                dismissLoading();
                if (isViewAttach()) {
                    getView().onLoginSuccess(data);
                }
            }

            @Override
            public void onFailed(Throwable ex, String code, String msg) {
            	dismissLoading();
                if (isViewAttach()) {
                    getView().onLoginFail(ex, code, msg);
                }
            }

            @Override
            public void onError() {
                onNetError();
            }
        }));
    }

}

3.LoginModel

public class LoginModel extends BaseModel implements LoginContract.Model{
    @Override
    public void login(Map map, Subscriber subscriber) {
        Subscription subscription = mApi.login(map).compose(RetrofitHelper.applySchedulers()).subscribe(subscriber);
        addSubscribe(subscription);
    }

}

4.LoginActivity

public class LoginActivity extends BaseMvpActivity<LoginPresenter> implements LoginContract.View {

    @BindView(R.id.tv_service)
    TextView tvService;
    
    private UserInfo userInfo;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_login;
    }

    @Override
    protected LoginPresenter createPresenter() {
        return new LoginPresenter();
    }

    @Override
    protected void initView() {
      addHeadTitle("登陆");
    }

	@OnClick({R.id.login, R.id.register})
    public void onViewClicked(View view) {
    	switch (view.getId()) {
            case R.id.login:
               	mPresenter.login(phone,password);
                break;
            case R.id.register:
              	// 注册 
                break;
        }
        
    }

    @Override
    public void onLoginSuccess(UserInfo data) {
        // 登陆成功回调
    }

	@Override
    public void onLoginFail(Throwable ex, String code, String msg) {
        // 登陆失败回调
    }
   
}

总结

至此,MVP搭建完成。其实还有很多可以优化的地方。每个人对MVP的理解不一样,而MVP架构也并不是一成不变,适合自己项目的才是最好的.

猜你喜欢

转载自blog.csdn.net/ym4189/article/details/86016276