美文网首页Android深入
[Android kotlin]如何使用Kotlin 协程进行

[Android kotlin]如何使用Kotlin 协程进行

作者: Darren老师 | 来源:发表于2022-06-29 16:34 被阅读0次

    关于这个项目:

    • 如果您想开始使用 Kotlin Coroutines for Android Development 并想掌握它,那么这个项目适合您。
    • 本项目已实现 Android 中 Kotlin Coroutines 的常见用例。
    • 另外,学习为使用 Kotlin 协程的 ViewModel 编写单元测试。
    • 这是您学习 Kotlin Coroutines for Android Development 的一站式解决方案。

    通过该项目中的示例学习 Kotlin Coroutines 的步骤

    • 首先,学习 Kotlin Coroutines 的概念
    • 然后,只需克隆、构建、运行项目并通过示例开始学习 Kotlin 协程。

    这个 Kotlin Coroutines 示例项目将帮助您学习以下 Android 应用程序开发:

    • 什么是 Kotlin 协程?
    • 如何在 Android 应用开发中使用 Kotlin 协程?
    • 关于如何在 Android 中实现 Kotlin 协程的分步指南。
    • 使用 Kotlin Coroutines 在后台执行简单任务。
    • 使用 Kotlin Coroutines 执行系列任务。
    • 使用 Kotlin 协程并行执行任务。
    • 使用 Kotlin 协程并行进行两个网络调用。
    • Kotlin 协程中的作用域是什么?
    • 使用 Kotlin Coroutines 取消后台任务。
    • Kotlin 协程中的异常处理。
    • 使用带有改造的 Kotlin 协程。
    • 将 Kotlin 协程与 Room 数据库结合使用。
    • 将 Kotlin Coroutines 与各种 3rd 方库一起使用。
    • 使用 Kotlin 协程为任务添加超时。
    • 为使用 Kotlin 协程的 ViewModel 编写单元测试。

    用于 Android 开发的 Kotlin 协程示例:

    • 单一网络调用:了解如何使用 Kotlin 协程进行网络调用。这是 Android 应用程序开发中一个非常简单的用例。

      • 活动代码
    package com.mindorks.example.coroutines.learn.retrofit.single
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class SingleNetworkCallActivity : AppCompatActivity() {
    
        private lateinit var viewModel: SingleNetworkCallViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(SingleNetworkCallViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.retrofit.single
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.launch
    
    class SingleNetworkCallViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    val usersFromApi = apiHelper.getUsers()
                    users.postValue(Resource.success(usersFromApi))
                } catch (e: Exception) {
                    users.postValue(Resource.error(e.toString(), null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • 系列网络调用:了解如何使用 Kotlin 协程进行系列网络调用。当您要进行依赖于另一个网络调用的网络调用时,这很有用。

      • 活动代码
    package com.mindorks.example.coroutines.learn.retrofit.series
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class SeriesNetworkCallsActivity : AppCompatActivity() {
    
        private lateinit var viewModel: SeriesNetworkCallsViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(SeriesNetworkCallsViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.retrofit.series
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.launch
    
    class SeriesNetworkCallsViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    val usersFromApi = apiHelper.getUsers()
                    val moreUsersFromApi = apiHelper.getMoreUsers()
                    val allUsersFromApi = mutableListOf<ApiUser>()
                    allUsersFromApi.addAll(usersFromApi)
                    allUsersFromApi.addAll(moreUsersFromApi)
                    users.postValue(Resource.success(allUsersFromApi))
                } catch (e: Exception) {
                    users.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • 并行网络调用:了解如何使用 Kotlin 协程并行进行网络调用。当您想要并行进行彼此独立的网络调用时,这很有用。

      • 活动代码
    package com.mindorks.example.coroutines.learn.retrofit.parallel
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class ParallelNetworkCallsActivity : AppCompatActivity() {
    
        private lateinit var viewModel: ParallelNetworkCallsViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(ParallelNetworkCallsViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.retrofit.parallel
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.async
    import kotlinx.coroutines.coroutineScope
    import kotlinx.coroutines.launch
    
    class ParallelNetworkCallsViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    // coroutineScope is needed, else in case of any network error, it will crash
                    coroutineScope {
                        val usersFromApiDeferred = async { apiHelper.getUsers() }
                        val moreUsersFromApiDeferred = async { apiHelper.getMoreUsers() }
    
                        val usersFromApi = usersFromApiDeferred.await()
                        val moreUsersFromApi = moreUsersFromApiDeferred.await()
    
                        val allUsersFromApi = mutableListOf<ApiUser>()
                        allUsersFromApi.addAll(usersFromApi)
                        allUsersFromApi.addAll(moreUsersFromApi)
    
                        users.postValue(Resource.success(allUsersFromApi))
                    }
                } catch (e: Exception) {
                    users.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • 房间数据库操作:了解如何使用 Kotlin 协程在数据库中获取或插入实体。当您在 Android 应用程序中使用房间数据库时,这很有用。

      • 活动代码
    package com.mindorks.example.coroutines.learn.room
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.local.entity.User
    import com.mindorks.example.coroutines.learn.base.UserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class RoomDBActivity : AppCompatActivity() {
    
        private lateinit var viewModel: RoomDBViewModel
        private lateinit var adapter: UserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                UserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<User>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(RoomDBViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.room
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.local.entity.User
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.launch
    
    class RoomDBViewModel(private val apiHelper: ApiHelper, private val dbHelper: DatabaseHelper) :
        ViewModel() {
    
        private val users = MutableLiveData<Resource<List<User>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    val usersFromDb = dbHelper.getUsers()
                    if (usersFromDb.isEmpty()) {
                        val usersFromApi = apiHelper.getUsers()
                        val usersToInsertInDB = mutableListOf<User>()
    
                        for (apiUser in usersFromApi) {
                            val user = User(
                                apiUser.id,
                                apiUser.name,
                                apiUser.email,
                                apiUser.avatar
                            )
                            usersToInsertInDB.add(user)
                        }
    
                        dbHelper.insertAll(usersToInsertInDB)
    
                        users.postValue(Resource.success(usersToInsertInDB))
    
                    } else {
                        users.postValue(Resource.success(usersFromDb))
                    }
    
    
                } catch (e: Exception) {
                    users.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<User>>> {
            return users
        }
    
    }
    
    • 长时间运行的任务:了解如何使用 Kotlin 协程执行长时间运行的任务。如果您想使用 Kotlin 协程在后台线程中执行任何任务,那么这很有用。

      • 活动代码
    package com.mindorks.example.coroutines.learn.task.onetask
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_long_running_task.*
    import kotlinx.android.synthetic.main.activity_recycler_view.progressBar
    
    class LongRunningTaskActivity : AppCompatActivity() {
    
        private lateinit var viewModel: LongRunningTaskViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_long_running_task)
            setupViewModel()
            setupLongRunningTask()
        }
    
        private fun setupLongRunningTask() {
            viewModel.getStatus().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        textView.text = it.data
                        textView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        textView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
            viewModel.startLongRunningTask()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(LongRunningTaskViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.task.onetask
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
    
    class LongRunningTaskViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val status = MutableLiveData<Resource<String>>()
    
        fun startLongRunningTask() {
            viewModelScope.launch {
                status.postValue(Resource.loading(null))
                try {
                    // do a long running task
                    doLongRunningTask()
                    status.postValue(Resource.success("Task Completed"))
                } catch (e: Exception) {
                    status.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getStatus(): LiveData<Resource<String>> {
            return status
        }
    
        private suspend fun doLongRunningTask() {
            withContext(Dispatchers.Default) {
                // your code for doing a long running task
                // Added delay to simulate
                delay(5000)
            }
        }
    
    }
    
    • 两个长时间运行的任务:了解如何使用 Kotlin 协程并行运行两个长时间运行的任务。

      • 活动代码
    package com.mindorks.example.coroutines.learn.task.twotasks
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_long_running_task.*
    import kotlinx.android.synthetic.main.activity_recycler_view.progressBar
    
    class TwoLongRunningTasksActivity : AppCompatActivity() {
    
        private lateinit var viewModel: TwoLongRunningTasksViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_long_running_task)
            setupViewModel()
            setupLongRunningTask()
        }
    
        private fun setupLongRunningTask() {
            viewModel.getStatus().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        textView.text = it.data
                        textView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        textView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
            viewModel.startLongRunningTask()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(TwoLongRunningTasksViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.task.twotasks
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.async
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    
    class TwoLongRunningTasksViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val status = MutableLiveData<Resource<String>>()
    
        fun startLongRunningTask() {
            viewModelScope.launch {
                status.postValue(Resource.loading(null))
                try {
                    // do long running tasks
                    val resultOneDeferred = async { doLongRunningTaskOne() }
                    val resultTwoDeferred = async { doLongRunningTaskTwo() }
                    val combinedResult = resultOneDeferred.await() + resultTwoDeferred.await()
    
                    status.postValue(Resource.success("Task Completed : $combinedResult"))
                } catch (e: Exception) {
                    status.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getStatus(): LiveData<Resource<String>> {
            return status
        }
    
        private suspend fun doLongRunningTaskOne(): String {
            delay(5000)
            return "One"
        }
    
        private suspend fun doLongRunningTaskTwo(): String {
            delay(5000)
            return "Two"
        }
    
    }
    
    • 超时:了解如何使用 Kotlin 协程为任务添加超时。如果您想为 Android 中的任何后台任务添加超时,这将非常有用。

      • 活动代码
    package com.mindorks.example.coroutines.learn.timeout
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class TimeoutActivity : AppCompatActivity() {
    
        private lateinit var viewModel: TimeoutViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(TimeoutViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.timeout
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.TimeoutCancellationException
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withTimeout
    
    class TimeoutViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    withTimeout(100) {
                        val usersFromApi = apiHelper.getUsers()
                        users.postValue(Resource.success(usersFromApi))
                    }
                } catch (e: TimeoutCancellationException) {
                    users.postValue(Resource.error("TimeoutCancellationException", null))
                } catch (e: Exception) {
                    users.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • Try-Catch 错误处理:了解如何使用 Try-Catch 处理 Kotlin 协程中的错误。

      • 活动代码
    package com.mindorks.example.coroutines.learn.errorhandling.trycatch
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class TryCatchActivity : AppCompatActivity() {
    
        private lateinit var viewModel: TryCatchViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(TryCatchViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.errorhandling.trycatch
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.launch
    
    class TryCatchViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    val usersFromApi = apiHelper.getUsers()
                    users.postValue(Resource.success(usersFromApi))
                } catch (e: Exception) {
                    users.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • CoroutineExceptionHandler:了解如何使用 CoroutineExceptionHandler 处理 Kotlin 协程中的错误。

      • 活动代码
    package com.mindorks.example.coroutines.learn.errorhandling.exceptionhandler
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class ExceptionHandlerActivity : AppCompatActivity() {
    
        private lateinit var viewModel: ExceptionHandlerViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
    
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(ExceptionHandlerViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.errorhandling.exceptionhandler
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.CoroutineExceptionHandler
    import kotlinx.coroutines.launch
    
    class ExceptionHandlerViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
            users.postValue(Resource.error("Something Went Wrong", null))
        }
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch(exceptionHandler) {
                users.postValue(Resource.loading(null))
                val usersFromApi = apiHelper.getUsers()
                users.postValue(Resource.success(usersFromApi))
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • 忽略错误并继续:了解如何使用supervisorScope忽略任务的错误并继续执行其他任务。换句话说,如果有两个以上的子作业在一个主管下并行运行,一个子作业失败了,我们可以继续另一个。

      • 活动代码
    package com.mindorks.example.coroutines.learn.errorhandling.supervisor
    
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.ViewModelProviders
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.mindorks.example.coroutines.R
    import com.mindorks.example.coroutines.data.api.ApiHelperImpl
    import com.mindorks.example.coroutines.data.api.RetrofitBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseBuilder
    import com.mindorks.example.coroutines.data.local.DatabaseHelperImpl
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.learn.base.ApiUserAdapter
    import com.mindorks.example.coroutines.utils.Status
    import com.mindorks.example.coroutines.utils.ViewModelFactory
    import kotlinx.android.synthetic.main.activity_recycler_view.*
    
    class IgnoreErrorAndContinueActivity : AppCompatActivity() {
    
        private lateinit var viewModel: IgnoreErrorAndContinueViewModel
        private lateinit var adapter: ApiUserAdapter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_recycler_view)
            setupUI()
            setupViewModel()
            setupObserver()
        }
    
        private fun setupUI() {
            recyclerView.layoutManager = LinearLayoutManager(this)
            adapter =
                ApiUserAdapter(
                    arrayListOf()
                )
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
            recyclerView.adapter = adapter
        }
    
        private fun setupObserver() {
            viewModel.getUsers().observe(this, {
                when (it.status) {
                    Status.SUCCESS -> {
                        progressBar.visibility = View.GONE
                        it.data?.let { users -> renderList(users) }
                        recyclerView.visibility = View.VISIBLE
                    }
                    Status.LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                    Status.ERROR -> {
                        //Handle Error
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            })
        }
    
        private fun renderList(users: List<ApiUser>) {
            adapter.addData(users)
            adapter.notifyDataSetChanged()
        }
    
        private fun setupViewModel() {
            viewModel = ViewModelProviders.of(
                this,
                ViewModelFactory(
                    ApiHelperImpl(RetrofitBuilder.apiService),
                    DatabaseHelperImpl(DatabaseBuilder.getInstance(applicationContext))
                )
            ).get(IgnoreErrorAndContinueViewModel::class.java)
        }
    }
    
    • 视图模型代码
    package com.mindorks.example.coroutines.learn.errorhandling.supervisor
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import kotlinx.coroutines.async
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.supervisorScope
    
    class IgnoreErrorAndContinueViewModel(
        private val apiHelper: ApiHelper,
        private val dbHelper: DatabaseHelper
    ) : ViewModel() {
    
        private val users = MutableLiveData<Resource<List<ApiUser>>>()
    
        init {
            fetchUsers()
        }
    
        private fun fetchUsers() {
            viewModelScope.launch {
                users.postValue(Resource.loading(null))
                try {
                    // supervisorScope is needed, so that we can ignore error and continue
                    // here, more than two child jobs are running in parallel under a supervisor, one child job gets failed, we can continue with other.
                    supervisorScope {
                        val usersFromApiDeferred = async { apiHelper.getUsersWithError() }
                        val moreUsersFromApiDeferred = async { apiHelper.getMoreUsers() }
    
                        val usersFromApi = try {
                            usersFromApiDeferred.await()
                        } catch (e: Exception) {
                            emptyList()
                        }
    
                        val moreUsersFromApi = try {
                            moreUsersFromApiDeferred.await()
                        } catch (e: Exception) {
                            emptyList()
                        }
    
                        val allUsersFromApi = mutableListOf<ApiUser>()
                        allUsersFromApi.addAll(usersFromApi)
                        allUsersFromApi.addAll(moreUsersFromApi)
    
                        users.postValue(Resource.success(allUsersFromApi))
                    }
                } catch (e: Exception) {
                    users.postValue(Resource.error("Something Went Wrong", null))
                }
            }
        }
    
        fun getUsers(): LiveData<Resource<List<ApiUser>>> {
            return users
        }
    
    }
    
    • 单元测试:了解如何为使用 Kotlin 协程的 ViewModel 编写单元测试。

      • 查看模型测试代码
    package com.mindorks.example.coroutines.learn.retrofit.single
    
    import androidx.arch.core.executor.testing.InstantTaskExecutorRule
    import androidx.lifecycle.Observer
    import com.mindorks.example.coroutines.data.api.ApiHelper
    import com.mindorks.example.coroutines.data.local.DatabaseHelper
    import com.mindorks.example.coroutines.data.model.ApiUser
    import com.mindorks.example.coroutines.utils.Resource
    import com.mindorks.example.coroutines.utils.TestCoroutineRule
    import kotlinx.coroutines.ExperimentalCoroutinesApi
    import org.junit.After
    import org.junit.Before
    import org.junit.Rule
    import org.junit.Test
    import org.junit.rules.TestRule
    import org.junit.runner.RunWith
    import org.mockito.Mock
    import org.mockito.Mockito.*
    import org.mockito.junit.MockitoJUnitRunner
    
    @ExperimentalCoroutinesApi
    @RunWith(MockitoJUnitRunner::class)
    class SingleNetworkCallViewModelTest {
    
        @get:Rule
        val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
    
        @get:Rule
        val testCoroutineRule = TestCoroutineRule()
    
        @Mock
        private lateinit var apiHelper: ApiHelper
    
        @Mock
        private lateinit var databaseHelper: DatabaseHelper
    
        @Mock
        private lateinit var apiUsersObserver: Observer<Resource<List<ApiUser>>>
    
        @Before
        fun setUp() {
            // do something if required
        }
    
        @Test
        fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
            testCoroutineRule.runBlockingTest {
                doReturn(emptyList<ApiUser>())
                    .`when`(apiHelper)
                    .getUsers()
                val viewModel = SingleNetworkCallViewModel(apiHelper, databaseHelper)
                viewModel.getUsers().observeForever(apiUsersObserver)
                verify(apiHelper).getUsers()
                verify(apiUsersObserver).onChanged(Resource.success(emptyList()))
                viewModel.getUsers().removeObserver(apiUsersObserver)
            }
        }
    
        @Test
        fun givenServerResponseError_whenFetch_shouldReturnError() {
            testCoroutineRule.runBlockingTest {
                val errorMessage = "Error Message For You"
                doThrow(RuntimeException(errorMessage))
                    .`when`(apiHelper)
                    .getUsers()
                val viewModel = SingleNetworkCallViewModel(apiHelper, databaseHelper)
                viewModel.getUsers().observeForever(apiUsersObserver)
                verify(apiHelper).getUsers()
                verify(apiUsersObserver).onChanged(
                    Resource.error(
                        RuntimeException(errorMessage).toString(),
                        null
                    )
                )
                viewModel.getUsers().removeObserver(apiUsersObserver)
            }
        }
    
        @After
        fun tearDown() {
            // do something if required
        }
    
    }
    

    从这个项目学习 Kotlin Coroutines for Android 时有用的参考资料

    作者:amitshekhariitbhu
    链接:https://github.com/MindorksOpenSource/Kotlin-Coroutines-Android-Examples

    相关文章

      网友评论

        本文标题:[Android kotlin]如何使用Kotlin 协程进行

        本文链接:https://www.haomeiwen.com/subject/wjlxbrtx.html