Android - 如何从小部件访问房间数据库

Posted

技术标签:

【中文标题】Android - 如何从小部件访问房间数据库【英文标题】:Android - How to access Room database from widget 【发布时间】:2019-01-29 03:10:36 【问题描述】:

我想访问我的小部件类中的 Room DB,以根据存储的数据更新小部件。

这里的问题是 ViewModelProvider 需要一个片段或活动才能使用..

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

应用小部件类中没有,怎么办?

【问题讨论】:

【参考方案1】:

如果您使用RemoteViewsService 来填充您的小部件UI,那么您可以通过在其onDataSetChanged() 方法中而不是在AppWidgetProvider 的onUpdate() 方法中包含DAO 查询来避免使用AsyncTask。如onDataSetChanged() documentation中所述,

...可以在此方法中安全地同步执行昂贵的任务。在此期间,旧数据将显示在小部件中。

这里的另一个优点是您可以在应用的 ViewModel 观察者的 onChanged() 方法中包含对 AppWidgetManager.notifyAppWidgetViewDataChanged() 的调用;这将触发onDataSetChanged(),并且每次更改房间数据库时,小部件都会更新,而不仅仅是onUpdate() 的固定间隔。

【讨论】:

您能否详细说明应该在哪里调用notifyAppWidgetViewDataChanged()?如果应该在 ViewModel 类中调用它,如何检索对 widgetIds 的引用? @AleksandarStefanović,我真的很抱歉,但我已经有一段时间没有做这个了,我不记得具体的细节了。也许其他人可以回答。我建议您将其作为一个新问题发布。【参考方案2】:

我知道这已经有一段时间了,但我只是遇到了这个问题,并想在其他成员 Aaron-dunigan-atlee 的启发下添加一些有用的东西。

基本上显示了数据库和 Dao 对象的去向……更重要的是,onDataSetChanged() 方法调用了 Room 查询。 该示例用于说明应用程序的笔记,并且正如上面评论的那样,任何地方都不需要 AsyncTasks。

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()
             ...

莎莎

【讨论】:

【参考方案3】:

所以在这里回答我自己..

唯一可行的方法是使用上下文从抽象类中获取数据库实例,并直接在后台线程中运行 DAO 查询,无需 ViewModelProviders 类

    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);


【讨论】:

【参考方案4】:

你可以不通过 ViewModel 访问 Room。ViewModel 只是 MVVM 架构的一个组件,就像 MVC 或 MVP 一样。没有 ViewModel,你仍然可以访问 Room,你只需要一个上下文

【讨论】:

【参考方案5】:

您可以编写一个 Repository 类,并使用它来异步访问您的 Room 数据库。

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

【讨论】:

【参考方案6】:

确实,你可以这样做..你已经需要从后台线程进行这个 DAO 查询,所以将上下文传递给 AsyncTask 以执行查询是一个很好的解决方案,但尝试在构造函数中传递上下文AsyncTask 而不是在参数中传递它。

【讨论】:

【参考方案7】:

您不需要 ViewModel 来访问 Room 中的数据。您可以从任何地方访问数据,唯一要注意的是当数据更改不起作用时收到通知的令人敬畏的功能。为此,只需向您的 Dao 对象添加一个方法,该方法返回一个简单的 List 而不是 LiveData>。就是这样。

【讨论】:

【参考方案8】:

在 JaviMar 在 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...


我通过观察某些东西来通知小部件更新。您可以在主要活动或应用程序中观察 ViewModel。不确定观察数据的最佳位置。

        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 )
        

【讨论】:

以上是关于Android - 如何从小部件访问房间数据库的主要内容,如果未能解决你的问题,请参考以下文章

如何从小部件上的 TextView 获取默认文本大小?

如何在初始化程序颤动中从小部件访问传递的值

从小部件扩展访问核心数据

Android:从小部件打开错误的活动

从小部件的父级访问坐标

Qt4:从 QDockedWidget 的子类访问 QtDesigner 创建的小部件