Help us understand the problem. What is going on with this article?

Android の罠 [1] ちゃんと onSaveInstanceState する

More than 5 years have passed since last update.

ちょくちょく Android ハマることとか気をつけることとかを書いてみます。Android てんでダメなので誤ったことを書いていたら指摘いただければ。

Activity と Fragment は死にやすい

Activity と Fragment, 画面を表示している間は生きてくれるものと普通の脳みそなら考えてしまいます。
が、Android ではそうではありません。

まず、画面を回転させるだけで死にます。(作り直される)
アプリがバックグラウンドに居る間に知らない間に殺されたりもします。(まあ iOS もそうだけど)
同じ画面なのに画面を回転させるだけで onCreate が二回発火します。

何らかの処理のフローとかを状態変数で管理していると知らない間におかしくなったりします。(そもそも何時死ぬか分からない Activity に状態変数を管理させるのもよくない)
コールバック云々の話もしたほうが良い気がしますが、今回は触れません。
(POST をお願いされたのに、戻ったら Activity が死んでいた…ウッ頭が)

状態の保存は、onCreate(Bundle savedInstanceState)onSaveInstanceState(Bundle outState) をちゃんと実装していれば問題を回避出来ます。

どう実装するか

ここで保存と復元を考えておくべきものは次の 3 つです

  1. 状態変数
  2. テキストフィールドの入力内容とか
  3. ビューの状態

1. 状態変数

変数の保存と復元は Icepick っていうナイスなライブラリがあるので任せてしまいましょう。

(2 タブにしてます)

class ExampleActivity extends Activity {
  @Icicle String username; // This will be automatically saved and restored

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

@Icicle アノテーションをプロパティに付けて、よろしくやるだけでイイ感じに保存と復元してくれます。

かんたん。

2. フォームの内容

次にフォーム入力内容の保存と復元ですがこちらは少し手間が必要です。
getViewById() で View を取ってきて .getText().toString() したものを保管します。getView を介在させる分面倒くさいです。
なぜか savedInstanceStatenull で返るクソ設計らしいので null チェック必要です。

Icicle ではよろしくやってくれていたのですが、本来はそれぞれの変数にキーが必要なので、適当に対応付けします。(EditText に対して EDIT_TEXT みたいな)

@Override protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ButterKnife.inject(this);

  if (savedInstanceState == null) {return;} // めんどくさい
  String editTextValue = savedInstanceState.getString("EDIT_TEXT"); // キー考えるのめんどくさい
  if (editTextValue != null) { editText.setText(editTextValue); } // null チェックだるい (追記: 今は setText() 内で null を "" に置換してくれる模様)
}

@Override protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putString("EDIT_TEXT", editText.getText().toString());
}

代案?

だるさがヤバいので Icicle property を介在させてこんな感じにすると良いのかもしれません。

@InjectView(R.id.edit_text) editText;
@Icicle String editTextValue;

@Override protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_my);
  ButterKnife.inject(this);

  Icepick.restoreInstanceState(this, savedInstanceState);
  editText.setText(editTextValue);
}

@Override protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);

  editTextValue = editText.getText().toString();
  Icepick.saveInstanceState(this, outState);
}

順番に気をつけましょう。変数が無駄に増えて、あまりスマートなやり方ではないと思います。
本来ならば editTextValue と EditText の内容を RxJava みたいなので bind するとかそういうのするともう少し綺麗になります。

Icepick と ButterKnife みたいに簡単にイイ感じにやってくれるライブラリが待たれますね。(@IcicleView みたいな)

3. View の状態

最後の View の状態の復元です。
これは、ユーザの操作によって状態の変わっている View をキチンと復活させるとかそういう話です。(ボタン押したら数値が増えるカウンターとか)

これは Icicle でイイ感じに保管した状態変数をほげほげして View を再現すればよいです。
難しいことはないですが、意識しなければ画面を回転させるだけで状態がリセットされる香ばしいアプリになります。

それぞれの状態変数が null かどうかで分岐する必要もあり面倒くさいです。
このへんは、RxJava とかで MVVM 的にやれば綺麗になると思いますが今回は触れません。

次?

何回か出た MVVM とか Rx まわりを触れたい。
でも次は多分 UI Thread と Background Thread まわりのアレコレにすると思います。

なんか間違ってたらコメントとか @_kaiinui にメンションお願いします

kaiinui
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした