相关文章推荐
温文尔雅的甜瓜  ·  Python Pandas ...·  1 年前    · 
活泼的黄花菜  ·  Newtonsoft.Json.Linq.J ...·  1 年前    · 
温文尔雅的豆腐  ·  javascript - How to ...·  1 年前    · 

一手遮天 Android - kotlin 协程: 通过 ticker 信道实现类似计时器的效果,协程的异常处理,解决协程的并发问题

示例如下:

/kotlin/coroutine/Demo5.kt

* coroutine - 协程 * 本例用于演示通过 ticker 信道实现类似计时器的效果,协程的异常处理,解决协程的并发问题 package com.webabcd.androiddemo.kotlin.coroutine import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import com.webabcd.androiddemo.R import kotlinx.android.synthetic.main.activity_kotlin_coroutine_demo5.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.ticker import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.atomic.AtomicInteger class Demo5 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_kotlin_coroutine_demo5) // 通过 ticker 信道实现类似计时器的效果 button1.setOnClickListener { sample1() // 协程的异常处理 button2.setOnClickListener { sample2() // 解决协程的并发问题 button3.setOnClickListener { sample3() fun sample1() { CoroutineScope(Dispatchers.Default).launch { // 通过 ticker() 创建一个信道,此信道会定时发送一个 Unit 用于实现类似计时器的效果 val tickerChannel = ticker(delayMillis = 500, initialDelayMillis = 0) repeat(100) { tickerChannel.receive() // 本例中每 500 毫秒会收到一个 Unit appendMessage("tick") if (it > 2) { tickerChannel.cancel() // 取消接收 // 06:08:51.179: tick(DefaultDispatcher-worker-1) // 06:08:51.634: tick(DefaultDispatcher-worker-1) // 06:08:52.128: tick(DefaultDispatcher-worker-1) // 06:08:52.628: tick(DefaultDispatcher-worker-1) fun sample2() { CoroutineScope(Dispatchers.Default).launch { launch { // 在此协程外,你是 catch 不到此异常的,程序会崩溃 // 需要的话,请在此协程内写 try/catch throw Exception() // 对于 GlobalScope.launch() 启动的顶级协程来说,你可以通过如下方式 catch 异常 val myExceptionHandler = CoroutineExceptionHandler { _, exception -> appendMessage("myExceptionHandler: $exception") GlobalScope.launch(Dispatchers.Default + myExceptionHandler) { throw Exception("launch exception") // async/await 异常的捕获方法如下 val deferred = GlobalScope.async { throw Exception() try { deferred.await() } catch (e: Exception) { appendMessage("async/await exception") // 用于模拟并发问题 suspend fun myFun(action: suspend () -> Unit) = coroutineScope { // 启动 200 个协程,因为协程可能会被分配到不同的线程,所以协程也是有并发问题的 repeat(200) { launch { // 每个协程执行 1000 次累加 repeat(1000) { action() fun sample3() { var counter1 = 0 val counter2 = AtomicInteger() var counter3 = 0 var counter4 = 0 val mutex = Mutex() CoroutineScope(Dispatchers.Default).launch { // 这么做是有并发问题的 myFun { counter1++ appendMessage("counter1: $counter1") // 使用支持原子操作的对象,就不会有并发问题了,而且效率很高 myFun { counter2.incrementAndGet() appendMessage("counter2: $counter2") // 强制要求只在指定的线程执行,就不会有并发问题了,但是效率比较低 val myThread = newSingleThreadContext("myThread") withContext(Dispatchers.Default) { myFun { withContext(myThread) { counter3++ appendMessage("counter3: $counter3") // 通过互斥锁避免并发问题,常规做法,效率一般 myFun { mutex.withLock { counter4++ appendMessage("counter4: $counter4") // 06:39:35.580: counter1: 195130(DefaultDispatcher-worker-2) // 有并发问题 // 06:39:35.683: counter2: 200000(DefaultDispatcher-worker-2) // 使用支持原子操作的对象,效率很高,本例用了 100 毫秒左右完成 // 06:39:49.887: counter3: 200000(DefaultDispatcher-worker-1) // 强制要求只在指定的线程执行,效率比较低,本例用了 1420 毫秒左右完成 // 06:39:55.249: counter4: 200000(DefaultDispatcher-worker-2) // 通过互斥锁避免并发问题,常规做法,效率一般,本例用了 540 毫秒左右完成 fun appendMessage(message: String) { val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.ENGLISH) val time = dateFormat.format(Date()); val threadName = Thread.currentThread().name CoroutineScope(Dispatchers.Main).launch{ val log = "$time: $message($threadName)" textView1.append(log); textView1.append("\n"); Log.d("coroutine", log)

/layout/activity_kotlin_coroutine_demo5.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="通过 ticker 信道实现类似计时器的效果"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="协程的异常处理"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="解决协程的并发问题"/>
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd