TL;DR
- Data Bindingライブラリで
<data>
タグと<variable>
タグを利用してXML上に変数定義をする際には、プリミティブ型で定義できる際には積極的にプリミティブ型を使っていこう - 参照型の変数を定義した際には、Data Bindingライブラリが内部でunboxingする
safeUnbox()
を利用することもできる
詳細
本記事執筆時点のライブラリバージョンは下記です:
- com.android.support:appcompat-v7: 27.0.1
- com.android.databinding:compiler: 3.0.0
Data Bindingライブラリを使ってXML上に変数を定義してViewの見た目を変えるとします。下記は、isLoading
であればProgressBarを表示、そうでなければProgressBarを非表示(View.GONE
指定)にする簡単なXMLの例です:
<data> <import type="android.view.View"/> <variable name="isLoading" type="Boolean" /> </data> ... <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:visibility="@{isLoading ? View.VISIBLE : View.GONE}" /> ...
一見なにも問題ないように見えますが、この状態でプロジェクトをビルドすると、タイトルのような警告が表示されます。
safeUnbox()
Boolean
は参照型なので、値にnull
が代入される可能性があります。null
が代入される可能性があるということは、NullPointerException
が発生する可能性が生まれるということです。
今回のコードを改善しようとするなら、2つ改善策がありそうです。
1つ目は、変数定義の際のtype
に、unboxする必要のない型を指定することです。ここではBoolean
型(=参照型)の代わりにboolean
型(=プリミティブ型)を指定すれば良さそうです。
<data> <import type="android.view.View" /> <variable name="isLoading" type="boolean" /> </data> ...
二つ目は、これはタイトルの警告文言にも記載されているのですが、
w: 警告: XXX is a boxed field but needs to be un-boxed to execute YYY. This may cause NPE so Data Binding will safely unbox it. You can change the expression and explicitly wrap XXX with safeUnbox() to prevent the warning.
safeUnbox()
というData Bindingライブラリが用意してくれているstaticメソッドを利用することもできるようです。
<ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{safeUnbox(isLoading) ? View.VISIBLE : View.GONE}" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" />
このsafeUnbox()
というメソッドはandroid.databinding
パッケージに内包されているDynamicUtil
クラスのstaticメソッドでした。
package android.databinding; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.databinding.BindingConversion; @javax.annotation.Generated("Android Data Binding") public class DynamicUtil { public static int safeUnbox(java.lang.Integer boxed) { return boxed == null ? 0 : (int)boxed; } public static long safeUnbox(java.lang.Long boxed) { return boxed == null ? 0L : (long)boxed; } public static short safeUnbox(java.lang.Short boxed) { return boxed == null ? 0 : (short)boxed; } public static byte safeUnbox(java.lang.Byte boxed) { return boxed == null ? 0 : (byte)boxed; } public static char safeUnbox(java.lang.Character boxed) { return boxed == null ? '\u0000' : (char)boxed; } public static double safeUnbox(java.lang.Double boxed) { return boxed == null ? 0.0 : (double)boxed; } public static float safeUnbox(java.lang.Float boxed) { return boxed == null ? 0f : (float)boxed; } public static boolean safeUnbox(java.lang.Boolean boxed) { return boxed == null ? false : (boolean)boxed; } }
ただ、実際にはこのメソッド呼び出しをXML上で明示的に行わなくても、Data Bindingライブラリが生成するJavaのソースコード(下記)内でこのメソッドを呼び出して安全にunboxしてくれているようなので、これに関しては書いても書かなくてもどちらでも良さそうです。(詳しいことはしっかりとは調べれていませんので、言い切る自信はないですが)
// read android.databinding.DynamicUtil.safeUnbox(isLoading)
androidDatabindingDynamicUtilSafeUnboxIsLoading = android.databinding.DynamicUtil.safeUnbox(isLoading);
DynamicUtil
やsafeUnbox()
について今回初めて知ったのでメモとして記載した次第です!以上です!
参考リンク
https://stackoverflow.com/questions/42872201/data-binding-safeunbox-warning