協程入門(六):調度方案比較

主要比較的線程切換的控制方案java

需求:

  • 1.根據url地址下載播放列表, 網絡訪問,運行後臺
  • 2.先解析下載好播放列表文件中佈局相關信息,協議解析,運行後臺
  • 3.根據佈局信息先繪製出界面框架,界面繪製,運行主線程
  • 4.接着解析播放列表中的播放素材列表,解析協議,運行後臺
  • 5.界面上播放多媒體素材,運行主線程
  • 6.反饋平臺播放結果,網絡訪問,運行後臺

Android原生方式實現

很容易會採用回調的方式,來實現android

val mainHandler = Handler(Looper.getMainLooper())

    interface CallBack {
        fun onSuccess(response : String)
    }

    /**
     * 1.根據url地址下載播放列表, 網絡訪問,運行後臺
     */
    fun download(url : String, callBack : CallBack) {
        thread {
            println("根據url下載播放節目表 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
            callBack.onSuccess("節目表")
        }
    }

    /**
     * 2.先解析下載好播放列表文件中佈局相關信息,協議解析,運行後臺
     */
    fun parseLayout(filePath : String, callBack : CallBack) {
        thread {
            println("先解析節目表界面佈局 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
            callBack.onSuccess("界面佈局信息")
        }
    }

    /**
     * 3.根據佈局信息先繪製出界面框架,界面繪製,運行主線程
     */
    fun drawLayout(layoutInfo : String, callBack : CallBack) {
        mainHandler.post(Runnable {
            println("繪製ui界面佈局 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
            callBack.onSuccess("佈局繪製完成")
        })
    }

    /**
     * 4.接着解析播放列表中的播放素材列表,解析協議,運行後臺
     */
    fun parsePlayList(filePath : String, callBack : CallBack) {
        thread {
            println("繼續解析節目單播放的素材內容 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
            callBack.onSuccess("播放素材列表")
        }
    }

    /**
     * 5.界面上播放多媒體素材,運行主線程
     */
    fun startPlay(playList : String, callBack : CallBack) {
        mainHandler.post(Runnable {
            println("播放多媒體素材 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
            callBack.onSuccess("播放成功")
        })
    }

    /**
     * 6.反饋平臺播放結果,網絡訪問,運行後臺
     */
    fun notifyResult() {
        thread {
            println("反饋平臺播放結果 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
        }
    }

    /**
     * android原生方式, 回調一旦太多,就會陷入回調地獄。。。
     *
     * 而且是把線程控制封裝在相應的api中,一旦線程控制放在外面,則更加難以理解
     */
    fun android() {

        download("http://....", object : CallBack {
            override fun onSuccess(filePath: String) {
                parseLayout(filePath, object : CallBack {
                    override fun onSuccess(layoutInfo: String) {
                        drawLayout(layoutInfo, object : CallBack {
                            override fun onSuccess(filePath: String) {
                                parsePlayList(filePath, object : CallBack {
                                    override fun onSuccess(playList: String) {
                                        startPlay(playList, object : CallBack {
                                            override fun onSuccess(response: String) {
                                                notifyResult()
                                            }
                                        })
                                    }
                                })
                            }
                        })
                    }
                })
            }
        })
    }
複製代碼

開源rxjava實現

rxjava相比原生方式,線程控制更加方便api

/**
     * 1.根據url地址下載播放列表, 網絡訪問,運行後臺
     */
    fun _download(url : String) : String {
        println("根據url下載播放節目表 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
        return "節目表"
    }

    /**
     * 2.先解析下載好播放列表文件中佈局相關信息,協議解析,運行後臺
     */
    fun _parseLayout(filePath : String) : String {
        println("先解析節目表界面佈局 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
        return "界面佈局信息"
    }

    /**
     * 3.根據佈局信息先繪製出界面框架,界面繪製,運行主線程
     */
    fun _drawLayout(layoutInfo : String) : String {
        println("繪製ui界面佈局 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
        return "佈局繪製完成"
    }

    /**
     * 4.接着解析播放列表中的播放素材列表,解析協議,運行後臺
     */
    fun _parsePlayList(filePath : String) : String {
        println("繼續解析節目單播放的素材內容 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
        return "播放素材列表"
    }

    /**
     * 5.界面上播放多媒體素材,運行主線程
     */
    fun _startPlay(playList : String) : String {
        println("播放多媒體素材 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
        return "播放成功"
    }

    /**
     * 6.反饋平臺播放結果,網絡訪問,運行後臺
     */
    fun _notifyResult() {
        println("反饋平臺播放結果 thread.name = ${Thread.currentThread().name} , id = ${Thread.currentThread().id}")
    }

    /**
     * 使用rxjava的方式,可以免得一大堆自定義的結果回調,以及帶來方便的線程切換功能
     */
    @Test
    fun rxjava() {
            Observable.just("url")
                    .map(object : Function<String, String> {
                        override fun apply(url: String?): String {
                            //後臺線程
                            return _download("http://......")
                        }
                    })
                    .map(object : Function<String, String> {
                        override fun apply(filePath: String): String {
                            //後臺線程
                            return _parseLayout(filePath)
                        }
                    })
                    .observeOn(AndroidSchedulers.mainThread())
                    .map(object : Function<String, String> {
                        override fun apply(layoutInfo: String): String {
                            //UI線程
                            return _drawLayout(layoutInfo)
                        }
                    })
                    .observeOn(Schedulers.newThread())
                    .map(object : Function<String, String> {
                        override fun apply(filePath: String): String {
                            //後臺線程
                            return _parsePlayList(filePath)
                        }
                    })
                    .observeOn(AndroidSchedulers.mainThread())
                    .map(object : Function<String, String> {
                        override fun apply(playList: String): String {
                            //UI線程
                            return _startPlay(playList)
                        }
                    })
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(Schedulers.newThread())
                    .subscribe(object : Consumer<String> {
                        override fun accept(isSuccess: String) {
                            //後臺線程
                            _notifyResult()
                        }
                    })
    }

    //經過lambda表達式優化下

    fun rxjavaLambda() {
         Observable.just("http://......")
                    //後臺線程
                    .map { url -> _download(url) }
                    //後臺線程
                    .map { filePath -> _parseLayout(filePath) }
                    .observeOn(AndroidSchedulers.mainThread())
                    //UI線程
                    .map { layoutInfo -> _drawLayout(layoutInfo) }
                    .observeOn(Schedulers.newThread())
                    //後臺線程
                    .map { filePath -> _parsePlayList(filePath) }
                    .observeOn(AndroidSchedulers.mainThread())
                    //UI線程
                    .map { playList -> _startPlay(playList) }
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(Schedulers.newThread())
                    //後臺線程
                    .subscribe{isSuccess -> _notifyResult()}
    }
複製代碼

kotlin協程實現

協程的方式同樣擁有很好的線程切換方式,而且可以比rxjava更加高效(看示例中的註釋),表達上,更加簡潔。(結合異常處理後,還會有更好的優點)bash

fun coroutines() {
        //async默認最後一行是返回值,因此return@async均可以去掉
        runBlocking {
            //後臺線程
            var filePath = async(Dispatchers.Default) {  _download("http://...")}.await()
            //後臺線程
            val layoutInfo = async(Dispatchers.IO) { _parseLayout(filePath) }.await()
            //這邊是與上邊rxjava方案相比最大的不一樣,能夠實現_drawLayout與_parsePlayList同步進行,效率更高
            //UI線程
            launch(Dispatchers.Main) { _drawLayout(layoutInfo) }
            //後臺線程
            val playList = async(Dispatchers.IO) { _parsePlayList(filePath) }.await()
            //UI線程
            val isSuccess = async(Dispatchers.Main) { _startPlay(playList) }.await()
            //後臺線程
            launch(Dispatchers.Default) { _notifyResult() }
        }
    }
複製代碼

微信公衆號

相關文章
相關標籤/搜索