Activity间优雅地处理参数

174 阅读3分钟

传统方式


1.Activity内部封装跳转逻辑

class VideoDetailActivity : BaseActivity(), InitUI, View.OnClickListener {

    companion object {
        fun start(
            activity: Activity,
            data: ArrayList<VideoBean>,
            position: Int
        ) {
            val intent = Intent(activity, VideoDetailActivity::class.java).apply {
                this.putParcelableArrayListExtra("data",data)
                this.putExtra("position",position)
            }
            activity.startActivity(intent)
        }
    }
  1. 获取值
override fun onCreate(savedInstanceState: Bundle?) {
    val position = intent.getIntExtra("position",0)
    val data = intent.getParcelableArrayListExtra<VideoBean>("data")
}

3.优缺点

优点:

  1. 大家都很熟悉,几乎没有阅读成本以及学习成本;
  2. 值类型显而易见,intent.getIntExtra("position",0),明确知道获取值的类型是int;

缺点:

  1. key跟接收值的变量名定义似乎重复了,val position = intent.getIntExtra("position",0),key是"position",变量名名也是position;
  2. 对应获取值,需要明确知道使用哪个api(getIntExtra、getStringExtra等),有点不太智能(程序员都是很懒的);
  3. getXXXExtra看起来像是模板代码,一旦传递的参数较多,代码就显得很冗余;

思考


  1. 设置参数,有没有办法像给HashMap put参数一样那么简单呢?
  2. 是否可以把接收参数的变量名当成key来使用呢?
  3. 是否可以不使用getXXXExtra方式来获取参数呢?

针对以上的思考,有如下设计:

扩展函数+属性委托传参

  1. 扩展函数,key value方式设置参数

给Intent增加扩展函数set,operator是重载运算符,以下代码定义: 给Intent增加set扩展函数set,且重载它的运算符,那么就可以像HashMap那样设置参数,解决了思考1

//定义
operator fun Intent.set(key: String, value: Any) {
    when (value) {
        is Int -> {
            this.putExtra(key, value)
        }
        is ArrayList<*> -> {
            this.putParcelableArrayListExtra(key, value as ArrayList<Parcelable>)
        }
    }
}

//调用
val intent = Intent(activity, VideoDetailActivity::class.java).apply {
    this["data"] = data
    this["position"] = position
}

这里就可以只关注设置参数,不用关心应该调用哪个具体的函数。简单点说,就是可以更关注业务。

  1. Kotlin委托方式获取值

//定义属性委托类,只读符合业务场景
class IntentsProperty<in A : Any, out T : Any>(private val f:(()->Bundle?)) :
    ReadOnlyProperty<A, T> { //1

    private var value: T? = null //2

    override operator fun getValue(thisRef: A, property: KProperty<*>): T {//3
        value?.let { return it }//4
        val name = property.name///5

        return ((f.invoke()?.get(name)) as T).let {
            value = it//6
            it
        }
    }
}


//定义Activity的扩展函数
inline fun <reified T : Any> Activity.intents(): IntentsProperty<Any, T> {//7
    return IntentsProperty{
        this.intent.extras//8
    }
}


//调用

private val position: Int by intents()// 9

private val data: ArrayList<VideoBean> by intents()

注释1:

  • 定义属性委托类IntentsProperty实现ReadOnlyProperty接口;
  • 第一个泛型in A : Any,表示被委托属性所在的类,因为要在Activity以及Fragment都可以使用,所以传入Any;- 第二个泛型out T : Any,表示getValue函数返回的类型,因为被委托的属性可能是任意类型,所以同样传入Any;
  • private val f:(()->Bundle?),需传入可以获取Bundle对象的高阶函数;

注释2:value用来保存值,避免重复从Bundle中获取;

注释3:当被委托属性被调用,getValue函数会被触发;第一参数为被委托属性所在类的对象,第二个参数为被委托属性的信息封装类;

注释4:当有值,即可返回;

注释5:获取被委托属性的名称,名称即为key,这里解决了思考2

注释6:调用高阶函数获取Bundle,从其获取传递过来的值,并保存起来;

注释7:定义Activity的扩展函数intents,那么就可以在Activity使用by intents()处理属性委托,这里解决了思考3

注释8:传入高阶函数this.intent.extras;

3.优缺点

优点:

  1. 参数名就是获取参数的key,保证传参key跟取参key一致,相当于一种协议;
  2. 设置参数以及获取值,可以不用关心具体调用哪个函数;

缺点:

  1. 只能用在Kotlin;
  2. 有一定的学习成本;