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

徘徊边缘 提交于 2020-06-12 03:00:06

问题


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

  1. get a ViewModel via viewModel = ViewModelProviders.of(this).get(DataViewModel.class);
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!