modified: 2018-02-20

Firebase RTDB で同じパスに対する Listen 時の注意

使用したライブラリは以下のバージョン。

implementation "com.google.firebase:firebase-core:11.8.0"
implementation "com.google.firebase:firebase-database:11.8.0"

概要

データベース(のパス)に読み取りアクセス権がない場合、登録したリスナの onCancelled() が呼ばれるが、orderBy を指定してリスナを設定したあと、同じパスに orderBy を指定せずに別のリスナを設定すると、orderBy を指定したリスナには onCancelled() がコールバックされるが、指定しないリスナの onCancelled() は呼ばれない。

バグのようだったので、いずれ直るかもしれません。

検証

ログを出力するだけの LogValueEventListenerLogChildEventListener を用意し、以下のようなコードを書いてみる。

val ref = FirebaseDatabase.getInstance().getReference("test")
ref.orderByKey().addListenerForSingleValueEvent(LogValueEventListener("single-order"))
ref.addListenerForSingleValueEvent(LogValueEventListener("single"))
ref.orderByKey().addValueEventListener(LogValueEventListener("value-order"))
ref.addValueEventListener(LogValueEventListener("value"))
ref.orderByKey().addChildEventListener(LogChildEventListener("child-order"))
ref.addChildEventListener(LogChildEventListener("child"))

同じパスに6つのリスナを登録しているが、onCancelled() が呼ばれたのは orderByKey() を指定した3つのみ。orderByKey() を指定しなかったリスナの onCancelled() は呼ばれなかった。

先頭を orderByKey() なしにしてみる。(2行めと3行めを入れ替え)

val ref = FirebaseDatabase.getInstance().getReference("test")
ref.addListenerForSingleValueEvent(LogValueEventListener("single"))
ref.orderByKey().addListenerForSingleValueEvent(LogValueEventListener("single-order"))
ref.orderByKey().addValueEventListener(LogValueEventListener("value-order"))
ref.addValueEventListener(LogValueEventListener("value"))
ref.orderByKey().addChildEventListener(LogChildEventListener("child-order"))
ref.addChildEventListener(LogChildEventListener("child"))

この場合はすべてのリスナで onCancelled() が呼ばれた。

あとからパーミッションがなくなった場合

addValueEventListener() または addChildEventListener() で登録したリスナに対しては、onDataChanged() などのイベントが通知された後で、パーミッションがなくなった場合も onCancelled() が呼ばれる。

しかし addListenerForSingleValueEvent() はワンショットなので、 onDataChanged() が呼ばれた後は onCancelled() は呼ばれない。

最初に登録したリスナが orderBy ありの addListenerForSingleValueEvent() で、その他が orderBy なしだけの場合、

val ref = FirebaseDatabase.getInstance().getReference("test")
ref.orderByKey().addListenerForSingleValueEvent(LogValueEventListener("single-order"))
ref.addValueEventListener(LogValueEventListener("value"))
ref.addChildEventListener(LogChildEventListener("child"))

いずれのリスナの onCancelled() も呼ばれず、パーミッションエラーになったことに気づくことができなかった。

その後

パーミッションエラーになった後、さらにリスナを登録した場合、もう orderBy ありだろうがなしだろうが何も通知されなくなってしまった。

登録済みのリスナをすべて removeEventListener() で解除したらこの現象は直った。

しかし、addListenerForSingleValueEvent() で登録したリスナは解除する方法がないので、復旧できない。この場合はアプリを終了するしかない気がする。