【Android】如何写出优雅的适用于MVVM的BaseActivity

发布于:2023-10-25 ⋅ 阅读:(95) ⋅ 点赞:(0)

Android如何写出优雅的BaseActivity

1.引言

日常开发, 相信我们对于Activity都有自己的BaseActivity, 那么在引入JetPack后, 如何使我们的BaseActivity简洁且高效成了值得思考的问题

  • 涉及的技术栈
    MVVM, JetPack, LiveData, DataBing, ViewModel
  • 先来看看最后的效果
    在这里插入图片描述
    在这里插入图片描述
    是不是很简洁…

2. 如何实现

  • MainActivity继承自BaseActivityVBVM
abstract class BaseActivityVBVM<VB : ViewDataBinding, VM : ViewModel> : BaseActivityVB<VB>, IViewDataBinding<VB> {

    protected var _factory: ViewModelProvider.Factory?

    /**
     * 针对Hilt(@JvmOverloads kotlin默认参数值无效)
     * @constructor
     */
    constructor() : this(null)

    constructor(factory: ViewModelProvider.Factory?) : super(){
        _factory = factory
    }

    //

    protected lateinit var vm: VM

    //

    @CallSuper
    override fun initLayout() {
        super.initLayout()
        vm = UtilKViewModel.get(this, _factory/*, 1*/)
        bindViewVM(vb)
    }
}

vm是通过反射获得

  • 再看BaseActivityVB
abstract class BaseActivityVB<VB : ViewDataBinding>(
    /*protected var _factory: ViewModelProvider.Factory? = null*/
) : AppCompatActivity(), IActivity, IUtilK {

    protected val vb: VB by lazy(mode = LazyThreadSafetyMode.NONE) {
        UtilKViewDataBinding.get<VB>(this::class.java, layoutInflater/*, 0*/).apply {
            lifecycleOwner = this@BaseActivityVB
        }
    }

    ///

    @CallSuper
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initFlag()
        initLayout()
        initData(savedInstanceState)
    }

    @CallSuper
    override fun onDestroy() {
        vb.unbind()
        super.onDestroy()
    }

    ///

    @CallSuper
    override fun initLayout() {
        setContentView(vb.root)
    }

    @CallSuper
    override fun initData(savedInstanceState: Bundle?) {
        initView(savedInstanceState)
        initObserver()
    }
}

等等?!这就结束了,稍安勿躁,最精华的来了
VB和VM是怎么来的 --> 反射

  • VM
    @JvmStatic
    @Suppress(CSuppress.UNCHECKED_CAST)
    fun <VB : ViewDataBinding> get(clazz: Class<*>, inflater: LayoutInflater/*, index: Int = 0*/): VB =
        UtilKReflectGenericKotlin.getParentGenericTypeByTClazz(clazz, ViewDataBinding::class.java)?.run {
            getDeclaredMethod("inflate", LayoutInflater::class.java).invoke(null, inflater) as VB
        } ?: throw Exception("inflate activity vb fail!")
  • 由于多层继承, 我们还要考虑泛型的类型匹配
    @JvmStatic
    fun getParentGenericTypeByTClazz(clazz: Class<*>, parentClazz: Class<*>/*, index: Int = 0*/): Class<*>? =
        getParentGenericTypeByT(clazz, parentClazz) as? Class<*>?

    @JvmStatic
    fun getParentGenericTypeByT(clazz: Class<*>, tClazz: Class<*>/*, index: Int = 0*/): Type? {
        val superClazz: Class<*>? = clazz.superclass
        val genericSuperclass: Type? = clazz.genericSuperclass
        if (genericSuperclass !is ParameterizedType) {//当继承类不是参数化类型,就从父类中寻找
            return if (superClazz != null) {
                getParentGenericTypeByT(superClazz, tClazz)//当我们继承多层BaseActivity时递归查找泛型
            } else
                null
        }
        genericSuperclass.actualTypeArguments.filterIsInstance<Class<*>>()
            .run {
                this.printlog()//for debug
                if (this.isNotEmpty()) {
                    for (clz in this) {
                        if (tClazz.isAssignableFrom(clz))
                            return clz
                    }
                }
                if (superClazz != null)
                    return getParentGenericTypeByT(superClazz, tClazz)
                else
                    return null
            }
    }

3. 实际开发

在这里插入图片描述

@AndroidEntryPoint
class MainActivity : BaseActivityVBVM<ActivityMainBinding, MainViewModel>() {

    override fun initView(savedInstanceState: Bundle?) {

    }
    override fun bindViewVM(vb: ActivityMainBinding) {
        vb.vm = vm
    }
}
@HiltViewModel
class MainViewModel @Inject constructor(private val _cache: Cache) : BaseViewModel() {
    var number_gege by VarProperty_Set(_cache.number_gege) { field, value ->
        lv_number_gege.value = value.toString()
        _cache.number_gege = value
        true
    }
    var number_meimei by VarProperty_Set(_cache.number_meimei) { field, value ->
        lv_number_meimei.value = value.toString()
        _cache.number_meimei = value
        true
    }
    val lv_number_gege = MutableLiveData(_cache.number_gege.toString())
    val lv_number_meimei = MutableLiveData(_cache.number_meimei.toString())

    fun add_gege(other: Double) {
        number_gege = number_gege.toBigDecimal().add(BigDecimal(other)).toDouble()
    }

    fun minus_gege(other: Double) {
        number_gege = number_gege.toBigDecimal().minus(BigDecimal(other)).toDouble()
    }

    fun add_meimei(other: Double) {
        number_meimei = number_meimei.toBigDecimal().add(BigDecimal(other)).toDouble()
    }

    fun minus_meimei(other: Double) {
        number_meimei = number_meimei.toBigDecimal().minus(BigDecimal(other)).toDouble()
    }
}

在这里插入图片描述

bingo这就是所有的代码,等等, 还有的是不是没贴,莫担心, 热乎的开源地址为下
1.图中库的开源地址: SwiftKit
1.图中示例项目的开源地址: LoveCalculator


网站公告

今日签到

点亮在社区的每一天
去签到