kotlin协程-Android实战,android面试八股文

override fun onError(t: Throwable?) {
tv_text.text = “error”
}
})
}

// 使用协程 请求+渲染数据
fun requestData2() {
GlobalScope.launch(Dispatchers.Main) {
try {
tv_text.text = Gson().toJson(testApi.getLatestNews2())
} catch (e: Exception) {
tv_text.text = “error”
}
}
}
}

rxjava2是使用回调的方式渲染数据,这个大家都知道

而协程需要先使用GlobalScope.launch启动一个协程(启动协程的方法很多,请自行查看官方文档),并使用Dispatchers.Main指定协程调度器为主线程(即ui线程), 然后通过 try catch分别处理正常和异常的情况(暂时使用GlobalScope上下文启动协程,下面会介绍一种专门再android中启动协程的方法)

这样看来是不是使用协程可以简化很多代码,使代码看起来更加优雅

我们再来看看多个请求并发和串行的情况

先多添加几个api,方便操作

interface TestApi {
@GET(“api/3/news/latest”)
fun getLatestNews(): Flowable

@GET(“api/3/news/{id}”)
fun getNewsDetail(@Path(“id”) id: Long): Flowable

@GET(“api/4/news/latest”)
suspend fun getLatestNews2(): LatestNews

@GET(“api/3/news/{id}”)
suspend fun getNewsDetail2(@Path(“id”) id: Long): News
}

比如我们先调用getLatestNews()方法请求一系列的新闻列表,然后在调用getNewsDetail请求第一个新闻的详情,代码如下

// 非协程用法
testApi.getLatestNews()
.flatMap {
testApi.getNewsDetail(it.stories!![0].id!!)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : DisposableSubscriber() {
override fun onComplete() {}

override fun onNext(t: News) {
tv_text.text = t.title
}

override fun onError(t: Throwable?) {
tv_text.text = “error”
}
})

// 协程用法
GlobalScope.launch(Dispatchers.Main) {
try {
val lastedNews = testApi.getLatestNews2()
val detail = testApi.getNewsDetail2(lastedNews.stories!![0].id!!)
tv_text.text = detail.title
} catch(e: Exception) {
tv_text.text = “error”
}
}

再比如如果我们想调用getNewsDetail同时请求多个新闻详情数据

// 非协程用法
testApi.getLatestNews()
.flatMap {
Flowable.zip(
testApi.getNewsDetail(it.stories!![0].id!!),
testApi.getNewsDetail(it.stories!![1].id!!),
BiFunction { news1, news2->
listOf(news1, news2)
}
)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : DisposableSubscriber() {
override fun onComplete() {}

override fun onNext(t: List) {
tv_text.text = t[0].title + t[1].title
}

override fun onError(t: Throwable?) {
tv_text.text = “error”
}
})

// 协程的用法
GlobalScope.launch(Dispatchers.Main) {
try {
// 先请求新闻列表
val lastedNews = testApi.getLatestNews2()
// 再使用async 并发请求第一个和第二个新闻的详情
val detail1 = async { testApi.getNewsDetail2(lastedNews.stories!![0].id!!) }
val detail2 = async { testApi.getNewsDetail2(lastedNews.stories!![1].id!!) }
tv_text.text = detail1.await().title + detail2.await().title
} catch(e: Exception) {
tv_text.text = “error”
}
}

可见相对于非协程的写法(代码中使用rxjava2),协程能让你的代码更加简洁、优雅,能更加清晰的描述你第一步想做什么、第二步想做什么等等

room数据库对协程的支持

room数据库在2.1.0开始支持协程, 并且需要导入room-ktx依赖

implementation “androidx.room:room-ktx:2.1.0”

然后在Dao中使用suspend定义挂起函数

@Dao
abstract class UserDao {
@Query(“select * from tab_user”)
abstract suspend fun getAll(): List
}

最后就像上面retrofit2那样使用协程即可

class RoomActivity : AppCompatActivity() {
private var adapter: RoomAdapter? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_room)

}

private fun loadUser() {
GlobalScope.launch(Dispatchers.Main) {
adapter!!.data = AppDataBase.getInstance().userDao().getAll()
}
}

}

这里指介绍room数据库的协程用法,对于room数据库的介绍和其他用法请查看Android Jetpack ROOM数据库用法介绍和android Jetpack ROOM数据库结合其它Library的使用介绍

协程在android里的应用

上面的example都是使用GlobalScope上下文来启动协程, 其实真正在android中一般不建议直接使用GlobalScope,因为使用GlobalScope.launch 时,我们会创建一个顶层协程。虽然它很轻量,但它运行时仍会消耗一些内存资源,如果我们忘记保持对新启动的协程的引用,它还会继续运行,所以我们必须保持所有对GlobalScope.launch启动协程的引用,然后在activity destory(或其它需要cancel)的时候cancel掉所有的协程,否则就会造成内存泄露等一系列问题

比如:

class CoroutineActivity : AppCompatActivity() {
private lateinit var testApi: TestApi
private var job1: Job? = null
private var job2: Job? = null
private var job3: Job? = null

override fun onDestroy() {
super.onDestroy()
job1?.cancel()

你可能感兴趣的:(程序员,架构,移动开发,android)