kotlin學習-Coroutines(協程)

協程,相似線程,非阻塞式編程(像同步編寫同樣),在用戶態直接對線程進行管理,使用掛起當前上下文替代阻塞,從而能夠複用被delay的線程,大量減小了線程資源浪費。java

基本使用:android

fun runAsync()= runBlocking {
    val time = measureTimeMillis {//系統函數統計時間
        val one = async { doSomethingUsefulOne() }//異步調用,返回結果
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")//等待異步執行完成(wait調用會掛起當前線程)
    }
    println("Completed in $time ms")
}

//協程coroutines 調用方法須要suspend修飾
suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}

 這裏面沒有使用異步+回調,直接像寫同步代碼同樣,簡潔編程

launch 返回Job可取消任務app

fun cancelCoroutine() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        while (isActive) { // cancellable computation loop
            // print a message twice a second
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // cancels the job and waits for its completion
    println("main: Now I can quit.")
}

線程之間切換異步

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun jumpCor(){//建立單線程coroutines
    newSingleThreadContext("Ctx1").use { ctx1 ->
        newSingleThreadContext("Ctx2").use { ctx2 ->
            runBlocking(ctx1) {
                log("Started in ctx1")
                withContext(ctx2) {
                    log("Working in ctx2")
                }
                log("Back to ctx1")
            }
        }
    }
}

 方法內建立Scopesocket

suspend fun showSomeData() = coroutineScope {
      val data = async(Dispatchers.IO) { // IO task  io線程調用操做
//          ... load some UI data for the Main thread ...
       }

    withContext(Dispatchers.Main){//UI task  UI更新
        val result = data.await()
//        display(result)
    }
}

 

協程上下文環境,CoroutineScope,CoroutineContextasync

每一個協程運行須要在指定Scope內才能使用協程相關方法delay,asyc,launch,建立CoroutineScope ,runBlocking函數內部會建立CoroutineScope,系統提供GlobalScope,MainScope等輔助類內部建立Scopeide

也能夠經過CoroutineContext和Job建立本身的CoroutineScope函數

 

fun sampleCreateCorountine(){
    //create corountine scope
    //自定義CoroutineScope
    val coroutineContext = Dispatchers.Default
    val job = Job()
    val coroutineScope = CoroutineScope(coroutineContext + job)
    //使用scope
    coroutineScope.launch {

    }
    //建立全局Scope
    GlobalScope.launch (Dispatchers.Default+CoroutineName("global background thread")){

    }
    //建立主線程分發處理Scope
    MainScope().launch {

    }

}

 

類內部定義協程oop

1,直接繼承CoroutineScope

class SomethingWithLifecycle : CoroutineScope {
    // 使用job來管理你的SomethingWithLifecycle的全部子協程
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    fun destory(){//退出取消
        job.cancel()
    }
}

2,直接使用已定義Scope

class CorMyActivity : AppCompatActivity(), CoroutineScope by MainScope() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        showSomeData()
    }

    /**
     * Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
    in this method throws an exception, then all nested coroutines are cancelled.
     */
    fun showSomeData() = launch {
        // <- extension on current activity, launched in the main thread
        // ... here we can use suspending functions or coroutine builders with other dispatchers
//        draw(data) // draw in the main thread
    }

    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }

}

 

 

Dispatchers,協程分發器:

fun dispatchTask()= runBlocking<Unit> {
    // it inherits the context (and thus dispatcher) from the CoroutineScope that it is being launched from.
        launch { // context of the parent, main runBlocking coroutine
            println("main runBlocking      : I'm working in thread ${Thread.currentThread().name}")
        }
    //執行coroutine是在調用者的線程,可是當在coroutine中第一個掛起以後,後面所在的線程將徹底取決於
    // 調用掛起方法的線程(如delay通常是由kotlinx.coroutines.DefaultExecutor中的線程調用)
    //Unconfined在掛起後在delay的調用線程DefaultExecutor執行
        launch(context = Dispatchers.Unconfined) { // not confined -- will work with main thread
            println("Unconfined            : I'm working in thread ${Thread.currentThread().name}")
        }
    // coroutines are launched in GlobalScope,uses shared background pool of threads
    //uses the same dispatcher as GlobalScope.launch
  //Dispatchers.Default 處理cup密集型任務,線程數爲cpu內核數,最少爲2,Dispatchers.IO 處理阻塞性IO,socket密集度任務,數量隨任務多少變化,默認最大數量64 launch(context = Dispatchers.Default) { // will get dispatched to DefaultDispatcher println("Default : I'm working in thread ${Thread.currentThread().name}") } //creates a thread for the coroutine to run launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") } }

 

協程結合Architecture ViewModel

class NewsViewModel: ViewModel() {

    private val mApi:WebServer
    init {
        mApi = WebServer()
    }

    val dataNews: MutableLiveData<DataResource<NewsDataRsp>> by lazy {
//        MutableLiveData<DataResource<NewsDataRsp>>().also {
//            loadNewsData(minId=null)
//        }
        MutableLiveData<DataResource<NewsDataRsp>>()
    }

     fun loadNewsData(pageIndex:Int =1,countItem:Int = 20,minId:String?=null){
        runCoroutine(dataNews){
            val mp = mutableMapOf("encode" to "ywjh","source" to "app","sys" to "android","banner" to "banner",
                    "limit" to countItem.toString(),"version" to "7002000")
            if(pageIndex>1 && false==minId.isNullOrEmpty()){
                mp.put("min_id",minId)
            }
            val response = mApi.commonDataSourceApi.getNewsData(mp).execute()
            return@runCoroutine response.body()!!
        }
    }

     fun fetchNews(pageIndex:Int =1,countItem:Int = 20,minId:String){
         val mp = mutableMapOf("encode" to "ywjh","source" to "app","sys" to "android","banner" to "banner",
                 "limit" to countItem.toString(),"version" to "7002000")
         if(pageIndex>1 && false==minId.isNullOrEmpty()){
             mp.put("min_id",minId)
         }

         val cor = CoroutineScope(Dispatchers.IO)
         cor.launch {
             try {
                 val response = mApi.commonDataSourceApi.getNewsData(mp).execute()
                 dataNews.postValue(DataResource(DataResource.Status.COMPLETED, response.body(), null))
             } catch (exception: Exception) {
                 dataNews.postValue(DataResource(DataResource.Status.COMPLETED, null, exception))
             }
         }
    }

    suspend fun simpleGetData(pageIndex:Int =1,countItem:Int = 20,minId:String) = withContext(Dispatchers.IO) {
        val mp = mutableMapOf("encode" to "ywjh","source" to "app","sys" to "android","banner" to "banner",
                "limit" to countItem.toString(),"version" to "7002000")
        if(pageIndex>1 && false==minId.isNullOrEmpty()){
            mp.put("min_id",minId)
        }

        try {
            val response = mApi.commonDataSourceApi.getNewsData(mp).execute()
            dataNews.postValue(DataResource(DataResource.Status.COMPLETED, response.body(), null))
        } catch (exception: Exception) {
            dataNews.postValue(DataResource(DataResource.Status.COMPLETED, null, exception))
        }
    }

    private fun <T> runCoroutine(correspondenceLiveData: MutableLiveData<DataResource<T>>, block: suspend () -> T) {
        correspondenceLiveData.value = DataResource(DataResource.Status.LOADING, null, null)

        GlobalScope.launch(Dispatchers.IO) {
            try {
                val result = block()
                correspondenceLiveData.postValue(DataResource(DataResource.Status.COMPLETED, result, null))
            } catch (exception: Exception) {
//                val error = ErrorConverter.convertError(exception)
                correspondenceLiveData.postValue(DataResource(DataResource.Status.COMPLETED, null, exception))
            }
        }
    }

}
相關文章
相關標籤/搜索