Kotlin协程 - - - 协程的简单使用

Kotlin协程 - - - 协程的简单使用,第1张

一.协程介绍 1.简介

协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。

在 Android 上,协程有助于管理长时间运行的任务,如果管理不当,这些任务可能会阻塞主线程并导致应用无响应。使用协程的专业开发者中有超过 50% 的人反映使用协程提高了工作效率。本主题介绍如何使用 Kotlin 协程解决以下问题,从而让您能够编写出更清晰、更简洁的应用代码。

2.特点

协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括:

  • 轻量:您可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行 *** 作。
  • 内存泄漏更少:使用结构化并发机制在一个作用域内执行多项 *** 作。
  • 内置取消支持:取消 *** 作会自动在运行中的整个协程层次结构内传播。
  • Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。

二.协程的简单应用

1.用协程写Retrofit案例

先导入依赖

 //协程依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC-native-mt'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC-native-mt'
//retrofit依赖
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
//实体类
data class User(
    val `data`: List,
    val errorCode: Int,
    val errorMsg: String
)

data class Data(
    val category: String,
    val icon: String,
    val id: Int,
    val link: String,
    val name: String,
    val order: Int,
    val visible: Int
)
//Retrofit网络请求
val userServiceApi: UserServiceApi by lazy {
    val retrofit = Retrofit.Builder()
        .client(OkHttpClient.Builder().addInterceptor {
            it.proceed(
                it.request()
            ).apply {
                Log.d("jason", "request:${code()}")
            }
        }.build()).baseUrl("https://www.wanandroid.com/friend/").addConverterFactory(MoshiConverterFactory.create())
        .build()
    retrofit.create(UserServiceApi::class.java)
}

interface UserServiceApi {

    @GET("json")
    //挂起
    suspend fun  User() :User

}
class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text.text="Jack"

        button.also {
            it.setOnClickListener {
                // 协程构建器         主线程
                GlobalScope.launch(Dispatchers.Main) {
                                     //任务调度器    子线程
                    val user=  withContext(Dispatchers.IO){
                          userServiceApi.User()
                    }
                    text.text="address:${user?.data?.get(0)?.category}"
                }
            }
        }
    }
}

到此这个例子结束了,接下来我们一 一解释。

2.协程的挂起与恢复

常规函数基础 *** 作包括:invoke(或call)和return,协程新增了suspend和res
ume:
suspend 一也称为挂起或暂停,用于暂停执行当前协程,并保存所有局部变量;
resume 一用于让已暂停的协程从其暂停处继续执行。

class MainActivity3 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text.text="Jack"

        button.also {
            it.setOnClickListener {
                // 协程构建器         主线程
                GlobalScope.launch(Dispatchers.Main) {
                    //挂起方法  
                    getUser()
                }
            }
        }
    }
            //挂起关键字
    private suspend fun getUser(){
       val  user=get()
        show(user)
    }

    private fun show(user:User){
        text.text="address:${user?.data?.get(0)?.category}"
    }                                    //任务调度器    子线程
    private suspend fun get()=  withContext(Dispatchers.IO){
        userServiceApi.User()
    }
}

通过使用suspend 修改后的代码, 需要注意子方法用suspend 父方法也得加suspend 。还用suspend 关键字只能在协程内或其他挂起函数内使用.

3.挂起与阻塞
class MainActivity4 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text.text="Jack"
        //挂起不会阻塞主线程,而且会把你所去要的任务记录下来并完成
        button.also {
            it.setOnClickListener {
                // 协程构建器         主线程
                GlobalScope.launch(Dispatchers.Main) {
                    //延迟  有挂起的作用
                   delay(10000)
                   Log.d("delay","${Thread.currentThread().name} after delay")
                }
                    //sleep会阻塞主线程,
//                Thread.sleep(10000)
//               Log.d("delay","${Thread.currentThread().name} after sleep")
            }
        }
    }
}

这里通过使用delay,你每点一次按钮都会开一个协程发起一次任务,过10秒后你点了多少下会打印多少次,而且并没有阻塞主线程。而底下的sleep会阻塞主线程,有ANR的危险。

4.协程的基础写法
class MainActivity5 : AppCompatActivity() {
    /**
     * 
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text.text="Jack"
        //这个是协程底部基础函数
        //协程体  也是挂起函数
        val createCoroutine = suspend {
            5  //传入的值
        }.createCoroutine(object : Continuation {
            //协程上下文
            override val context: CoroutineContext = EmptyCoroutineContext

            //协程执行完成的回调
            override fun resumeWith(result: Result) {
                //result 协程执行结果
                Log.i("resumeWith", "$result ")
            }
        })
        //启动协程
        createCoroutine.resume(Unit)
    }
}

5.协程的调度器

所有协程必须在调度器中运行

Dispatchers.Main : Android上的主线程用来处理UI交互和一些轻量级任务

Dispatchers.IO : 非主线程专为磁盘和网络IO进行了

Dispatchers.Default : 非主线程转为CPU密集型任务进行了优化

6.任务泄露

当某个协程任务丢失,无法追踪,会导致内存、CPI-R磁盘等资源浪费,甚至发
送一个无用的网络请求,这种情况称为任务泄漏。
为了能够避免协程泄漏,Kot|in引入了结构化并发机制。

1.结构化并发

使用结构化并发可以做到:
取消任务 当某项任务不再需要时取消它。
追踪任务 :当任务正在执行是,追踪它。                                                                                      发出错误信号:当协程失败时,发出错误信号表明有错误发生。


 

7.CoroutineScope

定义协程必须指定其CoroutineScope,它会跟踪所有协程,同样它还可以取消
由它所启动的所有协程。
常用的相关API有:
GlobalScope,生命周期是process级别的,即使Activity或Fragment已经被销毁,协程仍然在执行。
MainScope,在Activity中使用,可以在onDestroy()中取消协程。
viewModeIScope,只能在ViewM0de|中使用,绑定ViewM0de|的生命周期。
IifecycIeScope,只能在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期。

class MainActivity6 : AppCompatActivity() {
     private var nametext :TextView?=null;
     private var mainScope = MainScope()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //text.text="Jack"
        nametext=findViewById(R.id.text)
        button.apply {
            setOnClickListener {
                mainScope.launch {
                    //User函数用了挂起所以他会自动启用一个子线程请求
                    val user = userServiceApi.User()
                    nametext?.text="address:${user?.data?.get(0)?.category}"
            
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        //取消mainScope的任务
        mainScope.cancel()
      
    }
}

还可以这么写

class MainActivity6 : AppCompatActivity(),CoroutineScope by MainScope() {
     private var nametext :TextView?=null;
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      
        nametext=findViewById(R.id.text)
        button.apply {
            setOnClickListener {            
                launch {
                    //User函数用了挂起所以他会自动启用一个子线程请求
                    val user = userServiceApi.User()
                    nametext?.text = "address:${user?.data?.get(0)?.category}"
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        //取消mainScope的任务
        cancel()
    }
}

这是两种MainScope的两种写法

三.协程上手

这里写一个例子使用:协程+Retrofit+ViewModel+LiveData+DataBinding

在先说明,数据类和Retrofit的封装类和第一个列子使用的使用一个.

先配置DataBinding与依赖

android {
  dataBinding {
        enabled = true
    }
}
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
    
    implementation "androidx.activity:activity-ktx:1.1.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"


    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC-native-mt'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC-native-mt'
class UserRepository{
        //挂起函数  返回数据
        suspend fun getUser():User{
            return userServiceApi.User()
        }
}
class MainViewModel : ViewModel() {

    val userLiveData= MediatorLiveData()

    private val UserRepository=UserRepository()

    fun getUser(){
        //viewModelScope 绑定viewModel的生命周期
          viewModelScope.launch {
              userLiveData.value= UserRepository.getUser()
        }
    }
}


    
        
    

    

        

        
class MainActivity7 : AppCompatActivity() {
    private val mainViewModel:MainViewModel by  viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner=this;
        binding.viewModel=mainViewModel

        text.text="Jack"

        binding.button.setOnClickListener {
            mainViewModel.getUser()
            }
    }
}

演示:

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/788695.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存