上回对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;
- 注释2,开发的页面都会自定义背景,所以系统自带的背景就可以去掉,防止过度渲染;
- 注释3,用过LoadSir的都知道,当接口请求数据失败,可以设置一个失败的页面,点击页面触发重新请求,这就是重新请的处理,由子类实现;
- 注释4,也是跟LoadSir相关的,提供一个注册的view,如果子类用不到LoadSir,可以不用重写注释3、4这两个函数;
- 注释5,这三个函数的就是模板代码,初始化view、初始化数据、请求数据;
- 注释6,细分页面回退方式,巧妙地利用实现空接口来区分,具体可以注释11的定义,可根据实际情况进行调整;
- 注释7,处理点击页面关闭输入框;
abstract class BaseActivity : FragmentActivity(), View.OnClickListener {//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()
}
open fun loadSirReload() {}//3
open fun getRegisterView() = this//4
//做view初始化工作
abstract fun initView()//5
//初始化数据
abstract fun initData()//5
//设置本地数据&请求网络数据
abstract fun requestData()//5
override fun onClick(v: View?) {}
override fun onBackPressed() {//6
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 {//7
if (ev?.action == MotionEvent.ACTION_DOWN) {
KeyboardUtils.hideSoftInput(this@BaseActivity)
}
return super.dispatchTouchEvent(ev)
}
}
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> {
return getClazz(1) as Class<VB>
}
private fun getViewModelClass(): Class<VM> {
return getClazz(0) as Class<VM>
}
private fun getClazz(index: Int): Class<*> {
val parameterizedType =
if ((javaClass.genericSuperclass as? ParameterizedType) == null) {//兼容FamilyMembersActivity继承MembersActivity
javaClass.superclass.genericSuperclass
} else {
javaClass.genericSuperclass
}
return (parameterizedType as ParameterizedType).actualTypeArguments[index] as Class<*>
}
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 扩展函数
采用扩展的方式来丰富FragmentActivity的能力,相当于组合,比把函数封装到BaseActivity更厉害一些。 Toast相关的扩展函数;
fun FragmentActivity.showToast(msg:String,isLongDuration:Boolean = false){
ToastUtils.showToast(this, msg)
}
fun cancelToast() {
ToastUtils.cancelToast()
}
Dilaog相关的扩展函数;
fun FragmentActivity.showLoadingDialog(msg: String? = "loading...", cancelAble: Boolean = true) {
if (this.isLoadingDialogShowing()) {//多次触发,只显示一次
return
}
try {
dismissDialogFragment(FragmentLoadingDialog.TAG)
this.supportFragmentManager.beginTransaction()
.add(FragmentLoadingDialog(msg), FragmentLoadingDialog.TAG)
.commitAllowingStateLoss()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun FragmentActivity.dismissLoadingDialog() {
dismissDialogFragment(FragmentLoadingDialog.TAG)
}
fun FragmentActivity.isLoadingDialogShowing(): Boolean {
return this.supportFragmentManager?.findFragmentByTag(FragmentLoadingDialog.TAG) != null
}
suspend fun FragmentActivity.showInfoDialog(params: FragmentInfoDialog.Params): BaseDialogFragment.Action {
dismissInfoDialog()
FragmentInfoDialog().also {
it.bindParams(arrayMapOf("data" to params))
return it.showDialog(this.supportFragmentManager)
}
}
fun FragmentActivity.dismissInfoDialog() {
this.supportFragmentManager.findFragmentByTag(FragmentInfoDialog.TAG)
?.apply {
(this as? DialogFragment)?.dismissAllowingStateLoss()
}
}
fun FragmentActivity.isInfoDialogShowing(): Boolean {
return this.supportFragmentManager.findFragmentByTag(FragmentInfoDialog.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 总结
虽然分了三层,看起来有点多,但是每层职责都是很清晰,且都是可以替换的;
后续可能会出写一系列关于架构设计的内容,有兴趣的同学可以关注下。