【问题标题】:How to read data and listen to changes from Room database in a Service?如何在服务中读取数据并监听 Room 数据库的变化?
【发布时间】:2018-06-18 18:09:35
【问题描述】:

我将用户信息存储在本地会议室数据库中。在活动和片段中,我使用 AndroidViewModel 和 LiveData 来监听对数据库所做的更改并更新 UI。

现在我想分析所有过去的用户数据,为未来的决策提供建议。我的建议会随着用户对数据库所做的每一次更改而改变,因此我需要经常更新我的建议,同时一遍又一遍地进行相同的计算。

我正在考虑在应用启动时启动一项服务,该服务通过 ViewModel 和 LiveData 侦听数据库更改并更新我的建议(这些建议也存储在同一个数据库中)。但不知何故,服务不能

  1. 通过viewModel = ViewModelProviders.of(this).get(DataViewModel.class); 获取 ViewModel
  2. 观察 LiveData 对象,因为它不是 LifecycleOwner。

基本上我只需要从数据库中读取所有条目,分析数据并在每次数据库内容更改时更新 5-10 个值。

如果不在服务中,我应该如何以及在哪里进行计算?也许我陷入了错误的想法,而服务不是正确的方法,因此非常感谢任何关于如何做到这一点的想法!

【问题讨论】:

  • 你可以选择 RxJava 吗?
  • @StefanoMtangoo 我对 RxJava 了解不多,除了它提供类似于 LiveData 的 Observables。你能告诉我一些关于你将如何使用 RxJava 来解决这个问题的细节吗?
  • 如何“对数据库进行更改”?我们是在谈论一个绑定服务,它只在应用程序处于前台时才会运行?
  • @0X0nosugar 通过用户交互(EditTexts、按钮、开关等)对数据库进行更改,然后立即写入数据库。因此,只能在应用程序处于前台时进行更改。绑定服务-> 是的,如果我可以在应用程序进入后台后立即终止服务之前完成我的计算。绑定服务有可能吗?
  • “如果我能在服务被杀死之前完成我的计算”——这取决于计算需要多长时间。为了安全起见,可以启动一个 IntentService(它使用另一个线程进行工作,如果过早终止,它将重新启动)。 IntentService 通常会以某种方式保存其结果并发送本地广播以让感兴趣的各方知道它已完成其任务。现代变体:JobIntentService。一旦您的应用再次进入前台,一些 RecommendationsViewModel 的工作可能就是从存储中获取结果(如果有的话)

标签: java android android-service android-room android-viewmodel


【解决方案1】:

观察 LiveData 对象,因为它不是 LifecycleOwner

LiveData 上使用observeForever(),在适当的时候通过removeObserver() 手动取消注册(服务的onDestroy(),如果不是更早的话)。

请记住,此处适用标准服务限制(例如,服务在 Android 8.0+ 上运行约 1 分钟,除非它们是前台服务),因此您可能需要考虑其他方法。

【讨论】:

  • 这是否意味着服务必须实现Observer?如果是,服务如何获得正确的 LiveData 实例以进行 [un] 注册?是否应该通过消息传递 LiveData 实例?
  • @0X0nosugar:“这是否意味着服务必须实现观察者?” -- 如果你愿意,它可以使用内部类。 “服务如何获得正确的 LiveData 实例以进行 [un] 注册?” -- 它将从您的存储库或 DAO 中检索LiveData,并在字段中保留它。 “应该通过消息传递 LiveData 实例吗?” -- 我怀疑这是可能的。
  • @CommonsWare ObserveForever 是关键,非常感谢!此外,我还能够通过ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication)).create(MyViewModel.class); 在服务中接收 ViewModel 的实例。
【解决方案2】:

我最终使用了一项服务并解决了我的问题,如下所示:

在我的Application objectonCreate 方法中,我将MyService 绑定到它:

serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder iBinder) {
            service = ((MyService.MyLocalBinder) iBinder ).getService();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
Intent intent = new Intent(this, MyService.class);
getApplicationContext().bindService(intent, serviceConnection, BIND_AUTO_CREATE);

只要应用程序没有被破坏,将服务绑定到应用程序上下文就应该使服务保持活动状态。在MyService 中,我通过AndroidViewModelFactory 获得了一个ViewModel 实例,如下所示

MyViewModel myViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(MyViewModel.class);

我可以像这样通过observeForever 观察从 ViewModel 获取的 LiveData

Observer<List<Entry>> obsEntries = new Observer<List<Entry>>() {
        @Override
        public void onChanged(@Nullable List<Entry> entries) {
            //perform calculations with entries in here
        }
    };
    viewModel.getEntries().observeForever(obsEntries);

重要提示:从 Service 的 onDestroy 中的 LiveData 引用中删除观察者(这就是我保留对 Observer 对象的本地引用的原因):

@Override
public void onDestroy() {
    super.onDestroy();
    viewModel.getEntries().removeObserver(obsEntries);
}

谢谢大家!

【讨论】:

  • 您能否详细说明您创建的 MyService.class。我在 MyService.MyLocalBinder 的这条线上收到错误: service = ((MyService.MyLocalBinder) iBinder ).getService() Thnx in advance
【解决方案3】:

Entity 中没有任何改变。

在 DAO 中,例如

@Dao
public interface TimeDao {
    //    ...
    //    ...

    // for a started Service using startService() in background
    @Query("select * from times where name = :bName")
    List<TimeEntity> getServiceTimes(String bName);

}

这不是 LiveData

在数据库中,例如,

@Database(entities = {DataEntity.class, TimeEntity.class}, version = 1)
public abstract class BrRoomDatabase extends RoomDatabase {

    public abstract TimeDao iTimeDao();

    public static BrRoomDatabase getDatabase(final Context context) {

        if (INSTANCE == null) {        
            //          ...
        }
        return INSTANCE;
    }
}

一个接口类

public interface SyncServiceSupport {
    List<TimeEntity> getTimesEntities(String brName);
}

它的实现类。

public class SyncServiceSupportImpl implements SyncServiceSupport {

    private TimeDao timeDao;

    public SyncServiceSupportImpl(Context context) {

        timeDao = BrRoomDatabase.getDatabase(context).iTimeDao(); 
        // getDatabase() from where we get DB instance.
        // .. and .. 
        // public abstract TimeDao iTimeDao();  <= defined abstract type in RoomDatabase abstract class
    }

    @Override
    public List<TimeEntity> getTimesEntities(String name) {
        return timeDao.getServiceTimes(name);
    }

}

最后……在服务中……

public class SyncService extends Service {

    //..

    // now, iEntities will have a List of TimeEntity objects from DB .. 
    // .. retrieved using @Query 

    private static List<TimeEntity> iEntities = new ArrayList<>();  
    private static SyncServiceSupportImpl iSyncService;

    private static void getFromDB(String brName) {

        new AsyncTask<String, Void, Void>() {
            @Override
            protected Void doInBackground(String... params) {
                iEntities = iSyncService.getTimesEntities(params[0]);
                return null;
            }

            @Override
            protected void onPostExecute(Void agentsCount) {

            }
        }.execute(brName);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //init service
        iSyncService = new SyncServiceSupportImpl(SyncService.this);

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        // this can be elsewhere also !..

        getFromDB(myName);
    }

}

【讨论】:

  • 享负盛名
  • 一切都好,除了不应该再使用 AsyncTask 了。
猜你喜欢
  • 1970-01-01
  • 2016-05-25
  • 2010-11-26
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
  • 2012-05-26
相关资源
最近更新 更多