英文:
Run CountDownTimer inside CoroutineWorker
问题
I am developing a registration form with Kotlin in Android and after several failed attempts I need to block the form for a while, be it an hour or 30 minutes, and let the service run in the background so the app closes or gets destroyed.
After consulting and reading the Android documentation, I am solving this problem with WorkManager by extending the CoroutineWorker() class, but I find the following problem that does not let me proceed:
无法在线程Thread[DefaultDispatcher-worker-1,5,main]内创建处理程序,尚未调用Looper.prepare()
调用CoroutineWorker的ViewModel
@HiltViewModel
class RegisterViewModel @Inject constructor(
private val registerUseCase: RegisterUseCase
) : ViewModel() {
fun makeApiPost(
context: Context,
card: String,
.... : ...
) {
viewModelScope.launch {
val retroInstance = RetroInstance.getRetroInstanceAuth(context).create(
RetroService::class.java
)
val call = retroInstance.getGiftCardBalance(
CardBalanceRequest(card, ..., ...)
)
call.enqueue(object : Callback<BalanceResponse> {
override fun onResponse(
call: Call<BalanceResponse>,
response: Response<AppBalanceResponse>
) {
if (response.isSuccessful) {
val destination = response.body()
destination?.let {
_cardBalance.postValue(response.body()!!.payload.data)
}
} else {
val workManager = WorkManager.getInstance(ExtendedApp.myContext)
workManager.enqueue(OneTimeWorkRequest.from(WorkerActivate::class.java))
_cardBalance.postValue(null)
}
}
override fun onFailure(call: Call<CardBalanceResponse>, t: Throwable) {
t.printStackTrace()
_cardBalance.postValue(null)
call.request()
}
})
}
}
}
后台处理
class WorkerActivate(context: Context, workerParameters: WorkerParameters) : CoroutineWorker(
context, workerParameters
)
{
lateinit var countDownTimer: CountDownTimer
override suspend fun doWork(): Result {
val minutesTimer: Long = (60 * 1000 * 30)
countDownTimer = object : CountDownTimer(minutesTimer, 1000){
override fun onTick(millisUntilFinished: Long) {
Log.d("card", "Counting Down: ${millisUntilFinished/1000}")
}
override fun onFinish() {
Log.i("card", "countDownTimer de 15 segundos ok")
}
}
return Result.success()
}
}
英文:
I am developing a registration form with kotlin in android and after several failed attempts I need to block the form for a while, be it an hour or 30 minutes and that the service runs in the background so the app closes or destroys
After consulting and reading the android documentation, I am solving this problem with workmanager by extending the CoroutineWorker() class, but I find the following problem that does not let me proceed:
Can't create handler inside thread Thread[DefaultDispatcher-worker-1,5,main] that has not called Looper.prepare()
ViewModel that invokes the CoroutineWorker
@HiltViewModel
class RegisterViewModel @Inject constructor(
private val registerUseCase: RegisterUseCase
) : ViewModel() {
fun makeApiPost(
context: Context,
card: String,
.... : ...
)
{
viewModelScope.launch {
val retroInstance = RetroInstance.getRetroInstanceAuth(context).create(
RetroService::class.java
)
val call = retroInstance.getGiftCardBalance(
CardBalanceRequest(card, ..., ...)
)
call.enqueue(object : Callback<BalanceResponse> {
override fun onResponse(
call: Call<BalanceResponse>,
response: Response<AppBalanceResponse>
) {
if (response.isSuccessful) {
val destination = response.body()
destination?.let {
_cardBalance.postValue(response.body()!!.payload.data)
}
} else {
val workManager = WorkManager.getInstance(ExtendedApp.myContext)
workManager.enqueue(OneTimeWorkRequest.from(WorkerActivate::class.java))
_cardBalance.postValue(null)
}
}
override fun onFailure(call: Call<CardBalanceResponse>, t: Throwable) {
t.printStackTrace()
_cardBalance.postValue(null)
call.request()
}
})
}
}
}
Background process
class WorkerActivate(context: Context, workerParameters: WorkerParameters) : CoroutineWorker(
context, workerParameters
)
{
lateinit var countDownTimer: CountDownTimer
override suspend fun doWork(): Result {
val minutesTimer: Long = (60 * 1000 * 30)
countDownTimer = object : CountDownTimer(minutesTimer, 1000){
override fun onTick(millisUntilFinished: Long) {
Log.d("card", "Counting Down: ${millisUntilFinished/1000}")
}
override fun onFinish() {
Log.i("card", "countDownTimer de 15 segundos ok")
}
}
return Result.success()
}
}
答案1
得分: 2
在你的代码中,第13行有问题。WorkManager 只能从主线程启动,这就是你出现这个错误的原因。
viewModelScope.launch {
<<< 这个区域是子线程 >>>
}
// 尝试这样做...
withContext(Dispatchers.Main) {
val workManager = WorkManager.getInstance(ExtendedApp.myContext)
workManager.enqueue(OneTimeWorkRequest.from(WorkerActivate::class.java))
}
英文:
in your code 13 line is the problem. WorkManager can only be started from the Main thread and that`s the reason you are getting this error.
viewModelScope.launch {
<<< This area is sub Thread >>>
}
// Try this...
withContext(Dispatchers.Main) {
val workManager = WorkManager.getInstance(ExtendedApp.myContext)
workManager.enqueue(OneTimeWorkRequest.from(WorkerActivate::class.java))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论