协程入门(六):调度方案比较

540 阅读4分钟

主要比较的线程切换的控制方案

需求:

  • 1.根据url地址下载播放列表, 网络访问,运行后台
  • 2.先解析下载好播放列表文件中布局相关信息,协议解析,运行后台
  • 3.根据布局信息先绘制出界面框架,界面绘制,运行主线程
  • 4.接着解析播放列表中的播放素材列表,解析协议,运行后台
  • 5.界面上播放多媒体素材,运行主线程
  • 6.反馈平台播放结果,网络访问,运行后台

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相比原生方式,线程控制更加方便


    /**
     * 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更加高效(看示例中的注释),表达上,更加简洁。(结合异常处理后,还会有更好的优势)

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() }
        }
    }

微信公众号