警告ですが、エラーにならないので、そのままにしていることがあります。真面目に向き合ってみようという趣旨です。
Warnings
最近のemulator、Android Studio 3.0 ではwarningがはっきり表示されるようになったので(仕方なく) 積極的にバグの種をつぶせるようになりましたね。
1.@NonNull
2.Unchecked call
3.package-private
4.NullPointerException
5.buttonBarButtonStyle
6.This field leaks a context object
7.override performClick
8.override performClick
@NonNull
Not annotated parameter overrides @NonNull parameter
これはNullness アノテーション| Android Studioにあるように
このメソッドの引数はNullを許容しないことを表明するアノテーションです。
Nullにならないのであればこのアノテーションを付加します。
あるいは、ここにNullが入ってはいけないというこのメソッドを使う人(自分も含め)への警告ともなり得ます。
warningが出ている引数の前に@NonNullを付加して
NonNullが使うためのライブラリもimportしておきます。
1 2 3 4 5 6 7 8 |
import androidx.annotation.NonNull; ... @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { ... } |
この onRequestPermissionsResult ではシステムから返されるものなのでこちら側では操作できないと思っていましたが
Android Quickstartにはこういうコメントをみつけました。
permissions
grantResults
Never null
とありますから@NonNullをつけても大丈夫、というかwarning入れなくてもいいような気もします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Respond to requests for permissions at runtime for API 23 and above. * @param requestCode The request code passed in * requestPermissions(android.app.Activity, String, int, String[]) * @param permissions The requested permissions. Never null. * @param grantResults The grant results for the corresponding permissions * which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null. */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { //.. } |
Unchecked call
unchecked call to XXX as a member of raw type ZZZ
1 2 3 4 5 |
ArrayAdapter arrayAdapter = new ArrayAdapter(this, R.layout.list); for (int i = 0; i < texts.length; i++) { arrayAdapter.add(texts[i]); } |
このケースではArrayAdapterの設定でメソッドを使おうとした時のwarningです。うっかりジェネリックを設定しないでビルドしてもエラーではなかったのが、基本的にジェネリックが必要だという警告です。
java.lang.Object
↳ android.widget.BaseAdapter
↳ android.widget.ArrayAdapter<T>
1 2 |
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, R.layout.list); ... |
package-private
Access can be package-private
1 2 3 4 5 |
public class ListViewAdapter extends ArrayAdapter<CellData> { public ListViewAdapter(Context context, int itemLayout, List<CellData> list) { super(context, 0, list); } |
ありがちなのがクラスをpublicで作成していたけれど、最終的にスコープがパッケージ内部のみで済んだ場合などで、package-privateにし忘れてます的な警告でしょう。
1 2 3 4 5 |
public class ListViewAdapter extends ArrayAdapter<CellData> { ListViewAdapter(Context context, int itemLayout, List<CellData> list) { super(context, 0, list); } |
NullPointerException
XXX may produce ‘java.lang.NullPointerException’
Nullになる可能性があるという警告です。nullチェックを入れろという警告ですね
1 2 3 4 |
data = getItem(position); if(data != null){ holder.textView.setText(data.imageComment); } |
buttonBarButtonStyle
これはレイアウトファイルでButtonにwarningが表示されました。これは最近見なくなりましたが
Buttons in button bars should be borderless; use style=”android:attr/buttonBarButtonStyle” (and ?android:attr/buttonBarStyle on the parent) |
ということで、Buttons – Components – Material Designにリンクが貼られていますが、Material Designを周到せよということらしいです。
レイアウトファイルに2箇所の追加をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center_horizontal" style="?android:attr/buttonBarStyle" tools:context=".MainActivity"> <Button android:text="@string/save_file" android:layout_margin="20dp" android:textSize="20sp" style="?android:attr/buttonBarButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> |
ボーダーレスのフラットな、Material Design的なボタンになりました。
This field leaks a context object
A static field leak contexts.
Non-static inner class have an implicit reference to their outer class.
If that outer class is for example a Fragment or Activity, then this reference means that long-running handler/loader/task will hold a reference to the activity which prevent it from getting garbage collected.
Similarly, direct field references to activities and fragments from these longer running instances can cause leaks.
ViewModel class should never point to Views or non-application Contexts.
つまりstaticでないインナークラスは参照を持っているので、ガーベージコレクションされずリークしますよということです。
解決策は、ケースバイケースになります。この場合は非同期AsyncTaskのクラスからメインのテキストを表示させたかったのです。カスタムのlistenerを実装して解決しました。
AsyncTaskが非推奨になったのであまりみることもないかもしれませんが
非同期AsyncTaskの使い方
override performClick
or
“Custom View has onTouchListner called on it but doesn’t override performClick”
ViewにonClickを実装すると警告されるのですが、
performClickをoverrideしないといけないという警告です。これは簡単にはできないのでそれぞれのView, 例えばここではImageViewの場合を想定すると、ImageViewを継承したカスタムImageViewクラスを作ってoverrideします。
1 2 3 4 5 6 7 8 9 10 11 12 |
public class CustomImageView extends AppCompatImageView{ public CustomImageView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean performClick() { super.performClick(); return true; } } |
また、カスタムImageViewなので
com.example.testimageviewdrag.CustomImageView
のようにpackage name + class name で作成します。(そのままコピペしないように)
1 2 3 4 5 6 7 8 9 10 11 |
<!-- package name + class name --> <com.example.testimageviewdrag.CustomImageView android:id="@+id/image_view" android:src="@drawable/bag" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:contentDescription="@string/description" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/text_view" /> |
prindtStackTrace()
堅牢なログに置き換えるべき
try catch の例外検査としてprintStackTrace()を設定しときのWarningです
これじゃ後々何のことか分からなくなるよという警告だと思います
例えばAssetから呼び込む例で、画像ファイルがない場合
1 2 3 4 5 6 |
try (InputStream istream = assets.open("img_3.jpg")){ Bitmap bitmap = BitmapFactory.decodeStream(istream); imageView.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } |
ファイルがないというWariningになりますが、アプリとしては画像なしで起動します
1 |
java.io.FileNotFoundException: img_3.jpgひ |
非検査例外の
RuntimeException()に変更すると
1 2 |
//e.printStackTrace(); throw new RuntimeException(e); |
アプリは起動せずエラーログが出力されます
1 |
Caused by: java.lang.RuntimeException: java.io.FileNotFoundException: img_3.jpg |
ファイル名を間違えてCallした場合はエラーとして認識できる方が
robust なんでしょうか
ちなみに、このRuntimeExceptionでも、ファイルは存在しても中身がテキストだったりすると、Warningでアプリは起動してしまいます