ちょっとハマったのでメモしておきます。
以下のコードだとSnackbar上のボタンを押したときに出る2回目のSnackbarが高確率で表示されません。
Snackbar.make(view, "Snackbar1", Snackbar.LENGTH_LONG)
.setAction("Action") {
Snackbar.make(view, "Snackbar2", Snackbar.LENGTH_LONG).show()
val stringBuilder = StringBuilder()
for (i in 0..10000000) {
stringBuilder.append("test")
}
}
.show()
こんなにメモリが少ない状態を無理に作らなくても、メモリが少ないアプリでは普通に表示されなかったりします
なぜ?
SnackbarはSnackbarManagerというシングルトンなクラスが管理しており、前のSnackbarが消えてから次のSnackbarを表示する制御がされています。
次のSnackbarを表示するとき、実際に表示するためのレイアウトなどを参照しているcallbackはWeakReferenceになっていて、それがあれば表示を行います。
そのため、2つ目のSnackbar.showから、前のSnackbarが消えるまでに、メモリ不足の場合にcallbackが開放されてしまいnullが入ってしまい、表示されないことがあります。
SnackbarManager.java
private static class SnackbarRecord {
final WeakReference<Callback> callback;
int duration;
boolean paused;
SnackbarRecord(int duration, Callback callback) {
this.callback = new WeakReference<>(callback);
this.duration = duration;
}
SnackbarManager.java
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
どのように対応するか?
普通にSnackbarをメンバ変数で持ったり、以下のように持つことで対応できます。他にいい方法があれば教えてください!
<resources>
<item name="retain_snackbar" type="id"/>
</resources>
Snackbar snackbar = Snackbar.make(view, "Snackbar2", Snackbar.LENGTH_LONG);
view.setTag(R.id.retain_snackbar, snackbar);
snackbar
.addCallback(new Snackbar.Callback(){
@Override public void onShown(Snackbar sb) {
view.setTag(R.id.retain_snackbar, null);
}
})
.show();