ビルド時にエラー発生(Error generating final archive)
また出た。
毎年恒例のこのエラー。
対応策は 「debug.keystore」 の削除。
去年の自分のエントリにも書いてた。
Error generating final archive(Debug Certificate expired on~) が発生する
このエラーが出たということはー
自分の場合は 「Androidアプリ開発始めてから丸2年経過」 ということか。
毎年このエラーが出るたびにイロイロ考えさせられそうです。
自分の1年を振り返るいいタイミング?
画面回転してもFragment再生成しないでレイアウト変更したい+取得したデータ使い回したい
お題
- 画面回転させて、縦/横で別のレイアウトを表示する
- 縦画面のときは ListView を表示
- 横画面のときは GridView を表示(1行2列)
- ただし、一度取得したデータは使いまわしたい
縦画面
横画面
実装のポイント
- Activity
- コンフィグ変更のたびに毎回 再生成
- Fragment
- Fragment#setRetainInstance(true) を設定し、Activity 再生成時に Fragment は再生成されないようにする(Fragment#onCreate/onDestroy を抑止)
- Activity 再生成のたびに走る Fragment#onCreateView でレイアウト再読込させる
- データ取得
- 一度だけ(今回の例では一度だけ呼び出される Fragment#onCreate 内で)
- レイアウト
メイン部分のコード。(コード全体はこちら)
はてなblogに移行しました
一括インポート機能を使用して移行してみました。
idが dai4649 だったり。
URLは daichan4649 だったり。
いろいろとややこしいのですが、こちらでも4649お願い致します!
Activity再生成時のデータの保存・復元(Fragment#setRetainInstance)
- ネットワークからデータ取得(画像とか)
- データ取得完了したら画面にデータ反映
こういうパターンってよくありますよね。このときに画面回転等のコンフィグ変更(縦横切替とか)が行われると 「Activity のインスタンスが破棄->再生成」 されるので、何も考えてないと保存データも一緒に破棄されてしまいます。なので、内部に保存しているデータを 「保存/復元」 する必要があります。
自分、コンフィグ変更で Activity再生成 とかされると正直めんどくさいので、基本的に "android:configChanges" の属性指定で Activity再生成抑止 して データの保存/復元 とか考えない方向でいいんじゃね。とか思ってたんです(すみません)。
でもよくよく調べてたら Activity再生成 を回避できないケース がありまして。例えば、「フォント切替」。
こいつは configChanges のパラメータにないので、厳密にいうとやっぱり Activity再生成 は避けて通れない。悲しいです。というわけで、Activity再生成時の内部データの保存/復元方法 はどうやるのが正解なのだろう、と調べてみると 2.x、3.x〜系 でだいぶ変わっていました。自分への備忘録の意味も込めてまとめてみます。
Activity再生成時の保存/復元処理
2.x系(API Level 10まで)
- サイズ小さめのデータの場合(serializeされる)
- Activity#onSaveInstanceState で保存処理
- Activity#onCreate、onRestoreInstanceState で復元処理
- サイズ大きめのデータの場合(serializeされない)
- Activity#onRetainNonConfigurationInstance で保存処理
- 再生成後に任意のタイミングで Activity#getLastNonConfigurationInstance 呼び出して復元処理
3.x、4.x系(API Level 11〜)
- サイズ小さめのデータの場合(serializeされる) ※Activity単位(2.x系と一緒)
- Activity#onSaveInstanceState で保存処理
- Activity#onCreate、onRestoreInstanceState で復元処理
- サイズ小さめのデータの場合(serializeされる) ※Fragment単位
- Fragment#onSaveInstanceState で保存処理
- Fragment#onCreate で復元処理
- サイズ大きめのデータの場合
- Fragment#setRetainInstance(true) を指定して Fragment が破棄されないようにし、そこへデータ保存
- 任意のタイミングで↑の Fragment からデータ取得して復元処理
サイズ小さめデータの 保存/復元 を行う際、Activity#onSaveInstanceState〜 については 2.x系 と特に変わらないのですが、Fragment単位でも同様のことができるようになってます。
サイズ大きめデータの 保存/復元 を行う際、3.x系〜は Activity#onRetainNonConfigurationInstance が非推奨になってます。代わりに Fragment#setRetainInstance(true) を使用する方法が推奨されています。
Fragment#setRetainInstance(true) を呼び出すことでどうなるかというと、
- Activity再生成時に Fragment#onDestroy、onCreate が呼ばれなくなる。
- Activity再生成時には Fragment#onDetach、onAttach が呼び出されるだけ。
- Fragmentインスタンスが破棄されないので Fragmentインスタンス内にデータをそのまま保持しておくことが可能。
ということみたいです。つまり、でかいデータについては、
Fragmentインスタンス自体を破棄されないようにしとく。 で、Fragmentインスタンス内部でそのまま保持っておく。
ってことですかね!
参考
- Activity#onRetainNonConfigurationInstance
- Android UI Cookbook for 4.0
- 「2.9 ビューを持たないフラグメントで定期処理をする」
DialogFragment使用時に画面の回転で例外発生(InstantiationException)
DialogFragment を private なインナークラスで定義してたら、
画面の回転(Activity再生成) でアプリが落ちてしまった。
出力された例外はこれ。
Caused by: android.app.Fragment$InstantiationException: Unable to instantiate fragment ErrorCaseDialogFragment: make sure class name exists, is public, and has an empty constructor that is public
Fragment が private なインナークラスなので、
外部からコンストラクタ呼び出せなくて例外になった様子。
使用したコードはこんなかんじ。
ErrorCaseDialogFragment のクラス定義を
private static ErrorCaseDialogFragment ↓ public static ErrorCaseDialogFragment
こう修正したら普通に動くけども。
こうするくらいならFragmentを外部ファイルにした方がいいですよねw
というわけで。
結論
Activity再生成時に自動生成するFragmentは、
空のpublicコンストラクタが外部から呼び出せるように、
privateなインナークラスではなく publicなクラス にする。