App架构规划之BaseActivity(2)

上回对BaseActivity做了大体的介绍,接下来分析详细代码如何完成;

留意哈,这儿说到的BaseActivity不是指某个类,而是一组功能的组合;

1.整体架构

classDiagram
BaseActivity <|-- BaseVMActivity
BaseVMActivity <|-- AppBaseVMActivity
AppBaseVMActivity <|-- MainActivity
class BaseActivity{
+initView()
+initData()
+requestData()
}
class BaseVMActivity{
-BaseViewModel mViewModel
-ViewBinding mBinding
}
class AppBaseVMActivity{
+X x
+XX xx
+x()
}
class MainActivity{
+XXXX xxxx
+XXXXX xxxxx
+xx()
}

2. 代码完成

2.1 BaseActivity完成

它主打统一代码模板&通用的逻辑处理,详细分析如下:

  • 注释1,完成通用的接口,例如View.OnClickListener,至于ILoadingDialogAbility by LoadingDialogAbilityImpl() 是kotlin的特点托付才能,暂且理解为使得BaseActivity具有ILoadingDialogAbility的才能,详细的完成托付给LoadingDialogAbilityImpl来处理,下文会要点介绍;
  • 注释2,开发的页面都会自界说布景,所以体系自带的布景就能够去掉,避免过度烘托;
  • 注释3,kotlin的特点托付相当于组合,托付者是不知道被托付者的特点,但又需求运用上下文,所以手动传入this;代码点丑,暂时没有找到更好的处理方法;
  • 注释4,用过LoadSir的都知道,当接口恳求数据失利,能够设置一个失利的页面,点击页面触发从头恳求,这便是从头请的处理,由子类完成;
  • 注释5,也是跟LoadSir相关的,供给一个注册的view,如果子类用不到LoadSir,能够不用重写注释4、5这两个函数;
  • 注释6,这三个函数的便是模板代码,初始化view、初始化数据、恳求数据;
  • 注释7,细分页面回退方法,奇妙地利用完成空接口来区分,详细能够注释11的界说,可根据实际情况进行调整;
  • 注释8,处理点击页面封闭输入框;
abstract class BaseActivity : FragmentActivity(), View.OnClickListener,
    ILoadingDialogAbility by LoadingDialogAbilityImpl(),
    IInfoDialogAbility by InfoDialogAbilityImpl(),
    ILoadServiceAbility by LoadServiceAbilityImpl(),
    IToastAbility by ToastAbilityImpl() {//1
    val TAG by lazy {
        this@BaseActivity::class.java.simpleName
    }
    var lastClickTime = 0L
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.decorView.background = null//避免过渡烘托 //2
        bindActivity()
        initView()
        initData()
        requestData()
    }
    private fun bindActivity() {//3
        (this as IInfoDialogAbility).infoDialogActivity = this
        (this as ILoadServiceAbility).serviceView = getRegisterView()
        (this as ILoadServiceAbility).reloadCallback = ::loadSirReload
        (this as ILoadingDialogAbility).loadDialogActivity = this
        (this as IToastAbility).toastActivity = this
    }
    open fun loadSirReload() {}//4
    open fun getRegisterView() = this//5
    //做view初始化工作
    abstract fun initView()//6
    //初始化数据
    abstract fun initData()//6
    //设置本地数据&恳求网络数据
    abstract fun requestData()//6
    override fun onClick(v: View?) {}
    override fun onBackPressed() {//7
        when (this) {
            is PageType.TypeDisableBackScreen -> {}//制止回来
            is PageType.TypeTwoTimesBackScreen -> {
                if (System.currentTimeMillis() - lastClickTime > 2000) {//连续点击回来
                    lastClickTime = System.currentTimeMillis();
                } else {
                    super.onBackPressed()
                }
            }
            else -> {
                super.onBackPressed()
            }
        }
    }
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {//8
        if (ev?.action == MotionEvent.ACTION_DOWN) {
            KeyboardUtils.hideSoftInput(this@BaseActivity)
        }
        return super.dispatchTouchEvent(ev)
    }
}
class PageType{//11
    interface TypeDisableBackScreen
    interface TypeTwoTimesBackScreen
}
2.2 BaseVMActivity完成

它主打对mvvn架构完成处理,详细分析如下:

  • 注释1,承继Basectivity,且界说泛型类型VM,VB,子类传入对应类型,会主动通过注释2、注释3进行构建;
  • 注释3,构建App等级的ViewModel,一切页面模块公用一个;
  • 注释4,绑定对BaseViewModel liveData的监听;
  • 注释5,BaseViewModel 中界说了toast、loadingDialog、loadSir相关的LiveData,页面负责监听;
abstract class BaseVMActivity3<VM : BaseViewModel, VB : ViewBinding> : Basectivity() {//1
     val mBinding: VB by viewBinding(getViewBindingClass())//2
     val mViewModel: VM by viewModel(getViewModelClass())//3
    private fun getViewBindingClass(): Class<VB> {
        val parameterizedType = if ((javaClass.genericSuperclass as? ParameterizedType) == null) {
            javaClass.superclass.genericSuperclass
        } else {
            javaClass.genericSuperclass
        }
        val actualTypeArguments = (parameterizedType as ParameterizedType).actualTypeArguments
        return actualTypeArguments[1] as Class<VB>
    }
    private fun getViewModelClass(): Class<VM> {
        val parameterizedType =
            if ((javaClass.genericSuperclass as? ParameterizedType) == null) {
                javaClass.superclass.genericSuperclass
            } else {
                javaClass.genericSuperclass
            }
        val actualTypeArguments = (parameterizedType as ParameterizedType).actualTypeArguments
        val modelType = actualTypeArguments[0] as Class<*>
        return modelType as Class<VM>
    }
    val mAppViewModel by applicationViewModels<AppViewModel2>()//3
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startObserve()
    }
    open fun startObserve() {//4
        bindObserve(mViewModel)
    }
    fun bindObserve(viewModel: BaseViewModel){
        viewModel.toastState.observe(this) {//5
            showToast(it)
        }
        viewModel.loadingDialogState.observe(this) {//5
            when (it) {
                is BaseViewModel.LoadingDialogState.Showing -> {
                    showLoadingDialog(it.params?.first, it.params?.second ?: true)
                }
                else -> {
                    dismissLoadingDialog()
                }
            }
        }
        viewModel.pageState.observe(this) {//5
            when (it) {
                is BaseViewModel.PageState.Loading -> {
                    showPageLoading()
                }
                is BaseViewModel.PageState.Failed -> {
                    showPageFailed()
                }
                is BaseViewModel.PageState.Success -> {
                    showPageSuccess()
                }
                is BaseViewModel.PageState.NoNet -> {
                    showPageNoNet()
                }
            }
        }
    }
}
2.3 AppBaseVMActivity完成

它主打处理跟业务相关的:

  • 注释1、2,都是跟业务相关的,就不多做解释了;
abstract class AppBaseVMActivity<VM : BaseViewModel, VB : ViewBinding> : BaseVMActivity<VM,VB>(){
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       (this.mViewModel as? IConsultInfoRequest)?.apply {//1
           if (pageId != -1 && this@AppBaseVMActivity is PageType.TypeShowConsult) {
               ConsultHelper(pageId = pageId, this@AppBaseVMActivity, this)
           }
       }
       PageStateEventHelper(this@AppBaseVMActivity)//2
   }
}
2.4 LoadingDialogAbilityImpl完成

以ILoadingDialogAbility by LoadingDialogAbilityImpl()为,其他都是大同小异;

  • 接口界说
    首要供给显现、躲藏、是否显现的才能,还包含上下文;
interface ILoadingDialogAbility {
    var loadDialogActivity: FragmentActivity?
    fun showLoadingDialog(msg: String? = "loading...", cancelAble : Boolean = true)
    fun dismissLoadingDialog()
    fun isLoadingDialogShowing():Boolean
}
  • 完成类

首要还是对接口的完成,showDialogFragment、dismissDialogFragment都是自界说的扩展函数;

要点关注loadDialogActivity的set函数,对loadDialogActivity添加了生命周期的监听,onDestroy的时候封闭对话框,避免内存泄露;

open class LoadingDialogAbilityImpl : ILoadingDialogAbility {
    override var loadDialogActivity: FragmentActivity? = null
        set(value) {
            field = value
            value?.lifecycle?.addObserver(object : DefaultLifecycleObserver {
                override fun onDestroy(owner: LifecycleOwner) {
                    owner.lifecycle.removeObserver(this)
                    dismissLoadingDialog()//主动封闭对话框,避免内存泄露
                }
            })
        }
    override fun showLoadingDialog(msg: String?, cancelAble: Boolean) {
        if (isLoadingDialogShowing()) {//屡次触发,只显现一次
            return
        }
        loadDialogActivity?.showDialogFragment(FragmentLoadingDialog(), FragmentLoadingDialog.TAG)
    }
    override fun dismissLoadingDialog() {
        loadDialogActivity?.dismissDialogFragment(FragmentLoadingDialog.TAG)
    }
    override fun isLoadingDialogShowing(): Boolean {
        return loadDialogActivity?.supportFragmentManager?.findFragmentByTag(FragmentLoadingDialog.TAG) != null
    }
}
2.5 viewBinding 、viewModel完成

用的都是Kotlin的扩展函数,也不是太复杂,详细代码如下:

fun <VB : ViewBinding> ComponentActivity.viewBinding(clazz: Class<VB>) = lazy {
    (clazz.getMethod("inflate", LayoutInflater::class.java)
        .invoke(null, layoutInflater) as VB).apply {
        setContentView(root)
    }
}
fun <VM : ViewModel> ComponentActivity.viewModel(clazz: Class<VM>) = lazy {
    ViewModelProvider(this).get(clazz)
}
2.6 总结

尽管分了三层,看起来有点多,但是每层职责都是很清晰,且都是能够替换的;

后续可能会出写一系列关于架构规划的内容,有爱好的同学能够关注下。