在Activity/Fragment中,如何从ViewModel协程操作中get/wait取return值?
In Activity/Fragment, How to get/wait the return value from ViewModel coroutines operation?
按照Google(link), I try to refactor my code to ViewModel + coroutines. My question is, instead of just insert the data (original code)的codelab demo,我想等待插入操作的结果,如果插入成功应该return id,然后根据结果做某事。那怎么办呢?
目前,我将一个方法作为回调发送到 ViewModel 插入方法。当然,观察 ViewModel 是另一种选择。但是有没有更好的解决办法呢?
我当前的代码:
事件活动:
viewModel.insert(Event("name"), {
if (it == -1L) {
Log.i("insert", "failure")
} else {
Log.i("insert", "success: $it")
}
})
事件视图模型:
private val mEventDao: EventDao = AppDatabase.getDatabase(application).eventDao()
private val mJob = Job()
private val mScope = CoroutineScope(Dispatchers.Main + mJob)
fun insert(event: Event, callback: (id: Long) -> Unit) {
mScope.launch(Dispatchers.IO) {
val result =
try {
// just for testing delay situation
delay(5000)
val id = mEventDao.insertEvent(event)
id
} catch (e: Exception) {
-1L
}
withContext(Dispatchers.Main) {
callback(result)
}
}
}
事件道:
@Dao
interface EventDao {
fun insertEvent(event: Event): Long
}
suspend fun insert(data: String): String = suspendCoroutine { cont ->
//put logic here
cont.resume("Done")
//if error use this
cont.resumeWithException(Exception("Error"))
}
我可以使用 return 删除此函数中的回调并等待 return
fun insertData(){
GlobalScope.launch {
val status = insert("This is Data!")
if( status == "Done"){
}else{
}
}
}
@rofie-sagara 完全正确。您可以将 suspend
添加到您的模型 insert
方法中。但是没有必要使用 suspendCoroutine
语法。你可以在你的模型中写
class ViewModel {
private val job = Job()
private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + job)
private val dao = Dao()
suspend fun insert(event: Event): Long =
withContext(scope.coroutineContext) {
try {
// just for testing delay situation
delay(5000)
dao.insert(event)
} catch (e: Exception) {
-1L
}
}
}
您可以将 LiveData
对象添加到 EventViewModel
,插入完成后更新它并在 Activity
中订阅它:
class EventViewModel : ViewModel() {
//...
var insertionId = MutableLiveData<Long>()
fun insert(event: String) {
mScope.launch(Dispatchers.IO) {
val result =
try {
// just for testing delay situation
delay(5000)
val id = mEventDao.insertEvent(event)
id
} catch (e: Exception) {
-1L
}
insertionId.postValue(result)
}
}
}
并在 EventActivity
中订阅:
class EventActivity : AppCompatActivity() {
lateinit var viewModel: EventViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(EventViewModel::class.java)
viewModel.insertionId.observe(this, android.arch.lifecycle.Observer { id ->
// Use `id` for example to update UI.
})
// ...
viewModel.insert(Event("name"))
}
}
But is there any better solution?
我觉得有。
在androidx.lifecycle.*:2.2.0 alpha01
中
liveData { . . . }
, emit()
个函数可用。
您可以像这样重写 viewModel
中的代码。
fun insertData(event: String) = liveData {
val id = mEventDao.insertEvent(event)
emit(id)
}
并在你的 activity 中观察它。
viewModel.insertData("YourEvent").observe(this) {
updateUi(id)
}
别忘了将您的 Dao
方法更改为 suspend
@Dao
interface EventDao {
suspend fun insertEvent(event: Event): Long
}
BTW此时最新版本的直播数据是2.2.0-rc03
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc03"
您可以在 android 文档中看到更好的实现:Use coroutines with LiveData
按照Google(link), I try to refactor my code to ViewModel + coroutines. My question is, instead of just insert the data (original code)的codelab demo,我想等待插入操作的结果,如果插入成功应该return id,然后根据结果做某事。那怎么办呢?
目前,我将一个方法作为回调发送到 ViewModel 插入方法。当然,观察 ViewModel 是另一种选择。但是有没有更好的解决办法呢?
我当前的代码:
事件活动:
viewModel.insert(Event("name"), {
if (it == -1L) {
Log.i("insert", "failure")
} else {
Log.i("insert", "success: $it")
}
})
事件视图模型:
private val mEventDao: EventDao = AppDatabase.getDatabase(application).eventDao()
private val mJob = Job()
private val mScope = CoroutineScope(Dispatchers.Main + mJob)
fun insert(event: Event, callback: (id: Long) -> Unit) {
mScope.launch(Dispatchers.IO) {
val result =
try {
// just for testing delay situation
delay(5000)
val id = mEventDao.insertEvent(event)
id
} catch (e: Exception) {
-1L
}
withContext(Dispatchers.Main) {
callback(result)
}
}
}
事件道:
@Dao
interface EventDao {
fun insertEvent(event: Event): Long
}
suspend fun insert(data: String): String = suspendCoroutine { cont ->
//put logic here
cont.resume("Done")
//if error use this
cont.resumeWithException(Exception("Error"))
}
我可以使用 return 删除此函数中的回调并等待 return
fun insertData(){
GlobalScope.launch {
val status = insert("This is Data!")
if( status == "Done"){
}else{
}
}
}
@rofie-sagara 完全正确。您可以将 suspend
添加到您的模型 insert
方法中。但是没有必要使用 suspendCoroutine
语法。你可以在你的模型中写
class ViewModel {
private val job = Job()
private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + job)
private val dao = Dao()
suspend fun insert(event: Event): Long =
withContext(scope.coroutineContext) {
try {
// just for testing delay situation
delay(5000)
dao.insert(event)
} catch (e: Exception) {
-1L
}
}
}
您可以将 LiveData
对象添加到 EventViewModel
,插入完成后更新它并在 Activity
中订阅它:
class EventViewModel : ViewModel() {
//...
var insertionId = MutableLiveData<Long>()
fun insert(event: String) {
mScope.launch(Dispatchers.IO) {
val result =
try {
// just for testing delay situation
delay(5000)
val id = mEventDao.insertEvent(event)
id
} catch (e: Exception) {
-1L
}
insertionId.postValue(result)
}
}
}
并在 EventActivity
中订阅:
class EventActivity : AppCompatActivity() {
lateinit var viewModel: EventViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(EventViewModel::class.java)
viewModel.insertionId.observe(this, android.arch.lifecycle.Observer { id ->
// Use `id` for example to update UI.
})
// ...
viewModel.insert(Event("name"))
}
}
But is there any better solution?
我觉得有。
在androidx.lifecycle.*:2.2.0 alpha01
中
liveData { . . . }
, emit()
个函数可用。
您可以像这样重写 viewModel
中的代码。
fun insertData(event: String) = liveData {
val id = mEventDao.insertEvent(event)
emit(id)
}
并在你的 activity 中观察它。
viewModel.insertData("YourEvent").observe(this) {
updateUi(id)
}
别忘了将您的 Dao
方法更改为 suspend
@Dao
interface EventDao {
suspend fun insertEvent(event: Event): Long
}
BTW此时最新版本的直播数据是2.2.0-rc03
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc03"
您可以在 android 文档中看到更好的实现:Use coroutines with LiveData