6 votes

Comment lire les données et écouter les changements de la base de données Room dans un service ?

Je stocke les informations relatives aux utilisateurs dans une base de données locale. Dans les activités et les fragments, j'utilise AndroidViewModel et LiveData pour écouter les changements apportés à la base de données et mettre à jour l'interface utilisateur.

Je souhaite à présent analyser toutes les données relatives aux utilisateurs passés afin de formuler des recommandations pour les décisions futures. Mes recommandations changent à chaque modification de la base de données effectuée par l'utilisateur. Je dois donc mettre à jour mes recommandations fréquemment tout en effectuant les mêmes calculs encore et encore.

Je pensais lancer un service au démarrage de l'application qui écoute les changements dans la base de données via ViewModel et LiveData et met à jour mes recommandations (qui sont également stockées dans la même base de données). Mais d'une manière ou d'une autre, un Service ne peut pas

  1. obtenir un ViewModel via viewModel = ViewModelProviders.of(this).get(DataViewModel.class);
  2. observe un objet LiveData car il ne s'agit pas d'un LifecycleOwner.

Je dois simplement lire toutes les entrées de la base de données, analyser les données et mettre à jour 5 à 10 valeurs à chaque fois que le contenu de la base de données change.

Comment et où dois-je faire mes calculs si ce n'est pas dans un service ? Peut-être suis-je prisonnier d'une mauvaise pensée et qu'un service n'est pas la bonne façon de procéder, alors toute idée sur la façon de procéder est très appréciée !

5voto

CommonsWare Points 402670

observe un objet LiveData car il ne s'agit pas d'un LifecycleOwner

Utilisation observeForever() sur le LiveData , en se désinscrivant manuellement par l'intermédiaire de removeObserver() le cas échéant ( onDestroy() du service, si ce n'est plus tôt).

Gardez à l'esprit que les limitations de service standard s'appliquent ici (par exemple, les services s'exécutent pendant environ 1 minute sur Android 8.0+, sauf s'il s'agit de services d'avant-plan), il se peut donc que vous deviez envisager d'autres approches de toute façon.

2voto

steasy Points 83

J'ai fini par utiliser un Service et j'ai résolu mon problème comme suit :

Dans le cadre de la onCreate de ma méthode Application object Je lie MyService à elle :

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

Lier un service au contexte de l'application devrait permettre de maintenir le service en vie tant que l'application n'est pas détruite. Dans le cas d'un MyService J'obtiens une instance de ViewModel via AndroidViewModelFactory comme ceci

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

et je suis capable d'observer les données LiveData récupérées dans le ViewModel par l'intermédiaire de observeForever comme ceci

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 : Retirer l'observateur de la référence LiveData en onDestroy du Service (c'est pourquoi je garde une référence locale à l'objet Observer) :

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

Merci à tous !

1voto

Pramod P K Points 11

Rien à changer dans l'entité.

Dans le DAO, disons par exemple,

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

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

}

qui n'est pas un LiveData

Dans les bases de données, par exemple,

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

Une classe d'interface

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

Une classe d'implémentation.

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

}

Et enfin... dans le 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);
    }

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X