web-dev-qa-db-ja.com

サービスでデータを読み取り、Roomデータベースからの変更をリッスンするにはどうすればよいですか?

ユーザー情報をローカルルームデータベースに保存しています。アクティビティとフラグメントでは、AndroidViewModelとLiveDataを使用して、データベースに加えられた変更をリッスンし、UIを更新します。

ここで、過去のユーザーデータをすべて分析して、将来の決定に関する推奨事項を示します。私の推奨事項は、ユーザーがデータベースを変更するたびに変更されるため、同じ計算を何度も繰り返しながら、推奨事項を頻繁に更新する必要があります。

アプリの起動時に、ViewModelとLiveDataを介してデータベースの変更をリッスンし、推奨事項(同じデータベースに格納されている)を更新するサービスを開始することを考えていました。しかし、どういうわけかサービスはできません

  1. viewModel = ViewModelProviders.of(this).get(DataViewModel.class);を介してViewModelを取得する
  2. lifecycleOwnerではないため、LiveDataオブジェクトを観察します。

基本的に、データベースからすべてのエントリを読み取り、データを分析し、データベースの内容が変更されるたびに5〜10個の値を更新するだけです。

サービス中でない場合、どこでどのように計算する必要がありますか?多分私は間違った考えに囚われており、サービスはそれを行う正しい方法ではないので、これを行う方法についてのアイデアは非常に高く評価されています!

7
steasy

lifeDataOwnerではないため、LiveDataオブジェクトを観察する

LiveDataobserveForever()を使用し、必要に応じてremoveObserver()を介して手動で登録を解除します(サービスのonDestroy()、早くなければ)。

ここでは標準のサービス制限が適用されることに注意してください(たとえば、サービスはAndroid 8.0+でフォアグラウンドサービスでない限り)1分間実行されるため、とにかく他のアプローチを検討する必要がある場合があります。 。

5
CommonsWare

エンティティに変更はありません。

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

}
2
Pramod P K

私はサービスを使用することになり、次のように私の問題を解決しました:

onCreateメソッドでApplication objectMyServiceをバインドします:

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参照からオブザーバーを削除します(そのため、Observerオブジェクトへのローカル参照を保持しています)。

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

みんなありがとう!

2
steasy