kotlinは簡単なAndroidルーティングを実現する(2)--』rxbusはintentの代わりに値を伝達する

25598 ワード

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のコード「RxJava 2.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ライブラリを使用しています.このライブラリは観察者としても送信者としても、最も重要な点です.異常が発生した後もメッセージを受信できます(これはプログラムの頑丈性にとって非常に重要です.そうしないと、1、2回異常が発生した後、イベントバスが使えなくなり、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)
}

2つのfragment間の伝達パラメータでもpoststickyではなくpostを完全に使用できる場合、poststicky単はActivityでの伝達値を満たすシーン(observableはpost後に登録される)
もちろんパッケージが悪いので、このようなイベントバスはfragment間の伝達パラメータを満たすことができ、ルーティングのfragmentのサポートはまだ行われていません.次はfragmentがルーティングを使用して切り替えることを実現する必要があります.