MVP以及MVVM一些基类的封装

基类

Posted by River on April 15, 2022

前言

网上有个段子,面试造火箭进去拧螺丝;有时候在公司难免写一个新项目或者一些UI之类;本文简单讲讲一些基类的封装。

1、MVP模式的基类封装

MVP主要定义好Presenter层和View层的接口,在封装一些BaseFragment和BaseActivity。

1.1、 视图View层的封装

简化了视图接口的定义,只拿一个举例子

    public interface ErrorMessageView {
    void showError(String error);
}

1.2、 Presenter层的封装

Presenter封装有接口定义和抽象类封装

接口定义IPresenter:

   // 这里少了个onStart方法,不论需要自行补充即可
   public interface IPresenter {
    void onCreate();

    void onResume();

    void onPause();

    void onStop();

    void onDestroy();

    void onActivityResult(Intent intent, int requestCode, int resultCode);

    void onNewIntent(Intent intent);
}

抽象基类AbsPresenter封装:

    public abstract class AbsPresenter implements IPresenter,
        FailedCallback, ApiFailedCallback, CancelCallback {

        public final static int API_ALERT_CODE = -1234;

        protected Context context;
        protected ErrorMessageView errorMessageView;
        private MaterialDialog dialog;

        public AbsPresenter(Context context, ErrorMessageView view) {
            this.context = context;
            this.errorMessageView = view;
        }

        protected String getString(int rid) {
            return context.getString(rid);
        }

        protected String getString(int resId, Object... formatArgs) {
            return context.getString(resId, formatArgs);
        }

        protected int getColor(int rid) {
            return context.getResources().getColor(rid);
        }

        protected void hideLoadingView() {
            if (errorMessageView instanceof AlertErrorMessageView) {
                ((LoadingView) errorMessageView).hideLoading();
            }
            if (errorMessageView instanceof LoadingMessageView) {
                ((LoadingMessageView) errorMessageView).hideLoading();
            }
        }

        @Override
        public void onFailed(int errCode, String msg) {
            hideLoadingView();
            if (errCode == API_ALERT_CODE) {
                if (errorMessageView instanceof AlertErrorMessageView) {
                    ((AlertErrorMessageView) errorMessageView).alertError(msg);
                }
            } else {
                errorMessageView.showError(msg);
            }
        }

    @Override
    public void onApiFailed(int i, String msg) {
        hideLoadingView();
        if (i == API_ALERT_CODE) {
            if (errorMessageView instanceof AlertErrorMessageView) {
                ((AlertErrorMessageView) errorMessageView).alertError(msg);
            }
        } else {
            errorMessageView.showError(msg);
        }
    }

    @Override
    public void onCanceled() {
        hideLoadingView();
    }

    @Override
    public void onCreate() {
        // do nothing
    }

    @Override
    public void onResume() {
        //do nothing
    }

    @Override
    public void onPause() {
        //do nothing
    }

    @Override
    public void onStop() {
        //do nothing
    }

    @Override
    public void onDestroy() {
        //do nothing
    }

    @Override
    public void onActivityResult(Intent intent, int requestCode, int resultCode) {
        //do nothing
    }

    @Override
    public void onNewIntent(Intent intent) {
        //do nothing
    }
}

自动绑定生命周期基类AbsLifePresenter封装:

    public abstract class AbsLifePresenter extends AbsPresenter implements LifecycleObserver {

    protected LifecycleOwner mLifecycleOwner;

    protected CoroutineSupport mCoroutineSupport = new CoroutineSupport();

    public AbsLifePresenter(Context context, ErrorMessageView view, LifecycleOwner lifecycleOwner) {
        super(context, view);
        this.mLifecycleOwner = ObjectHelper.requireNonNull(lifecycleOwner,"LifecycleOwner can not be null in AbsLifePresenter");
        mLifecycleOwner.getLifecycle().addObserver(this);
    }

    protected <T> AutoDisposeConverter<T> bindLifecycle() {
        return autoDisposable(
                RxLifecycleInterop.from(AndroidLifecycle.createLifecycleProvider(mLifecycleOwner)));
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    @CallSuper
    @Override
    public void onDestroy() {
        mCoroutineSupport.destroy();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    @Override
    public void onCreate() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    @Override
    public void onPause() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    @Override
    public void onResume() {
        //do nothing
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    protected void onStart() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    @Override
    public void onStop() {
        //do nothing
    }

}

1.3、BaseActivity的简单封装

这里简化了视图View的定义

 
public abstract class BasePlatformActivity extends FragmentActivity
        implements ErrorMessageView{

    protected CoroutineSupport mCoroutineSupport = new CoroutineSupport();
    /**
     * 这个成员变量是用做onNewIntent和onActivityResult用
     */
    private IPresenter mIPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(getContentView());
        init();
        HiLogger.i("ActivityLifeCircle " + this.getClass().getSimpleName() + " onCreate");

    }

    /**
     * call this method can get onActivityResult and onNewIntent callBack(option)
     *
     * @param iPresenter 主要生命周期方法的回掉接口
     */
    public void setIPresenter(IPresenter iPresenter) {
        this.mIPresenter = iPresenter;
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        HiLogger.i("ActivityLifeCircle " + this.getClass().getSimpleName() + " onRestart");
    }

    @Override
    protected void onStart() {
        super.onStart();
        HiLogger.i("ActivityLifeCircle " + this.getClass().getSimpleName() + " onStart");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        HiLogger.i("ActivityLifeCircle " + this.getClass().getSimpleName() + " onDestroy");
        mCoroutineSupport.destroy();
    }

    protected abstract int getContentView();

    protected void init() {
        //do nothing
    }

    

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (mIPresenter != null) {
            mIPresenter.onNewIntent(intent);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (mIPresenter != null) {
            mIPresenter.onActivityResult(data, requestCode, resultCode);
        }
    }

    /**
     * 返回一个 Rx Converter ,自动在OnDestroy时中断Rx事件流
     *
     * @param <T>
     * @return
     */
    protected <T> AutoDisposeConverter<T> bindLifecycle() {
        return autoDisposable(
                RxLifecycleInterop.from(AndroidLifecycle.createLifecycleProvider(this)));
    }

    protected void toast(String msg) {
        if (TextUtils.isEmpty(msg)) return;
        ToastMaster.showToast(this, msg, Gravity.NO_GRAVITY, Toast.LENGTH_LONG);
    }

    @Override
    public void showError(String error) {
        toast(error);
    }
    
}

2、MVVM的封装

MVVM对基类的封装主要包含了View,ViewModel以及对应的BaseFragment(以Fragment举例)

2.1、ViewModel的封装


    open class BaseViewModel : ViewModel(), CoroutineScope {

    private val coroutineDelegate = lazy { CoroutineSupport() }

    private val coroutine by coroutineDelegate

    var data = Bundle()

    //toast
    val toast = MutableLiveData<Event<String?>>()
    //加载进度框
    val flower = MutableLiveData<Event<Boolean>>()
    //页面跳转
    val jump = MutableLiveData<Event2<KClass<*>?, Bundle?>>()
    //页面跳转并获取结果
    val jumpForResult = MutableLiveData<Event3<KClass<*>?, Bundle?, Int?>>()
    //ViewModel中的activity代码块
    val activityBlock = MutableLiveData<Event<(FragmentActivity) -> Unit>>()
    //ViewModel中的activity代码块
    val activityForeverBlock = MutableLiveData<Event<(FragmentActivity) -> Unit>>()
    //ViewModel中的Fragment代码块
    val fragmentBlock = MutableLiveData<Event<(Fragment) -> Unit>>()
    //关闭页面回参
    val finishResult = MutableLiveData<Event2<Int?, Bundle?>>()
    //关闭页面
    val finish = MutableLiveData<Event2<Boolean, Boolean?>>()
    //token 失效
    val tokenInvalid = MutableLiveData<Event<Boolean>>()

    fun internalOnStarted() {
        onStarted()
    }

    open fun onStarted() = Unit

    override val coroutineContext: CoroutineContext
        get() = coroutine.coroutineContext

    override fun onCleared() {
        super.onCleared()
        hideLoading()
        if (coroutineDelegate.isInitialized()) {
            coroutine.destroy()
        }
    }

    fun showToast(text: String?) {
        toast.postValue(Event(text))
    }

    fun showLoading() {
        flower.postValue(Event(true))
    }

    fun hideLoading() {
        flower.postValue(Event(false))
    }

    fun <T : Activity> startActivity(cls: KClass<T>, data: Bundle? = null) {
        jump.postValue(Event2(cls, data))
    }

    fun <T : Activity> startActivityForResult(cls: KClass<T>, requestCode: Int, data: Bundle? = null) {
        jumpForResult.postValue(Event3(cls, data, requestCode))
    }

    fun useActivityBlock(sync: Boolean = false, block: (FragmentActivity) -> Unit) {
        if (sync) {
            activityBlock.value = Event(block)
        } else {
            activityBlock.postValue(Event(block))
        }
    }

    fun userActivityForeverBlock(block: (FragmentActivity) -> Unit) {
        activityForeverBlock.postValue(Event(block))
    }

    fun useFragmentBlock(sync: Boolean = false, block: (Fragment) -> Unit) {
        if (sync) {
            fragmentBlock.value = Event(block)
        } else {
            fragmentBlock.postValue(Event(block))
        }
    }

    fun setResult(resultCode: Int?, data: Bundle?) {
        finishResult.postValue(Event2(resultCode, data))
    }

    fun finish(finishHostAct: Boolean? = null) {
        finish.postValue(Event2(true, finishHostAct))
    }

    fun delayAction(timeMillis: Long = 500L, func: suspend CoroutineScope.() -> Unit) {
        launch {
            delay(timeMillis)
            func.invoke(this)
        }
    }

    fun launchIO(func: suspend CoroutineScope.() -> Unit) {
        launch(Dispatchers.IO, block = func)
    }

    fun launchWithoutView(func: suspend CoroutineScope.() -> Unit) {
        BikeGlobalMainScope.launch(block = func)
    }

    fun cancelChildren() {
        if (coroutineDelegate.isInitialized()) {
            coroutine.cancelChildren()
        }
    }


}

// 辅助类Event示例
open class Event<out T>(private val content: T) {

    @Suppress("MemberVisibilityCanBePrivate")
    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}
    

2.2、视图层BaseView的封装

这里需要注意的是,BaseView定义辅助的扩展方法

    
    interface BaseBikeView {

    fun init(fragment: Fragment) {
        getBaseBikeViewProvider().fragment = fragment
        getBaseBikeViewProvider().context = fragment.context
    }

    fun init(activity: AppCompatActivity) {
        getBaseBikeViewProvider().activity = activity
        getBaseBikeViewProvider().context = activity
    }

    fun getBaseBikeViewProvider(): BaseBikeViewProvider

    fun getBaseActivity(): FragmentActivity? {
        return getBaseBikeViewProvider().activity
                ?: getBaseBikeViewProvider().fragment?.activity as? FragmentActivity
    }

    fun getBaseFragment(): Fragment? {
        return getBaseBikeViewProvider().fragment
    }

    fun getContext(): Context? {
        return getBaseBikeViewProvider().context
    }

    fun getViewModel(): BaseBikeViewModel? {
        return getBaseBikeViewProvider().viewModel
    }

    fun injectViewModel(viewModel: BaseBikeViewModel) {
        getBaseBikeViewProvider().fragment?.apply {
            setupExt(this, viewModel)
            arguments?.let {
                viewModel.data.putAll(it)
            }
            viewModel.internalOnStarted()
        }

        getBaseBikeViewProvider().activity?.apply {
            setupExt(this, viewModel)
            intent.extras?.let {
                viewModel.data.putAll(it)
            }
            viewModel.internalOnStarted()
        }
    }

    fun setupExt(lifecycleOwner: LifecycleOwner, viewModel: BaseBikeViewModel) {
        setupToast(lifecycleOwner, viewModel.toast)
        setupFlower(lifecycleOwner, viewModel.flower)
        setupJump(lifecycleOwner, viewModel.jump)
        setupJumpForResult(lifecycleOwner, viewModel.jumpForResult)
        setupActivityCallback(lifecycleOwner, viewModel.activityBlock)
        setupActivityForeverCallback(lifecycleOwner, viewModel.activityForeverBlock)
        setupFragmentCallback(lifecycleOwner, viewModel.fragmentBlock)
        setupFinishResult(lifecycleOwner, viewModel.finishResult)
        setupFinish(lifecycleOwner, viewModel.finish)
        setupTokenInvalid(lifecycleOwner, viewModel.tokenInvalid)
    }
}
    

辅助扩展类分析

    
    fun BaseBikeView.setupToast(
        lifecycleOwner: LifecycleOwner,
        toastEvent: LiveData<Event<String?>>
) {
    if (toastEvent.hasObservers()) {
        return
    }
    toastEvent.observe(lifecycleOwner, EventObserver {
        it ?: return@EventObserver

        try {
            val ctx = getContext() ?: return@EventObserver
            HMUIToast.toast(ctx, it)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    })
}

    fun BaseBikeView.setupFlower(
        lifecycleOwner: LifecycleOwner,
        flowerEvent: LiveData<Event<Boolean>>) {
        if (flowerEvent.hasObservers()) {
            return
        }
        flowerEvent.observe(lifecycleOwner, EventObserver {
            if (it) {
                getBaseBikeViewProvider().loadingDialog?.show()
            } else {
                getBaseBikeViewProvider().loadingDialog?.hide()
            }
        })
    }

2.3、BaseFragment初步封装


    open class BaseBikeFragment : Fragment(), BaseBikeView, BaseViewModelInterface {
    // 暂时没什么用处
    private val viewProvider = BaseBikeViewProvider()

    override fun getBaseBikeViewProvider(): BaseBikeViewProvider {
        return viewProvider
    }

    override fun getContext(): Context? {
        return activity
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        this.init(this)
    }

    protected fun <T> LiveData<T>.observe(call: (T) -> Unit) {
        this.observe(this@BaseBikeFragment, Observer {
            call.invoke(it)
        })
    }

    override fun getActivityContext(): AppCompatActivity? {
        activity?.let {
            return activity as AppCompatActivity
        }
        return null
    }

    override fun getFragmentInstance(): Fragment? {
        return this
    }

    override fun isActivityContext(): Boolean {
        return false
    }

    override fun showLoading(msg: String) {
    }

    override fun hideLoading() {
    }

    override fun showToast(msg: String) {
        activity?.let {
            HMUIToast.toast(it, msg)
        }
    }

    override fun getLifecycleOwner(): LifecycleOwner {
        return this
    }
}

3、MVVM的使用示范

具体某个Fragment


class GlobalNotificationFragment : BaseBikeFragment(){

    private lateinit var viewModel: GlobalNotificationViewModel
    
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        
        viewModel = ViewModelProviders.of(this).get(GlobalNotificationViewModel::class.java)
                .reverseInject(this)
                
        viewModel.notificationData.observe(this, Observer {
            if (it != null) {
               ...
            }
         })
                
         viewModel.payResult.observe(this, Observer {
            if (it != null) {
               ...
            }
          })
          
          // 类似接口获取数据
    }
}


// 具体ViewModel的示例

    
    class GlobalNotificationViewModel : BaseBikeViewModel() {

        private val globalRepository = GlobalNotificationRepository()
        val notificationData = MutableLiveData<NotificationInfo?>()
        val payResult = MutableLiveData<CardPayMgrV2.Result?>()
        val rideCardNoticeResult = MutableLiveData<BikeRideCardNotice?>()

        /**
         * 获取全局通知栏
         */
        fun getNotification(resource:Int?, from:String?) {
            launch {
                val response = globalRepository.getNotification(resource = resource,from = from)
                val data = response.data
                notificationData.postValue(data)
            }
        }

        fun globalNotifyUpdate(resource: Int?, notificationCode: Int?) {
            launch {
                globalRepository.globalNotifyUpdate(resource = resource,notificationCode = notificationCode)
            }
        }

        fun buyCard(ext: PaymentCombineWayVO?) {
            launch {
                val response = globalRepository.buyCard(viewModel = this@GlobalNotificationViewModel,pkgsResp = ext?.pkgsResp,pkgGroup = ext?.pkgGroup,pkg = ext?.pkg)
                payResult.postValue(response)
        }
    }

        fun loadData(pageFrom: Int?) {
            launch {

            if (UserManager.isNotLogin()) {
                return@launch
            }

            val accountInfo = BikeAccountCheck.getInstance().getBikeAccountInfo(
                    if (pageFrom == GlobalNotificationFragment.PAGE_FROM_CONFIRM) {
                        BikeAccountCheckRequest.FROM_OPEN_LOCK
                    } else if (pageFrom == GlobalNotificationFragment.PAGE_FROM_HOME) {
                        BikeAccountCheckRequest.FROM_BANNER
                    } else {
                        BikeAccountCheckRequest.FROM_NONE
                    },
                    false
            )

                rideCardNoticeResult?.postValue(accountInfo?.rideCardGuideInfo)
            }
        }



    }

总结

  1. 其实MVVM使用起来并不复杂,其实也是通过post和observer的方式更好的分离了数据与UI;
  2. 初步接触这种方式编写的同学可能感到陌生,多联系几次就好。