web-dev-qa-db-ja.com

Android RoomとRxJava 2で結果なしを処理する方法は?

データベースにテーブルの連絡先があり、電話番号の連絡先があるかどうかを確認したい。

@Query("SELECT * FROM contact WHERE phone_number = :number")
Flowable<Contact> findByPhoneNumber(int number);

電話番号との連絡があるかどうかを確認するために、上記のステートメントでRxJava 2 Compositeディスポーザブルを使用しています。

disposable.add(Db.with(context).getContactsDao().findByPhoneNumber(phoneNumber)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeWith(new DisposableSubscriber<Contact>() {
                @Override
                public void onNext(final Contact contact) {
                    Log.d("TAG", "phone number fined");
                    Conversation conversation;
                    if(contact != null){
                        conversation = Db.with(context).getConversationsDao().findBySender(contact.getContactId());
                        if(conversation != null){
                            conversation.setUpdatedAt(Utils.getDateAndTimeNow());
                            saveConversation(contact, conversation, context, text, phoneNumber, false);
                        } else {
                            conversation = getConversation(contact, contact.getPhoneNumber());
                            saveConversation(contact, conversation, context, text, phoneNumber, true);
                        }
                    } else {
                        conversation = Db.with(context).getConversationsDao().findByPhone(phoneNumber);
                        if(conversation != null){
                            conversation.setUpdatedAt(Utils.getDateAndTimeNow());
                            saveConversation(contact, conversation, context, text, phoneNumber, false);
                        } else {
                            conversation = getConversation(contact, phoneNumber);
                            saveConversation(contact, conversation, context, text, phoneNumber, true);
                        }
                    }
                }

                @Override
                public void onError(Throwable t) {
                    Log.d("TAG", "find phone number throwable");
                    Toast.makeText(context, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
                }

                @Override
                public void onComplete() {
                    Log.d("TAG", "onComplete");
                }
            }));

クエリで必要な電話番号の連絡先が見つかった場合、これは正常に機能しますが、結果がある場合は何も起こりません。

ここに私が書いた2つのテストケースがあり、それらはうまく機能します:

@RunWith(AndroidJUnit4.class)
public class ContactsTest {

    private AppDatabase db;

    @Rule
    public InstantTaskExecutorRule instantTaskExecutorRule =
            new InstantTaskExecutorRule();

    @Before
    public void initDb() throws Exception {
        db = Room.inMemoryDatabaseBuilder(
                InstrumentationRegistry.getContext(),
                AppDatabase.class)
                // allowing main thread queries, just for testing
                .allowMainThreadQueries()
                .build();
    }

    @After
    public void close(){
        db.close();
    }

    @Test
    public void insertAndFindTest(){
        final Contact contact = new Contact();
        contact.setName("Test");
        contact.setPhoneNumber(555);
        db.contactsDao()
                .insert(contact);

        db.contactsDao().findByPhoneNumber(contact.getPhoneNumber())
                .test()
                .assertValue(new Predicate<Contact>() {
                    @Override
                    public boolean test(@NonNull Contact savedContact) throws Exception {
                        if(savedContact.getPhoneNumber() == contact.getPhoneNumber()){
                            return true;
                        }
                        return false;
                    }
                });
    }

    @Test
    public void findNoValues(){
        db.contactsDao().findByPhoneNumber(333)
                .test()
                .assertNoValues();
    }

}

これをどのように解決できますか?

14
Zookey

here のように、この場合はMaybeまたはSingleを使用できます。

多分

@Query("SELECT * FROM Users WHERE id = :userId")
Maybe<User> getUserById(String userId);

ここで何が起こります:

  • データベースにユーザーがなく、クエリが行を返さない場合、たぶん完了するでしょう。
  • データベースにユーザーがいる場合、onSuccessがトリガーされ、完了します。
  • Maybeの完了後にユーザーが更新されても、何も起こりません。

シングル

@Query("SELECT * FROM Users WHERE id = :userId")
Single<User> getUserById(String userId);

以下にいくつかのシナリオを示します。

  • データベースにユーザーが存在せず、クエリが行を返さない場合、SingleはonError(EmptyResultSetException.class)をトリガーします
  • データベースにユーザーがいる場合、SingleはonSuccessをトリガーします。
  • Single.onCompleteが呼び出された後にユーザーが更新された場合、ストリームが完了したため、何も起こりません。

バージョン1.0.0-alpha5で追加されました。

24
J-rooft

シングルでラッパーを使用することもできると思います。お気に入り:

public class QueryResult<D> {
            public D data;
            public QueryResult() {}

            public QueryResult(D data) {
                this.data = data;
            }

            public boolean isEmpty(){
                return data != null;
            }
 }

そして次のように使用します:

public Single<QueryResult<Transaction>> getTransaction(long id) {
            return createSingle(() -> database.getTransactionDao().getTransaction(id))
                    .map(QueryResult::new);
}

ここでcreateAsyncSingle

protected <T> Single<T> createSingle(final Callable<T> func) {
            return Single.create(emitter -> {
                try {
                    T result = func.call();
                    emitter.onSuccess(result);

                } catch (Exception ex) {
                    Log.e("TAG", "Error of operation with db");
                }
            });
}

IOスレッドを使用することを忘れないでください。

1
Djek-Grif