LoginSignup
4
2

More than 5 years have passed since last update.

Snackbar表示中に別のSnackbarが表示できないことがある件

Last updated at Posted at 2017-11-09

ちょっとハマったのでメモしておきます。
以下のコードだと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();
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2