kotlin 实现一个简单 Android 路由(2)---》rxbus 代替intent进行传值

ok,前面一篇文章我们已经实现了基础功能,startactivity,kotlin 实现一个简单 Android 路由(1)但是还不能进行参数传递,这一篇就完成一下参数的传递了的。

当然,传递参数最先考虑的肯定是Intent了。

启动一个activity的时候把参数带过去这个是相对很容易的事情,但是我们看一下代码,我们在Router中需要一个addpama方法,当然我们完全可以做到类型判断,不需要手动添加类型的


    fun addPama(key:String,value:Any):Router{
        // 这里拿到 value 应该怎么组装后给到start 方法
        if (value == null) {
//            RLog.w("Ignored: The extra value is null.")
            return this
        }
        var bundle = intentEvent.bundle
        if (bundle == null) {
            bundle = Bundle()
        }
        if (value is Bundle) {
            bundle.putBundle(key, value)
        } else if (value is Byte) {
            bundle.putByte(key, value)
        } else if (value is Short) {
            bundle.putShort(key, value)
        } else if (value is Int) {
            bundle.putInt(key, value)
        } else if (value is Long) {
            bundle.putLong(key, value)
        } else if (value is Char) {
            bundle.putChar(key, value)
        } else if (value is Boolean) {
            bundle.putBoolean(key, value)
        } else if (value is Float) {
            bundle.putFloat(key, value)
        } else if (value is Double) {
            bundle.putDouble(key, value)
        } else if (value is String) {
            bundle.putString(key, value)
        } else if (value is CharSequence) {
            bundle.putCharSequence(key, value)
        } else if (value is ByteArray) {
            bundle.putByteArray(key, value)
        } else if (value is ShortArray) {
            bundle.putShortArray(key, value)
        } else if (value is IntArray) {
            bundle.putIntArray(key, value)
        } else if (value is LongArray) {
            bundle.putLongArray(key, value)
        } else if (value is CharArray) {
            bundle.putCharArray(key, value)
        } else if (value is BooleanArray) {
            bundle.putBooleanArray(key, value)
        } else if (value is FloatArray) {
            bundle.putFloatArray(key, value)
        } else if (value is DoubleArray) {
            bundle.putDoubleArray(key, value)
        }else if (value is CharArray) {
            bundle.putCharArray(key, value)
        }
        else if (value is IBinder) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                bundle.putBinder(key, value)
            } else {
//                RLog.e("putBinder() requires api 18.")
            }
        } else if (value is ArrayList<*>) {
            if (!value.isEmpty()) {
                val obj = value[0]
                if (obj is Int) {
                    bundle.putIntegerArrayList(key, value as ArrayList<Int>)
                } else if (obj is String) {
                    bundle.putStringArrayList(key, value as ArrayList<String>)
                } else if (obj is CharSequence) {
                    bundle.putCharSequenceArrayList(key, value as ArrayList<CharSequence>)
                } else if (obj is Parcelable) {
                    bundle.putParcelableArrayList(key, value as ArrayList<out Parcelable>)
                }
            }
        } else if (value is SparseArray<*>) {
            bundle.putSparseParcelableArray(key, value as SparseArray<out Parcelable>)
        } else if (value is Parcelable) {
            bundle.putParcelable(key, value)
        }

        else if (value is Serializable) {
            bundle.putSerializable(key, value)
        } else {
//            RLog.w("Unknown object type: " + value.javaClass.name)
        }
        // 目前这两种暂时无法支持自动识别,后续找找办法
        //        else if (value is Array<Parcelable>) {
        //            bundle.putParcelableArray(key, value)
        //        }
        //        else if (value is Array<String>) {
        //            bundle.putStringArray(key, value)}
        intentEvent.bundle = bundle
        return this
    }



然后我们在 startActivity的时候将intentEvent的bundle传过去就好了

不是很复杂,使用的时候也不错,我们可以这样去写代码了

router.initialize("activityfragment")
        .addPama("test","teststr")
        .addPama("testbool",true)
        .addPama("testInt",100)
        .start(baseContext)


是不是很清晰。但是我们如果获取数据的时候就比较不优雅了

var str =  intent.getStringExtra("test")
var b =  intent.getBooleanExtra("testbool",false)
var i =intent.getIntExtra("testInt",0)

就成这样的方式来获取了,当然我们Android一看就能明白,但是如果是ios或者EE的来一看这个代码就会有点蒙,不明白什么意思了,这样一块突兀的代码,显得很奇怪,我期望能是在router中去获取到参数,router.getPama()->{}这样的形式使得代码更清晰一些。

促使我放弃使用intent的还有另外一点 ,startactivityforresult的时候,接收参数 需要去写一个 onactivityResult()

然后在fragment传递参数的时候又是另外一种方式需要使用Arguments来传递参数,那么我们项目中传递参数的这个事情能不能简单一点,统一一点呢?


于是乎想了一些办法,感觉上都不是很好,于是想到使用eventbus 来做事件总监,又总觉得eventbus略显陈旧,于是在继续翻腾一阵之后,发现这个是可以使用ReactiveX来做的。

怎么做呢,先实现一个及简的rxbus,这块参考了tony的代码《RxJava2.x 实战》中的Rx实现Eventbus,精简了一下之后结果是这样的

class RxBus//禁用构造方法
private constructor() {
    private var bus: Relay<Any>? = null
    private val mStickyEventMap: MutableMap<Class<*>, Any>

    init {
        bus = PublishRelay.create<Any>().toSerialized()
        mStickyEventMap = ConcurrentHashMap()
    }

    fun post(event: Any) = bus!!.accept(event)

    private fun <T> toObservable(eventType: Class<T>): Observable<T> = bus!!.ofType(eventType)

    fun <T> register(eventType: Class<T>, onNext: Consumer<T>): Disposable =toObservable(eventType).observeOn(AndroidSchedulers.mainThread()).subscribe(onNext)

    fun postSticky(event: Any) {
        synchronized(mStickyEventMap) {
            mStickyEventMap.put(event.javaClass, event)
        }
        bus!!.accept(event)
    }

    /**
     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
     */
    private fun <T> toObservableSticky(eventType: Class<T>): Observable<T> {
        synchronized(mStickyEventMap) {
            val observable = bus!!.ofType(eventType)
            val event = mStickyEventMap[eventType]
            return if (event != null) {
                observable.mergeWith(Observable.create(object : ObservableOnSubscribe<T> {
                    override fun subscribe(e: ObservableEmitter<T>) {
                        e.onNext(eventType.cast(event))
                    }
                }))
            } else {
                observable
            }
        }
    }

    fun <T> registerSticky(eventType: Class<T>, onNext: Consumer<T>):Disposable
        = toObservableSticky(eventType).observeOn(AndroidSchedulers.mainThread()).subscribe(onNext)

    /**
     * 移除指定eventType的Sticky事件
     */
    fun <T> removeStickyEvent(eventType: Class<T>): T {
        synchronized(mStickyEventMap) {
            return eventType.cast(mStickyEventMap.remove(eventType))
        }
    }

    /**
     * 移除所有的Sticky事件
     */
    fun removeAllStickyEvents() {
        synchronized(mStickyEventMap) {
            mStickyEventMap.clear()
        }
    }
    fun unregister(disposable: Disposable?) {
        if (disposable != null && !disposable.isDisposed()) {
            disposable.dispose()
        }
    }
    private object Holder {
        val BUS = RxBus()
    }
    companion object {
        fun get(): RxBus {
            return Holder.BUS
        }
    }
}



简单说明一下,这个是使用了一个大神基于RxAndroid封装的Relay库,这个库既可以作为观察者,又可以作为发送者,最重要的一点,出现异常之后依然能接收消息(这个对程序健壮性非常重要,否则出现一两次异常之后事件总线不能用了,整个app就完蛋了)
然后呢里面有一个 registerSticky 方法,注册一个 toObservableSticky,看下代码,postSticky的时候,我们将事件同步,存入 mStickyEventMap 中了,然后注册的时候,从map中取出数据,发送给observable,这就解决了一个问题,observable可以在post之后进行注册,然后并接收之前发送的参数那么封装一下之后,我们可以在页面和页面之间进行参数传递,返回传递等等

@Route("testactivity")
class TestActivity: Activity() {
    var dispose :Disposable?=null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        // 好吧,这个写法确实有点傻,但是没有想到什么更好的办法,先就这样吧,然后整体进行优化
        dispose = RxBus.get().registerSticky(IntentEvent::class.java,onNext = Consumer {
            Log.e("result",it.bundle.getString("test"))
            Log.e("result",it.bundle.getBoolean("testbool").toString())
            Log.e("result",it.bundle.getInt("testInt").toString())
            Log.e("result",it.bundle.getDouble("testfloat").toString())
        })
    }

    override fun onBackPressed() {
        RxBus.get().unregister(dispose)//一定要 unregister
        RxBus.get().post(RouteResponse("respose")) //这里进行参数的回传
        super.onBackPressed()
    }

}

abstract class RouterResultActivity : Activity() {
    var backdispose : Disposable?=null  // 接收下一个页面的回传值

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        backdispose = RxBus.get().register(RouteResponse::class.java,onNext = Consumer {
            getResult(it)
        })
    }

    // 实现这个接口获取参数
    abstract fun getResult(response:RouteResponse)

    override fun onBackPressed() {
        // 关闭Disposable
        RxBus.get().unregister(backdispose)
        super.onBackPressed()
    }

    fun back(){
        RxBus.get().unregister(backdispose)
        finish()
    }
}
继承这样一个基类来完成参数获取

然后在需要获取回传参数的activity中实现一个gerResult方法就ok了

override fun getResult(response: RouteResponse) {
    Log.e("result",response.target)
}


如果是两个fragment之间传递参数也完全可以使用post,而不是poststicky,poststicky单拿出来说是因为满足Activity中进行传值的场景(observable在post之后才进行注册


当然现在封装很差,这样的事件总线已经能够满足fragment之间传递参数,路由的fragment的支持还没有做,下一步我们需要实现一下fragment使用路由进行切换


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值