Android - How to access Room database from widget
M

8

9

i wanted to access Room DB inside my widget class to update the widget according to the stored data.

the problem here is the ViewModelProvider require a fragment or activity to work with..

ViewModelProviders.of(fragment).get(YourViewModel.class);

which is not available in the app widget class so how to do it?

Mullinax answered 22/8, 2018 at 19:31 Comment(0)
M
4

So answering my self here..

the only way that worked is to use the context to get the DB instance from the abstract class and run the DAO queries directly in background thread without the ViewModelProviders class

    new AsyncTask<Context, Void, List<Data>>(){
        @Override
        protected List<Quote> doInBackground(Context... context) {

            List<Data> List=null;
                AppDatabase db = AppDatabase.getDatabase(context[0]);
                List = db.DataModel().getData();


            return List;
        }

        @Override
        protected void onPostExecute(List<Quote> List) {
            super.onPostExecute(List);

            if(List != null){

                final Random random=new Random();
                for (int appWidgetId : appWidgetIds) {
                    updateAppWidget(context, appWidgetManager, appWidgetId, quoteList.get(random.nextInt(List.size())));
                }

            }

        }

    }.execute(context);

}
Mullinax answered 22/8, 2018 at 19:46 Comment(0)
B
7

I know it has been a while, but I just came across this issue and wanted to add something useful following the inspiration of fellow member Aaron-dunigan-atlee.

Basically showing where the database and Dao object go... and more importantly, the onDataSetChanged() method that has the call to the Room query. The example is for a Notetaking the app to illustrate, and as it was commented above, no need of AsyncTasks anywhere.

public class MyWidgetRemoteViewsService extends RemoteViewsService
{
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent)
    {
        return new MyRemoteViewsFactory(this.getApplicationContext(), intent);
    }

    class MyRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
    {
        private NotesDao notesDao;
        private List<Note> allNotes;

        MyRemoteViewsFactory(Context context, Intent intent)
        {
            MyDb db;
            db = MyDatabase.getDatabase(context);
            notesDao = db.notesDao();
        }

        @Override
        public void onCreate() { }

        @Override
        public void onDataSetChanged()
        {
            allNotes = notesDao.getAllNotes();
        }

        @Override
            public int getCount()
            { ...

sasa

Bedouin answered 19/12, 2019 at 11:8 Comment(0)
O
6

If you use a RemoteViewsService to populate your widget UI, then you can avoid using an AsyncTask by including a DAO query in its onDataSetChanged() method, rather than in the AppWidgetProvider's onUpdate() method. As stated in the onDataSetChanged() documentation,

...expensive tasks can be safely performed synchronously within this method. In the interim, the old data will be displayed within the widget.

The other advantage here is that you can include a call to AppWidgetManager.notifyAppWidgetViewDataChanged() in your app's ViewModel observer's onChanged() method; this will trigger the onDataSetChanged() and the widget will update each time the Room database is changed, rather than just at the fixed interval for onUpdate().

Outlook answered 13/10, 2018 at 11:54 Comment(2)
Can you please elaborate on where the notifyAppWidgetViewDataChanged() should be called? If it should be called within the ViewModel class, how are references to widgetIds retreived?Haematoma
@AleksandarStefanović, I'm really sorry, but it's been a while since I worked on this, and I don't recall specific details. Maybe someone else can chime in with an answer. I recommend you post it as a new question.Outlook
M
4

So answering my self here..

the only way that worked is to use the context to get the DB instance from the abstract class and run the DAO queries directly in background thread without the ViewModelProviders class

    new AsyncTask<Context, Void, List<Data>>(){
        @Override
        protected List<Quote> doInBackground(Context... context) {

            List<Data> List=null;
                AppDatabase db = AppDatabase.getDatabase(context[0]);
                List = db.DataModel().getData();


            return List;
        }

        @Override
        protected void onPostExecute(List<Quote> List) {
            super.onPostExecute(List);

            if(List != null){

                final Random random=new Random();
                for (int appWidgetId : appWidgetIds) {
                    updateAppWidget(context, appWidgetManager, appWidgetId, quoteList.get(random.nextInt(List.size())));
                }

            }

        }

    }.execute(context);

}
Mullinax answered 22/8, 2018 at 19:46 Comment(0)
C
2

you can access Room without passing through ViewModel.. ViewModel is just a component of the MVVM architecture, just like MVC or MVP.. without ViewModel, you can still access Room, you just need to have a context

Caeoma answered 22/8, 2018 at 19:42 Comment(0)
C
0

Exactly, you can do that.. you already need to make this DAO query from a background thread, so passing a context to an AsyncTask to execute the query is a good solution, but try to pass context in the constructor of the AsyncTask instead of passing it in the params.

Caeoma answered 22/8, 2018 at 20:4 Comment(0)
T
0

You can write a Repository class, and use it to access your Room database asynchronously.

Example: https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#7

Trapezius answered 23/8, 2018 at 10:33 Comment(0)
S
0

You don't need ViewModel to access the data from Room. You can access the data from anywhere, only catch is that the awesome feature of getting notified when data changes won't work. To do so simply add a method to your Dao object which return a simple List not the LiveData>. And that is it.

Sheritasherj answered 4/12, 2018 at 12:11 Comment(0)
C
0

Added a little more info of how some things can fit together to JaviMar's answer in Kotlin:

class RoomWidgetService : RemoteViewsService() {
    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
        return RoomRemoteViewsFactory(this.applicationContext, intent)
    }
}

class RoomRemoteViewsFactory(private val context: Context, intent: Intent) : RemoteViewsService.RemoteViewsFactory {
    private var todos: List<TodoItem> = listOf()
    private lateinit var todoDao: TodoDao


    override fun onCreate() {
        val database = TodoRoomDatabase.getDatabase(context)
        todoDao = database.todoDao()
    }

    override fun onDataSetChanged() {
        todos = todoDao.getTodosSync()
    }
    // rest of class...
}

I notify the widget of an update by observing something. You could observe the ViewModel in the main activity, or the Application. Not sure the best place to observe the data.

        todoViewModel.todoItems.observe(this) {
            val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
            val thisAppWidget = ComponentName(
                applicationContext.packageName,
                RoomWidget::class.java.name
            )
            val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.list_view )
        }
Carouse answered 23/12, 2021 at 4:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.