问题
i am storing user information in a local room database. In activities and fragments I use AndroidViewModel and LiveData to listen to changes made to the database and update the UI.
Now I want to analyze all of the past user data to give recommendations for future decisions. My recommendations change on every change to the database made by the user so I need to update my reommendations frequently while doing the same calculations over and over again.
I was thinking about starting a service on app start that listens to database changes via ViewModel and LiveData and updates my recommendations (which are also stored in the same database). But somehow a Service cannot
- get a ViewModel via
viewModel = ViewModelProviders.of(this).get(DataViewModel.class);
- observe a LiveData object as it is not a LifecycleOwner.
Basically I simply need to read all entries from the database, analyze the data and update 5-10 values every time the database content changes.
How and where should I do my calculations if not in a service? Maybe I am trapped in a wrong thought and a service is not the right way to do it so any idea on how to do this is very much appreciated!
回答1:
observe a LiveData object as it is not a LifecycleOwner
Use observeForever()
on the LiveData
, manually unregistering via removeObserver()
when appropriate (onDestroy()
of the service, if not sooner).
Bear in mind that standard service limitations apply here (e.g., services run for ~1 minute on Android 8.0+ unless they are foreground services), so it may be that you need to consider other approaches anyway.
回答2:
I ended up using a Service and solved my problem as follows:
In the onCreate
method of my Application object
I bind MyService
to it:
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);
Binding a Service to the application context should keep the Service alive as long as the application is not destroyed. In MyService
I get an instance of ViewModel via AndroidViewModelFactory
like this
MyViewModel myViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(MyViewModel.class);
and I am able to observe the fetched LiveData from the ViewModel via observeForever
like this
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);
Important: Remove the observer from the LiveData reference in onDestroy
of the Service (that is why I keep a local reference to the Observer object):
@Override
public void onDestroy() {
super.onDestroy();
viewModel.getEntries().removeObserver(obsEntries);
}
Thanks everybody!
回答3:
Nothing to change in Entity.
In DAO, say for e.g.,
@Dao
public interface TimeDao {
// ...
// ...
// for a started Service using startService() in background
@Query("select * from times where name = :bName")
List<TimeEntity> getServiceTimes(String bName);
}
which is not a LiveData
In Database, for e.g.,
@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;
}
}
An interface class
public interface SyncServiceSupport {
List<TimeEntity> getTimesEntities(String brName);
}
An implementation class for it.
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);
}
}
And finally... in the service..
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);
}
}
来源:https://stackoverflow.com/questions/50915186/how-to-read-data-and-listen-to-changes-from-room-database-in-a-service