協程,相似線程,非阻塞式編程(像同步編寫同樣),在用戶態直接對線程進行管理,使用掛起當前上下文替代阻塞,從而能夠複用被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)) } } } }