当前位置: 首页>编程语言>正文

Android kotlin 协程轮询任务 kotlin开启协程

  • 协程
  • 如何使用协程?
  • 1.启动协程的方式
    - launch 启动一个协程,返回一个Job,可用来取消协程
    - async 启动一个带返回结果的协程Deferred,通过Deferred.await()获取结果;
    有异常并不会直接抛出,只会在调用 await 的时候抛出
    - public interface Deferred : Job {}
    - withContext 启动一个协程,传入CoroutineContext改变协程运行的上下文
  • 2.协程运行的线程
    - Dispatchers.Main -Android中的主线程
    - Dispatchers.IO -针对磁盘和网络IO进行了优化,适合IO密集型的任务,比如:读写文件,操作数据库以及网络请求
    - Dispatchers.Default -适合CPU密集型的任务,比如解析JSON文件,排序一个较大的list
    - Dispatchers.Unconfined
  • 3.协程执行的函数或者方法使用suspend修饰
    - 开启协程,示例代码:
fun main() {
		    val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job
			    delay(1000L)
			    println("World!")
			}
			println("Hello,")
			job.join() 	// wait until child coroutine completes
		}
  • 协程的取消
    - 调用cancel()或者job.cancelAndJoin()方法取消协程,运行中的协程任务并不会立即结束,需要配合Job的【isActive】属性判断。
    - 正常情况下,父协程被取消,它的子协程都会被取消。
    - 同一个CoroutineScope作用域取消了,其下面所有的子协程也会被取消
    - 这种情况下,子协程不会被取消
try {
                repeat(10) {
                     Log.d(TAG, "test: $it")
                     delay(500L)
                 }
             } finally {
                 withContext(NonCancellable) {
                     Log.d(TAG, "test: withContext NonCancellable")
                 }
                 launch(NonCancellable) {
                     Log.d(TAG, "test: launch NonCancellable")
                 }
             }
             delay(1300L)
         	launch.cancel()
         	
         	-------------------------------
         	结果如下:
         	test: 0
	test: 1
	test: 2
	test: withContext NonCancellable
	test: launch NonCancellable
  • 超时取消
    - withTimeout(2000L) 取消失败会抛出异常信息:kotlinx.coroutines.TimeoutCancellationException
    - withTimeoutOrNull(2000L) 取消失败会返回 null
    - SupervisorJob —— Job是有父子关系的,如果存在多个子job,任一个子Job失败了,父Job也就会失败。
    SupervisorJob是一个特殊的Job,里面的子Job不会相互影响,其中一个子Job失败了,不影响其他子Job的执行。
  • 协程作用域?
  • GlobalScope 单例的scope
public object GlobalScope : CoroutineScope {
		    /**
		     * Returns [EmptyCoroutineContext].
		     */
		    override val coroutineContext: CoroutineContext
		        get() = EmptyCoroutineContext
		}
  • MainScope()
    @Suppress(“FunctionName”)
    public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
    作用域嵌套的情况,MainScope作用域下开启的协程不会被取消
val scope = GlobalScope.launch {
           MainScope.async {
               Log.d(TAG, "async。。。。")
        }
    }
    scope.cancel()
  • 协程间数据同步
  • 使用线程安全的数据类型,如:AtomicInteger。volatile在协程间不能保证数据的一致性
  • Mutex 类似于线程中使用的 synchronized or ReentrantLock
    - Mutex.withLock{ … }
public suspend inline fun <T> Mutex.withLock(owner: Any? = null, action: () -> T): T {
	    lock(owner)
	    try {
	        return action()
	    } finally {
	        unlock(owner)
	    }
	}
  • actor 使用方法有点复杂
  • 异常处理 CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception -> 
	    println("Caught $exception") 
	}
	val job = GlobalScope.launch(handler) {
	    throw AssertionError()
	}
  • java代码如何使用kotlin的协程?
  • java中不能直接启动协程,需要在Kotlin类中使用协程,实现逻辑功能,然后在java代码中调用该类。
  • 接口改造示例代码:
  • ViewModel 基类 EasyVM.kt
val TAG: String? = EasyVM::class.java.canonicalName
abstract class EasyVM(application: Application) : AndroidViewModel(application) {
    private val mainScope = MainScope()
    fun launch(
        context: CoroutineContext = Dispatchers.Main,
        block: suspend CoroutineScope.() -> Unit
    ) =
        mainScope.launch(context) {
            try {
                block()
            } catch (e: Exception) {
                Log.e(TAG, "[launch error info]:" + e.message)
            }
        }

    /**
     * viewmodel scope async
     */
    suspend fun <T> async(
        context: CoroutineContext = Dispatchers.Default,
        block: suspend CoroutineScope.() -> T
    ) =
        withContext(context) {
            val result: T? = try {
                block()
            } catch (e: Exception) {
                Log.e(TAG, "[async error info]:" + e.message)
                null
            }
            result
        }

    override fun onCleared() {
        super.onCleared()
        mainScope.cancel()

    }
}
  • 生成VM对象与之前相同。ViewModelProviders.of(this).get(VM.class);
  • 简单封装retrofit网络库 EasyNet.kt
object EasyNet {
    val EMPTY_CONFIG = NetConfig()
    //全局默认配置
    var gConfigMap = LinkedHashMap<String, NetConfig>()

    inline fun <reified T> init(config: NetConfig = EMPTY_CONFIG) {
        if (T::class.java.canonicalName?.isEmpty()!!) {
            throw IllegalArgumentException("illegal argument T error !!!")
        }
        checkNonnull(config)
        gConfigMap[T::class.java.canonicalName!!] = config
    }
    inline fun <reified T> create(config: NetConfig? = gConfigMap[T::class.java.canonicalName!!]): T {
        config?.let {
            checkNonnull(config)
            return Retrofit.Builder()
                .baseUrl(config.baseUrl)
                .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
                .client(providerClient(config))
                .build()
                .create(T::class.java)
        }
        throw IllegalArgumentException("illegal argument error:config must be init!")
    }

    fun providerClient(config: NetConfig): OkHttpClient =
        OkHttpClient.Builder().apply {
            addInterceptor(providerHeaderInterceptor(config))
            addInterceptor(providerLogInterceptor(config))
            config.interceptors.forEach { addInterceptor(it) }
            config.dns?.let { dns(it) }
        }.build()

    private fun providerHeaderInterceptor(config: NetConfig): Interceptor = Interceptor { chain ->
        val origin = chain.request()
        val newBuilder = origin.newBuilder().headers(Headers.of(config.headers)).build()
        chain.proceed(newBuilder)
    }

    private fun providerLogInterceptor(config: NetConfig): Interceptor =
        HttpLoggingInterceptor().apply { level = config.logLevel }

    fun checkNonnull(config: NetConfig) {
        if (config.baseUrl.isEmpty()) {
            throw IllegalArgumentException("illegal argument error:Expected baseUrl scheme 'http' or 'https' but no colon was found!!!")
        }
    }
}
class NetConfig {
    /**
     * 请求dns
     */
    var dns: Dns? = null
    /**
     * 请求基地址
     */
    var baseUrl = ""
    /**
     * 拦截器
     */
    val interceptors = LinkedList<Interceptor>()
    /**
     * 头部参数
     */
    val headers = LinkedHashMap<String, String>()
    /**
     * 网络请求日志level
     */
    var logLevel = HttpLoggingInterceptor.Level.HEADERS
}
  • 协程的支持情况
    - retrofit 2.6.0
    - LiveData
    - Room
    - WorkManager
  • 协程优缺点
    - 优点:
    - 1.协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
    - 2.单线程内就可以实现并发的效果,最大限度地利用cpu
    - 3.可以按照同步思维写异步代码,即用同步的逻辑,写由协程调度的回调
    - 缺点:
    - 1.协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
    - 2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
    注意事项:
    - 1. runBlocking 是会阻塞主线程的,直到 runBlocking 内部全部子任务执行完毕。减少使用!!!



https://www.xamrdz.com/lan/5kn1963830.html

相关文章: