上回对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 总结
虽然分了三层,看起来有点多,但是每层职责都是很清晰,且都是可以替换的;
后续可能会出写一系列关于架构设计的内容,有兴趣的同学可以关注下。