こんにちは。エンジニアの松下です。 最近視力が落ちたな〜と思い、そろそろメガネを新調したいな〜と思っています。
そういえば、 DroidKaigi 2024 が発表されました。去年に引き続き弊社も参加したい!
DroidKaigi 2024のティザーサイトをオープンしました🎉
— DroidKaigi (@DroidKaigi) 2024年4月11日
2024年9月11〜13日に東京で開催予定です!皆様のご参加をお待ちしております!
協賛をご検討の皆様もこちらからお問い合わせください!https://t.co/9wFP54n4N2
セッション公募も間もなく開始します。続報をお待ち下さい!
#DroidKaigi
弊社にも結構長く保守している案件がありまして、その新 OS バージョン対応を行っている際にコンパイルが通らなくなっていることに気が付きました。
原因を追っていると、どうも icepick のコード自動生成部分が悪さをしているようでした。
icepick は @State
というアノテーションをつけておけばいい感じに View や Activity の状態を保持できるスグレモノなのですが、自分の記憶だと kotlin でうまく動かなくて、なんやかんやで SavedStateHandle とかあるし最近は使われなくなった印象です。
icepick 久しぶりに見たな〜と懐かしみつつ、今回のアプリは ViewModel とか使ってないのでどうしようかなと悩んでいたところ、 SavedStateRegistry.SavedStateProvider
を使えばいい感じになるのではと思い、以下のように実装してみました。
public class SavedStateProvider<T extends Serializable> implements SavedStateRegistry.SavedStateProvider { private final String provider; private final String tag; private final WeakReference<SavedStateRegistryOwner> registryOwner; @Nullable private T state; public SavedStateProvider(SavedStateRegistryOwner registryOwner, String tag) { this(registryOwner, tag, null); } public SavedStateProvider(SavedStateRegistryOwner registryOwner, String tag, @Nullable T defaultValue) { this.registryOwner = new WeakReference<>(registryOwner); this.tag = tag; this.provider = "SavedStateProvider: " + tag; this.state = defaultValue; registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> { switch (event) { case ON_CREATE -> { SavedStateRegistry registry = registryOwner.getSavedStateRegistry(); registry.registerSavedStateProvider(provider, this); restore(registryOwner); break; } case ON_DESTROY -> { this.registryOwner.clear(); break; } default -> { break; } } }); } @Nullable public T getState() { return state; } public void setState(@Nullable T state) { this.state = state; } /** * どうしても同期的にリストアされたいときに気合でリストアを試みる * * @return state */ public T restore() { return this.restore(registryOwner.get()); } @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); bundle.putSerializable(tag, state); Timber.d("saveSate: (tag: %s, state: %s)", tag, state); return bundle; } /** * @noinspection unchecked */ private T restore(SavedStateRegistryOwner registryOwner) { SavedStateRegistry registry = registryOwner.getSavedStateRegistry(); Bundle state = registry.consumeRestoredStateForKey(provider); if (state != null) { Timber.d("restore: (tag: %s, state: %s)", tag, state.getSerializable(tag)); this.state = (T) state.getSerializable(tag); } return this.state; } }
利用例
public class HogeActivity extends AppCompatActivity { private final SavedStateProvider<Boolean> fuga = new SavedStateProvider<>(this, "fuga", false); void piyo() { if (!fuga.getState()) { fuga.setState(true); } } }
これで自動でリストアや保存が行われ、画面回転やアクティビティ破棄に対応できるようになりました。
ただし、 LifecycleEventObserver
で自動でやる場合は利用側の onCreate
ではリストアが間に合わないので、要件によっては同期的にコントロールするほうが良いかもしれません。
しかし、いい時代になったものだ…。そして超久しぶりに Java ちゃんと書いた。
いつもの
弊社ではいっしょに働くアプリエンジニアを募集しています!
ご興味のある方はぜひご応募いただけますと嬉しいです。
Androidエンジニア募集中!: www.wantedly.com
リードエンジニア(Android、iOS問わず)も募集中!:www.wantedly.com