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) } else if (obj is String) { bundle.putStringArrayList(key, value as ArrayList ) } else if (obj is CharSequence) { bundle.putCharSequenceArrayList(key, value as ArrayList ) } 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 ) { // bundle.putParcelableArray(key, value) // } // else if (value is Array) { // 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? = null private val mStickyEventMap: MutableMap , Any> init { bus = PublishRelay.create ().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使用路由进行切换