/**
 * 获取泛型ViewModel的真实类型
 */
fun <T> Any.getViewModelClass(clz: Class<*>): Class<T> {
    return this.getRealClass(ViewModel::class.java, clz)
}

/**
 * 获取泛型Repository的具体类型
 */
fun <T> Any.getRepositoryClass(): Class<T> {
    return this.getRealClass(BaseRepository::class.java, BaseViewModel::class.java)
}

/**
 * 获取泛型的具体类型
 * @param targetClz 目标类型,例如 BaseRepository、ViewModel
 * @param dstClz 用于表示搜索结束,一般为泛型声明类本身 BaseVMActivity 、BaseViewModel
 */
fun <T> Any.getRealClass(targetClz: Class<*>, dstClz: Class<*>): Class<T> {
    var clz: Class<*> = this.javaClass
    var realType: Type? = null
    while (true) {
        val generic = clz.genericSuperclass
        if (generic is ParameterizedType) {
            val typeArguments = generic.actualTypeArguments
            typeArguments.forEach {
                if (it is Class<*> && targetClz.isAssignableFrom(it)) {
                    realType = it
                    return@forEach
                }
            }
        }
        clz = clz.superclass
        if (clz == Any::class.java || clz == Object::class.java || clz == dstClz::class.java) break
    }
    if (realType == null) {
        throw ClassCastException("${this.javaClass.name} 不是继承自 ${dstClz.name},或者${dstClz.name} 没有泛型类型")
    } else {
        return realType as Class<T>
    }
}
/**
 * 通过 layoutInflater 获取对应的 ViewBinding
 */
@JvmName("ViewBindingUtils")
fun <VB : ViewBinding> Any.inflateBindingWithGeneric(layoutInflater: LayoutInflater): VB {
    return withGenericBindingClass(this) { clazz ->
        clazz.getMethod("inflate", LayoutInflater::class.java).invoke(null, layoutInflater) as VB
    }
}

/**
 * 通过 ViewGroup 获取对应的 ViewBinding
 */
@JvmName("inflateWithGeneric")
fun <VB : ViewBinding> Any.inflateBindingWithGeneric(parent: ViewGroup): VB =
    inflateBindingWithGeneric(LayoutInflater.from(parent.context), parent, false)

/**
 * 获取ViewGroup对象的真实数据类型
 */
@JvmName("inflateWithGeneric")
fun <VB : ViewBinding> Any.inflateBindingWithGeneric(
    layoutInflater: LayoutInflater,
    parent: ViewGroup?,
    attachToParent: Boolean
): VB = withGenericBindingClass(this) { clazz ->
    clazz.getMethod(
        "inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java
    ).invoke(null, layoutInflater, parent, attachToParent) as VB
}

/**
 * 获取对象的真实数据类型
 */
private fun <VB : ViewBinding> withGenericBindingClass(any: Any, block: (Class<VB>) -> VB): VB {
    var throwable: Throwable? = null
    any.allParameterizedType.forEach { parameterizedType ->
        parameterizedType.actualTypeArguments.forEach {
            try {
                val clazz = it as Class<*>
                Logger.d("ViewBindingUtils", "当前泛型 Type 名称为:${clazz.name}")
                if (clazz.name.endsWith("Binding")) {
                    Logger.d("ViewBindingUtils", "当前泛型参数是 ViewBinding,准备获取其真实类型")
                    return runCatching {
                        block.invoke(it as Class<VB>)
                    }.onFailure { e ->
                        throwable = e
                    }.getOrThrow()
                }
            } catch (e: Exception) {
                Logger.e("ViewBindingUtils", "获取 ViewBinding 泛型实例异常", e)
                e.printStackTrace()
            }
        }
    }

    throw IllegalArgumentException("ViewBinding 生成失败,请检查相关自定义View布局和代码是否有错误", throwable)
}

/**
 * 获取对象中的全部 ParameterizedType
 */
private val Any.allParameterizedType: List<ParameterizedType>
    get() {
        val genericParameterizedType = mutableListOf<ParameterizedType>()
        var genericSuperclass = javaClass.genericSuperclass
        var superclass = javaClass.superclass
        while (superclass != null) {
            if (genericSuperclass is ParameterizedType) {
                genericParameterizedType.add(genericSuperclass)
            }
            genericSuperclass = superclass.genericSuperclass
            superclass = superclass.superclass
        }
        return genericParameterizedType
    }

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐