您现在的位置是:首页 >学无止境 >Android使用Kotlin代理实现Bundle数据传递封装网站首页学无止境

Android使用Kotlin代理实现Bundle数据传递封装

吕子明 2024-07-25 12:01:02
简介Android使用Kotlin代理实现Bundle数据传递封装


前言

Android中发送数据比较常用的方式是使用Bundle,但每次使用fragment的时候要写一些模板代码,既然kotlin中支持延迟初始化对象,使用自定义代理的方式是否可以实现不写模板代码也可以获取到值呢?


一、封装思路

在mvi框架封装这篇文章中使用到了自定义路由跳转,苦思冥想后发现少了点什么,嗯就是参数传递应当简化,本篇文章和上篇文章无关,可以看做独立的篇章,本篇文章主要是简化Bundle的参数传递和接受。

二、实现步骤

1.创建数据传递类,此类作为核心

代码如下:

class RouterBundle private constructor(private val builder: Builder) {

    companion object {
        inline fun build(block: Builder.() -> Unit = {}) =
            Builder().apply(block).build()
    }

    /** 数据 **/
    fun bundle() = builder.bundle

    /** 是否有回调 **/
    fun activityResult() = builder.registerForActivityResult

    class Builder {
        // 用于android序列化数据
        val bundle = Bundle()
        // 如果指定该参数则表示有回调
        var registerForActivityResult:  ActivityResultLauncher<Intent>? = null

        /** 外部无需在调用该函数 **/
        fun build() = RouterBundle(this)

        fun setString(key: String, value: String) =
            bundle.putString(key, value)

        fun setStringArray(key: String, value: Array<out String>) =
            bundle.putStringArray(key, value)

        fun setBool(key: String, value: Boolean) =
            bundle.putBoolean(key, value)

        fun setFloat(key: String, value: Float) =
            bundle.putFloat(key, value)

        fun setLong(key: String, value: Long) =
            bundle.putLong(key, value)

        fun setDouble(key: String, value: Double) =
            bundle.putDouble(key, value)

        fun setSerializable(key: String, value: Serializable) =
            bundle.putSerializable(key, value)

        fun setParcelable(key: String, value: Parcelable) =
            bundle.putParcelable(key, value)

        fun setParcelableArray(key: String, value: Array<out Parcelable>) {
            bundle.putParcelableArray(key, value)
        }

        fun setActivityResult(result: ActivityResultLauncher<Intent>) {
            registerForActivityResult = result
        }
    }
}

2.fragment拓展,这里只讲fragment毕竟activity的跳转就涉及到路由框架了

代码如下:

object FragmentExt {

    /**
     * 简化fragment创建实例的数据传递
     */
    fun <T: Fragment> T.bundle(block: RouterBundle.Builder.() -> Unit = {}): T {
        val request = RouterBundle.build(block)
        this.arguments = request.bundle()
        return this
    }
}

3.代理

3.1. activity代理,这个部分是独立的,只要路由框架支持使用Bundle就可以接收

代码如下:

/**
 * 获取 intent bundle的值
 */
class ActivityParamsDelegate<T>(
    private val activity: Activity,
    private val customKey: String = "",
    private val defValue: T? = null,
    private val type: KType
) : ReadOnlyProperty<Any?, T> {
    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        val key = customKey.ifEmpty { property.name }
        val bundle = activity.intent?.extras ?: return throw NullPointerException("没有找到Bundle对象")

        return bundle.get(key, type, defValue)
    }
}

/**
 * 自定义类型传递
 */
internal inline fun <P, reified R> P?.or(defaultValue: () -> R): R {
    return this as? R ?: defaultValue()
}

internal fun <T> Bundle.get(key: String, type: KType, def: T?): T {
    val result: Any = when (type) {
        typeOf<Byte>() -> getByte(key, def.or { 0.toByte() })
        typeOf<Short>() -> getShort(key, def.or { 0.toShort() })
        typeOf<Int>() -> getInt(key, def.or { 0 })
        typeOf<Float>() -> getFloat(key, def.or { 0f })
        typeOf<Double>() -> getDouble(key, def.or { 0.0 })
        typeOf<Long>() -> getLong(key, def.or { 0L })
        typeOf<Boolean>() -> getBoolean(key, def.or { false })
        typeOf<Char>() -> getChar(key, def.or { '0' })
        typeOf<CharSequence>() -> getCharSequence(key, def.or { "" })
        typeOf<String>() -> getString(key, def.or { "" })

        //array type
        typeOf<ByteArray>() -> getByteArray(key) ?: def.or { ByteArray(0) }
        typeOf<ShortArray>() -> getShortArray(key) ?: def.or { ShortArray(0) }
        typeOf<IntArray>() -> getIntArray(key) ?: def.or { IntArray(0) }
        typeOf<FloatArray>() -> getFloatArray(key) ?: def.or { FloatArray(0) }
        typeOf<DoubleArray>() -> getDoubleArray(key) ?: def.or { DoubleArray(0) }
        typeOf<LongArray>() -> getLongArray(key) ?: def.or { LongArray(0) }
        typeOf<BooleanArray>() -> getBooleanArray(key) ?: def.or { BooleanArray(0) }
        typeOf<CharArray>() -> getCharArray(key) ?: def.or { CharArray(0) }
        typeOf<Array<CharSequence>>() -> getCharSequenceArray(key)
            ?: def.or { emptyArray<CharSequence>() }
        typeOf<Array<String>>() -> getStringArray(key) ?: def.or { emptyArray<String>() }


        else -> {
            /**
             * Must check array and list first!!
             */
            when {
                type.isSubtypeOf(typeOf<List<*>>()) -> {
                    when {
                        type.isSubtypeOf(typeOf<ArrayList<String>>()) ->
                            getStringArrayList(key) ?: def.or { arrayListOf<String>() }
                        type.isSubtypeOf(typeOf<ArrayList<CharSequence>>()) ->
                            getCharSequenceArrayList(key) ?: def.or { arrayListOf() }
                        type.isSubtypeOf(typeOf<ArrayList<Int>>()) ->
                            getIntegerArrayList(key) ?: def.or { arrayListOf() }
                        else -> throw Exception("获取参数当前不支持该类型")
                    }
                }
                type.isSubtypeOf(typeOf<Parcelable>()) -> {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                        getParcelable(key, type.javaClass) ?: def.or { type.jvmErasure.createInstance() }
                    } else {
                        getParcelable(key) ?: def.or { type.jvmErasure.createInstance() }
                    }
                }
                type.isSubtypeOf(typeOf<Serializable>()) -> {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                        val cls = type.javaClass as Class<Serializable>
                        getSerializable(key, cls) ?: def.or { type.jvmErasure.createInstance() }
                    } else {
                        getSerializable(key) ?: def.or { type.jvmErasure.createInstance() }
                    }
                }
                else -> throw Exception("获取参数当前不支持该类型")
            }
        }
    }
    return result as T
}

3.2. fragment代理

代码如下:

class FragmentParamsDelegate<T>(
    private val fragment: Fragment,
    private val customKey: String = "",
    private val defValue: T? = null,
    private val type: KType
) : ReadOnlyProperty<Any?, T> {

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        val key = customKey.ifEmpty { property.name }

        val bundle = fragment.arguments ?: return throw NullPointerException("没有找到Bundle对象")
        // bundle.get 在ActivityParamsDelegate中,由于这两个类在一个层级,所以是可以访问的
        return bundle.get(key, type, defValue)
    }
}

3.3. Params实际对外部调用代理

代码如下:

object ParamsExt {

    /**
     * 获取传递参数
     */
    inline fun <reified T> Activity.params(
        key: String,
        defaultValue: T? = null
    ) = ActivityParamsDelegate(this, key, defaultValue, typeOf<T>())

    /**
     * 获取传递的参数
     */
    inline fun <reified T> Fragment.params(
        key: String,
        defaultValue: T? = null
    ) = FragmentParamsDelegate(this, key, defaultValue, typeOf<T>())
}

4. 使用

// 设置参数
private fun fragmentSuccess() = FragmentXXX().bundle {
        setString("title", "Hello")
    }
class FragmentXXX: Fragment() {
	// 获取参数, activity中使用方式一致
	private val title: String by params("title", "")
}

总结

简简单单的小技巧,简化代码量看着也舒服了,不得不说kotlin拓展功能的强大,当然延迟加载功能在移动端开发挺实用的。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。