さて問題、次のAndroidのコードでまずいところはなにー?
public class EditItemFragment extends Fragment {
private int mItemId;
public EditItemFragment(int itemId) {
mItemId = itemId;
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.xxxx, container, false);
// mItemIdをもとに値をセット
return root;
}
// 以下省略
}
正解は「Fragmentを継承したクラスに引数ありコンストラクタのみを定義してはいけない」だねー。引数ありコンストラクタを書くと、Android Studioは次のように赤線で警告だすから、みんな気づくよね?
赤線で警告がでるけど、実はこのコード、実行できるんだ。Javaのプログラムとしては文法間違っていたりはしないからねー。
で、そもそも「なんで引数ありコンストラクタを定義したらだめなのー?」という問題、ちゃんと答えれるかなー?
この正解は「Fragmentオブジェクトは、中断からの復帰時などにシステムによって再生成されることがある」からだよね!この時、システムはリフレクションで「引数なしコンストラクタ」を探して生成するんだったね。
「再生成なんて滅多に起きないんじゃないの?」「テストできるの?」って、誰か言ったかなー?
次の方法で、「引数ありコンストラクタだけ定義していたら、再生成時にクラッシュする」ことが確認できるよ!
1. 設定→開発者オプション で、「アクティビティを保持しない」をONにしよう。これで、ホームボタンで中断した時に、確実にActivity(とFragment)が破棄されるようになるよ。
3. ほら、クラッシュした
こんなログがでるよ。
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mokelab.antipattern/com.mokelab.antipattern.MainActivity}: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.mokelab.antipattern.EditItemFragment: make sure class name exists, is public, and has an empty constructor that is public
「クラス名はちゃんとあって、publicで、publicな空のコンストラクタがあるのをたしかめてね!」って、ちゃんとログにも出てくるねー
Fragmentオブジェクトを作る時に、パラメータも一緒に渡したいときは、次のようにファクトリーメソッド(newInstance()メソッド)を用いてFragmentオブジェクトを生成しようね。GoogleのサンプルでnewInstance()をわざわざ使っているのは、こんな理由なんだよね!
public class EditItemFragment extends Fragment {
private static final String ARGS_ITEM_ID = "itemId";
int mItemId;
public static EditItemFragment newInstance(int itemId) {
EditItemFragment fragment = new EditItemFragment();
Bundle args = new Bundle();
args.putInt(ARGS_ITEM_ID, itemId);
fragment.setArguments(args);
return fragment;
}
// 続きは省略
}
0 件のコメント:
コメントを投稿