LoginSignup
16
18

More than 5 years have passed since last update.

自分の備忘メモ。ガスガス更新していく。

ライフサイクル/リソース管理

Fragmentにフィールドを持たせない

Fragmentのフィールドは、ライフサイクルにより オブジェクトが再生成されるタイミングで破棄される。
Fragmentにパラメータを付与したい場合はフィールドでなく setArguments()によってBundleオブジェクトとして渡す必要があり、 かつFragmentがアクティブになる前に設定する必要がある。 フィールドに直接設定すると、FragmentManager等に再生成された時に 内容が失われる。
Bundleオブジェクトに登録できない類のオブジェクトが必要な場合、 それらを必要な時に生成するリスナーを定義する。

悪い例:


class SampleFragment extends Fragment {
    View mView; // このviewは再生成時にnullになり復元されない
    void setView(View view) {
        mView = view;
    }
    public View onCreateView(...) {
        :
        mView.findViewById(...); // mViewはnullの可能性がある
        :
    }
}

良い例:


class SampleFragment extends Fragment {
    public static interface ViewProvider {
        View onCreateView();
    }
    public View onCreateView(...) {
        :
        // このFragmentを所有するActivityにViewを生成させる
        if (getActivity() != null && getActivity() instanceof ViewProvider) {
            View v = ((ViewProvider) getActivity()).onCreaetView();
            v.findViewById(...);
        }
        // このFragmentを所有するFragmentにViewを生成させる
        Fragment f = getTargetFragment();
        if (f != null && f instanceof ViewProvider) {
            View v = ((ViewProvider) f).onCreaetView();
            v.findViewById(...);
        }
        :
    }
}

FragmentとActivityに依存関係を持たせない

Fragment#getActivity()はnullの場合がある。
また逆に、FragmentViewPagerを使ってFragmentを複数管理し、 Activityから特定のFragmentを操作しようとする場合などで、 参照が無効になっている場合がある。 できる限り、ActivityとFragmentは互いに依存しないようにする。

ArrayAdapter#getView()で生成するビューは再利用される

ListViewの行のビューを生成するArrayAdapter#getView()では、 他の行のデータが設定済みのビューが使われなくなった時点で 別の行へ使い回される。 したがって、getView()の時点でのデータを使用してイベントリスナーの 処理等を記述すると別の行のデータを扱ってしまう可能性があることに注意する。

例:


public View getView(…) {
    View view = super.getView(…);
    final Person p = getItemAt(position);
    ((TextView) view.findViewById(R.id.name))
            .setText(p.getName());
    view.findViewById(R.id.btn).setOnClickListener(
        new View.OnClickListener {
            @Override
            public void onClick(View v) {
                // この時点でこのボタンは
                // 別のデータを割り当てられた行の
                // ボタンになっている可能性あり。
                // p.getName()は表示している内容と
                // 一致しない可能性がある
            }
        });
}

Dialog#show()を直接呼び出さない

Android 3.0以降(もしくはandroid-support-v4.jar利用)の場合はDialogFragmentを使う。 それ以外の場合は、Activityのライフサイクルメソッドから呼び出すこと。

Activity#onPause() または Activity#onStop() で破棄する

Activity内で確保したリソースの解放はできればonPause()メソッド、 遅くともonStop()メソッドで行うこと。

理由:

onPause()やonStop()が呼び出されたタイミングで、 確保したリソース(特にハードウェア)を 別のActivityやアプリが使用しようとしている可能性があるため。
Application#onDestroy()を終了処理に使わない

Application#onDestroy()はAPIとして存在しているものの実機では動作しない。 したがって、このメソッドをアプリケーション全体の終了処理に利用するのではなく、 より早い段階で終了処理を行なうことを検討する。 (Activity#onStop()など)

Bitmapはrecycle()してnullにする

Bitmapを生成した後は、利用が終わった後にrecycle()メソッドにより解放し、 さらにフィールドとして保持している場合はnullを代入して ガベージコレクションされるようにすること。

理由:

大抵の場合、Bitmapは他のオブジェクトに比べてメモリ使用量が大きく、 積み重なるとOutOfMemoryError等の致命的な問題に繋がるため、 不要になり次第すぐに解放する必要がある。

引数ContextにActivityを渡さない

引数にContextを取るメソッドに対して、原則としてActivityをContextとして渡さないこと。
ただし、そのメソッドを持つオブジェクトがユーティリティ系メソッドで すぐにActivityの参照を解放する場合や、 そのメソッドを持つオブジェクトがActivityのライフサイクルと 同期している場合は問題ない。
大抵のケースでは、Activity#getApplicationContext()を渡せば良い。

理由:

引数に渡したActivityがライフサイクルを終えても、 そのオブジェクトに参照を保持されているためにガベージコレクションされず、 メモリリークを引き起こすため。
注意:

Contextを渡すオブジェクトがUI部品である場合、 Activityを渡さないと、適切なStyleが適用されない場合がある。

内部クラスはstaticにする

内部クラスを定義する場合、親になるクラスとライフサイクルが 完全に同期するのでない限りstaticクラスにする。 特に、ActivityやFragmentはライフサイクルによってインスタンスが再生成 され、参照が無効になることがあるため非staticな内部クラスから 親のActivityやFragmentを参照するとバグにつながる。

理由:

非staticな内部クラスを作ると、そのインスタンスは親クラスの参照を 保持し続けるためメモリリークに繋がる。
UI設定はタイミングに気をつける

Activity#requestWindowFeature()やテーマの変更など、一部のUI変更メソッドは Activity#setContentView()より前に呼び出さなければ反映されないことに注意する。

匿名クラス、内部クラスの内部で参照する親クラスの変数のライフサイクルを考慮する

匿名クラスや内部クラスでは、その呼出し元メソッドの変数がfinalであるか、 フィールドであればアクセスすることができるが、実際にそのメソッドが 動作するタイミングで有効な参照であるかどうかを考慮すること。

悪い例:


public class SampleFragment() {
    private Param mParam;
    public void onCreate() {
        :
        mParam = new Param();
        findViewById(R.id.btn).setOnClickListener(
            new View.OnClickListener() {
                mParam.doSomehting();
                // この匿名クラスが暗黙的に参照している
                // SampleFragment.thisは
                // 再生成されてmParamがnull
                // になっている可能性がある

                getActivity().finish();
                // R.id.btnをタップしたタイミングでは
                // getActivity()はnullの可能性がある
            });
    }
}

フラグメントとActivityとの実装方針

フラグメントはTransaction管理しない
XMLでアタッチする!!

▼イメージを表した例
http://k-1-ne-jp.blogspot.jp/2013_10_01_archive.html

つーかこっちだな
http://y-anz-m.blogspot.jp/2012/06/fragment-activity.html

他のアプリから呼ばれるようにする

Dialogへのプロパティセット

DialogFragmentを使おう。
値の渡しは、独自setterではなく、Bundleを使おう

public class MyDialogFragment extends DialogFragment {

    public static MyDialogFragment newInstance(String title, String message) {
        MyDialogFragment dialog = new MyDialogFragment();
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("message", message);
        dialog.setArguments(args);
        return dialog;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        String title = getArguments().getString("title");
        String message = getArguments().getString("message");
        return new AlertDialog.Builder(getActivity()).setTitle(title).setMessage(message).setPositiveButton(android.R.string.ok, null).setNegativeButton(android.R.string.cancel, null).create();
    }
}
16
18
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
16
18