android java协程,Android开发中使用协程(代码实战)

本文介绍了如何使用Kotlin协程配合Room库进行SQLite数据库的操作,包括创建数据库、定义DAO接口以及数据模型。同时,通过viewModelScope启动协程确保了UI线程的安全。此外,针对快速点击可能导致的并发问题,提出了三种解决方案:取消前一个任务、任务排队以及复用已存在的任务。这些方法有助于避免并发冲突,确保应用的稳定运行。
摘要由CSDN通过智能技术生成

使用协程完成本地数据库SQLite操作

封装dao

package com.example.kotlin01.database

import android.app.Application

import androidx.room.Database

import androidx.room.Room

import androidx.room.RoomDatabase

import com.example.kotlin01.dao.UserDao

import com.example.kotlin01.model.User

/**

* 创建数据库,并根据实体类创建表

*/

@Database(entities = arrayOf(User::class), version = 1)

abstract class AppDatabase : RoomDatabase() {

abstract fun getUserDao(): UserDao

/**

* object关键字通过对象声明来实现Java 中的单实例模式,与普通类不同,对象声明在定义的时候就被创建,

* 无需构造方法。Kotlin中的对象声明被编译成了通过静态字段持有它的单实例的类。

*/

object AppDataBaseSingleClass {

/**

* 以单实例的形式初始化数据 并对外提供 AppDataBase实例

* @param context

* @return

*/

open fun getInstance(application: Application): AppDatabase? {

val db = Room.databaseBuilder(

application.applicationContext,

AppDatabase::class.java, "user-info"

).build()

return db

}

}

}

package com.example.kotlin01.dao

import androidx.room.Dao

import androidx.room.Delete

import androidx.room.Insert

import androidx.room.Query

import com.example.kotlin01.model.User

/**

* dao层

*/

@Dao

interface UserDao {

// 因为这个方法被标记为了 suspend,Room 将会在保证主线程安全的前提下使用自己的调度器来运行这个查询

@Query("SELECT * FROM user")

suspend fun queryUser(): List

// 因为这个方法被标记为了 suspend,Room 将会在保证主线程安全的前提下使用自己的调度器来运行这个查询

@Insert

suspend fun addUser(user: User)

//因为这个方法被标记为了 suspend,Room 将会在保证主线程安全的前提下使用自己的调度器来运行这个查询

@Delete

suspend fun deleteUser(user: User)

}

package com.example.kotlin01.model

import androidx.room.ColumnInfo

import androidx.room.Entity

import androidx.room.PrimaryKey

//表名为user

@Entity(tableName = "user")

data class User(

@PrimaryKey val id: Long,

@ColumnInfo(name = "username") val username: String?,

@ColumnInfo(name = "sex") val sex: String?

)

使用viewModelScope+协程

package com.example.kotlin01.viewmodel

import android.app.Application

import android.util.Log

import androidx.lifecycle.AndroidViewModel

import androidx.lifecycle.MutableLiveData

import androidx.lifecycle.viewModelScope

import com.example.kotlin01.database.AppDatabase

import com.example.kotlin01.model.User

import com.example.kotlin01.repository.UserRepository

import kotlinx.coroutines.launch

//加val就是作为成员变量(val repository: UserRepository)

class UserViewModel constructor(application: Application) : AndroidViewModel(application) {

val repository: UserRepository

init {

repository = UserRepository(

AppDatabase.AppDataBaseSingleClass.getInstance(application)!!

.getUserDao()

)

}

val userLiveData: MutableLiveData> = MutableLiveData()

fun queryUser() {

suspend 和 resume 使得这个数据库请求是主线程安全的,所以 ViewModel 不需要关心线程安全问题

viewModelScope.launch {

userLiveData.value = repository.queryUser()

}

}

fun addUser(user: User) {

suspend 和 resume 使得这个数据库请求是主线程安全的,所以 ViewModel 不需要关心线程安全问题

viewModelScope.launch {

val result = repository.addUser(user)

Log.e("UserViewModel", "addUser: " + result)

}

}

fun deleteUser(user: User) {

suspend 和 resume 使得这个数据库请求是主线程安全的,所以 ViewModel 不需要关心线程安全问题

viewModelScope.launch {

val result = repository.deleteUser(user)

Log.e("UserViewModel", "addUser: " + result)

}

}

}

以上我们通过viewModelScope启动协程,在数据库的dao方法中我们使用supend修饰符修饰,表示当前是一个挂起函数,我们不需要额外的Dispatcher线程,因为room内部帮忙调度了线程。因此我们直接使用supend修饰即可,表示当前是一个挂起函数。执行SQL的时候协程会挂起,当执行完毕返回结果的时候,协程会恢复。

解决快速点击问题

当我们连续快速点击的时候,会启动很多个协程,这样会可能造成,后面启动的协程执行结果比前面启动的快。这样的情况,我们可以通过禁用按钮的形式来解决。当协程还没有返回结果的时候,可以禁用按钮。除此之外,还可以通过协程自身的api解决。

并发模式

解决以上的问题,我们可以通过控制协程运行和不运行来解决

(1)启动更多协程之前取消之前的任务

(2)让下一个协程任务排队,等待上一个执行完成

(3)如果已经有一个任务正在执行,返回该任务,而不是继续执行

方案 1: 取消之前的任务

val controllerRuuner = ControlledRunner>()

// 方案 1: 取消之前的任务

// 对于排序和过滤的情况,新请求进来,取消上一个,这样的方案是很适合的。

suspend fun queryUser(): List {

// 在开启新的排序之前,先取消上一个排序任务

return controllerRuuner.cancelPreviousThenRun {

userDao.queryUser()

}

}

方案 2: 让下一个任务排队等待

val singleRunner = SingleRunner()

suspend fun queryUser(): List {

// 开始新的任务之前,等待之前的排序任务完成

return singleRunner.afterPrevious {

userDao.queryUser()

}

}

方案 3: 复用前一个任务

var controlledRunner = ControlledRunner>()

suspend fun queryUser(): List {

// 如果已经有一个正在运行的请求,那么就返回它。如果没有的话,开启一个新的请求。

return controlledRunner.joinPreviousOrRun {

userDao.queryUser()

}

}

源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值