App架构设计之BaseActivity(3)

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,处理点击页面关闭输入框;
kotlin 复制代码
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,页面负责监听;
kotlin 复制代码
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,都是跟业务相关的,就不多做解释了;
less 复制代码
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()为,其他都是大同小异;

  • 接口定义 主要提供显示、隐藏、是否显示的能力,还包括上下文;
kotlin 复制代码
interface ILoadingDialogAbility {
    var loadDialogActivity: FragmentActivity?
    fun showLoadingDialog(msg: String? = "loading...", cancelAble : Boolean = true)
    fun dismissLoadingDialog()
    fun isLoadingDialogShowing():Boolean
}
  • 实现类

主要还是对接口的实现,showDialogFragment、dismissDialogFragment都是自定义的扩展函数;

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

kotlin 复制代码
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的扩展函数,也不是太复杂,具体代码如下:

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 总结

虽然分了三层,看起来有点多,但是每层职责都是很清晰,且都是可以替换的;

后续可能会出写一系列关于架构设计的内容,有兴趣的同学可以关注下。

相关推荐
猿小帅0139 分钟前
androidnetflix手机版遥控器操作
android·framework
l and44 分钟前
Android Http-server 本地 web 服务
android
CYRUS STUDIO1 小时前
使用 AndroidNativeEmu 调用 JNI 函数
android·汇编·arm开发·arm·逆向·jni
消失的旧时光-19432 小时前
Android 串口通信
android
风浅月明2 小时前
[Android]使用WorkManager循环执行任务
android
_extraordinary_3 小时前
Linux权限(一)
android·linux·excel
人生!?4 小时前
给小米/红米手机root(工具基本为官方工具)——KernelSU篇
android·linux·智能手机
古苏5 小时前
Android输入事件传递流程系统源码级解析
android
生产队队长6 小时前
ThinkPHP:配置Redis并使用
android·数据库·redis
踏雪羽翼6 小时前
android 差值器的使用
android