如何从服务中的 Room 数据库读取数据并监听更改?

How to read data and listen to changes from Room database in a Service?

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

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

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

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

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

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

observe a LiveData object as it is not a LifecycleOwner

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

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

我最终使用了一项服务并按如下方式解决了我的问题:

在我的 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);

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

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

谢谢大家!

实体中无需更改。

在 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;
    }
}

一个接口class

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

它的实现 class。

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

}