Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to use Coroutines runBlocking() inside onDataSetChanged of RemoteViewsFactory?

I am trying to fetch some latest movie images and display them in the Android app widgets, As per the requirement, the widget should refresh every hour to get the latest movies images.

Since I am using coroutines throughout the app, I need to use the existing retrofit services with suspend function to get the data.

As per the Android documentation, it's safe to do heavy operations inside onDataSetChanged of RemoteViewsFactory So I am using this method itself to fetch data from service instead of WorkManger as it has its own drawbacks. See here (https://commonsware.com/blog/2018/11/24/workmanager-app-widgets-side-effects.html)

Called when notifyDataSetChanged() is triggered on the remote adapter. This allows a RemoteViewsFactory to respond to data changes by updating any internal references. Note: expensive tasks can be safely performed synchronously within this method. In the interim, the old data will be displayed within the widget.

In the Below code snippet, I am using runBlocking() to make a blocking retrofit call... Otherwise get view will be triggered before the data is fetched. Is this safe?

Note:Also the adapter is noticed in the onUpdate method of MyWidgetProvider every hour

override fun onUpdate(
    context: Context,
    appWidgetManager: AppWidgetManager,
    appWidgetIds: IntArray
) {
   ....
   appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.listViewMovies)
   .....
 }

Code Sample: MyRemoteViewsFactory : RemoteViewsFactory {

override fun onDataSetChanged() {
  try {
     fetchFromServer()
  } catch (e: Exception) {
    clear()      
  }
}

private fun fetchFromServer() = runBlocking {
 job = launch {
     service.getMovieList?.also {
       data = it
     }
   }
}

private fun clear(){
    job?.cancel()
    job = null
    data = null
    log("Cleared")
}

 override fun onDestroy() {
    clear()
 }

Console log shows onDestory() s being called... but in different binder.

 onCreate _main
 1 _Binder:11940_5
 2 _Binder:11940_5
 3 _Binder:11940_5
 4 _Binder:11940_5
 5 _Binder:11940_5
 6 _Binder:11940_5
 7 _Binder:11940_5
 8 _Binder:11940_5
 9 _Binder:11940_5
 10 _Binder:11940_5

 11 _Binder:11940_5
 onDestroy _Binder:11940_2
 Clear called _Binder:11940_2

So My queries:

  1. Is it safe to use runBlocking inside onDataSetChanged Method?
  2. Is it safe to create nested coroutine inside runBlocking to cancel the Coroutines? is there any better way?
  3. Is it safe to fetch the data from the network inside onDataSetChanged().
  4. I can't make async call inside onDataSetChanged() .. So is there anyway we can handle without using runBlocking()

NOTE: This question specially For Android App Widget RemoteViewsFactory onDataSetChanged() implementation.

like image 383
Jess Avatar asked Oct 26 '25 08:10

Jess


1 Answers

1- Is it safe to use runBlocking inside onDataSetChanged Method?

3- Is it safe to fetch the data from the network inside onDataSetChanged().

The documentation specifically says that "expensive tasks can be safely performed synchronously within this method. In the interim, the old data will be displayed within the widget."

So the answer to questions 1 and 3 is yes, it's ok to use runBlocking there and to make a network call.

2- Is it safe to create nested coroutine inside runBlocking to cancel the Coroutines? is there any better way?

There might not be a real reason to do that, because runBlocking blocks the thread until the child coroutines are done, so you wouldn't give control back to the caller anyway until the work is finished. See next question.

4- I can't make async call inside onDataSetChanged() .. So is there anyway we can handle without using runBlocking()

If you want to launch coroutines that you can later cancel (in case of concurrent use of onDataSetChanged and clear - which is yet to be confirmed), you should instead declare a CoroutineScope (as a property of your class) and launch coroutines using that scope instead of using runBlocking, and then cancel the scope when/where appropriate.

like image 184
Joffrey Avatar answered Oct 28 '25 22:10

Joffrey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!