$30 off During Our Annual Pro Sale. View Details »

How to keep data between orientation changes

How to keep data between orientation changes

AbemaTVで実装している画面回転を支えるデータ保持についての話

Masaki Ogata

April 20, 2017
Tweet

More Decks by Masaki Ogata

Other Decks in Programming

Transcript

  1. CA.apk #2 @CyberAgent Masaki Ogata ը໘ճసΛࢧ͑Δσʔλอ࣋ :)

  2. ࣗݾ঺հ Masaki Ogata CyberAgent, Inc. / AbemaTV, Inc. ogaclejapan @ogaclejapan

    New
  3. 2017೥4݄11೔ൃച ࢁށໜथɼࡔాߊҰɼࠇ઒༸ɼ౻ా ୖຏɼࢁాߤɼాࡔ࿨ெɼ۽୩஌ࢠɼ ৿ຊརതɼࡔຊ࿨େɼখܗণथɼླ ໦େوɼࢤำါلɹஶ B5൑ʗ192ϖʔδ ఆՁʢຊମ1,980ԁʴ੫ʣ ISBN 978-4-7741-8863-8 https://goo.gl/Jcy3lJ

    New
  4. ը໘ճసΛࢧ͑Δσʔλอ࣋ :) by AbemaTV

  5. 2017/4 ॎରԠϦϦʔε͠·ͨ͠

  6. Կ͕มΘͬͨͷ͔ʁ

  7. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="tv.abema" > ... <activity android:name=".components.activity.MainActivity" android:configChanges="orientation|keyboardHidden| screenSize" android:screenOrientation="sensorLandscape"

    android:theme="..." /> ... </manifest> AndroidManifest.xml
  8. AndroidManifest.xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="tv.abema" > ... <activity android:name=".components.activity.MainActivity" android:configChanges="orientation|keyboardHidden| screenSize"

    android:screenOrientation="sensorLandscape" android:theme="..." /> ... </manifest>
  9. AndroidManifest.xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="tv.abema" > ... <activity android:name=".components.activity.MainActivity" android:screenOrientation="behind" android:theme="..."

    /> ... </manifest>
  10. Activity࠶ੜ੒ͱ ৽͍͠ੜ׆࢝Ί·ͨ͠$

  11. ཧ༝̍ɿॎʗԣͰ࠷దͳUIΛ࣮ݱ͍ͨ͠

  12. ཧ༝̎ɿMultiWindowʹରԠ͍ͨ͠

  13. Activity࠶ੜ੒͸Կ͕໰୊ͳͷ͔ʁ

  14. Activity࠶ੜ੒ͷ໰୊఺ɿ • ϨΠΞ΢τঢ়ଶͷ෮ݩ
 ʢChannelɺFullscreenɺ…ʣ • αʔόʔ͔Βऔಘͨ͠σʔλͷ࠶ར༻ • ॎʗԣͷը໘੾Γସ͑ͷγʔϜϨε͞

  15. ҰൠతͳΞϓϩʔν

  16. onSaveInstanceState(..)ͰBundle΁อଘ public class MainActivity extends AppCompatActivity { @Override protected void

    onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable("foo", foo); ... } @Override protected void onCreate(Bundle savedInstanceState) { ... if (savedInstanceState != null) { Foo foo = savedInstanceState.getParcelable("foo"); }
  17. (‘A`)ΫιΊΜͲͤ͐͘ʔʂ

  18. AbemaTVͷΞϓϩʔν

  19. onRetainCustomNonConfigurationInstance()ͰObjectฦ͢ public class MainActivity extends AppCompatActivity { @Override public Object

    onRetainCustomNonConfigurationInstance() { return foo; } @Override protected void onCreate(Bundle savedInstanceState) { ... Object obj = getLastCustomNonConfigurationInstance(); if (obj instanceof Foo) { Foo foo = (Foo) obj; }
  20. ͜Εͩ! ! ‷( ŋŷŋ)و ̑̑ ŞƄŕ !

  21. ͪΐͬ͜ͱιʔείʔυΛ௥ͬͯΈͨ

  22. FragmentActivity.java public Object onRetainCustomNonConfigurationInstance() { return null; } @Override public

    final Object onRetainNonConfigurationInstance() { ... Object custom = onRetainCustomNonConfigurationInstance(); ... NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.fragments = fragments; nci.loaders = loaders; return nci; }
  23. Activity.java public Object onRetainNonConfigurationInstance() { return null; } NonConfigurationInstances retainNonConfigurationInstances()

    { Object activity = onRetainNonConfigurationInstance(); ... NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; nci.children = children; nci.fragments = fragments; nci.loaders = loaders; ... return nci; }
  24. ActivityThread.java public final ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing) { return

    performDestroyActivity(token, finishing, 0, false); } private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance) { ActivityClientRecord r = mActivities.get(token); ... if (getNonConfigInstance) { try { r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); ...
  25. ActivityThread.java private void handleRelaunchActivity(ActivityClientRecord tmp) { ... handleDestroyActivity(r.token, false, configChanges,

    true); ... ... handleLaunchActivity(r, currentIntent, ".."); ... } private Activity performLaunchActivity(...) { activity.attach(appContext, ..., r.lastNonConfigurationInstances, ...); ... r.lastNonConfigurationInstances = null;
  26. Configuration ChangeʹΑΔ Activity࠶ੜ੒࣌ͷΈ෮ݩՄೳ

  27. AbemaTVͰ͸ Ͳ͏࣮૷͍ͯ͠Δͷ͔ʁ

  28. AbemaTVͷΞʔΩςΫνϟɿ https://speakerdeck.com/ogaclejapan/flux-de-relax

  29. AbemaTVͷFluxΞʔΩςΫνϟɿ

  30. ը໘ճసͰഁغͤ͞ͳ͍DaggerͷComponentΛؚΊΔ @Override public ScreenComponent getScreenComponent() { if (screenComponent == null)

    { ScreenInstance retainedInstance = (ScreenInstance) getLastCustomNonConfigurationInstance(); if (retainedInstance == null) { ... screenComponent = Components.forApp(this).plus(...); } else { screenComponent = retainedInstance.component; } } return screenComponent; } @Override public Object onRetainCustomNonConfigurationInstance() { return new ScreenInstance(screenComponent, ...); }
  31. AbemaTVͷDIίϯϙʔωϯτߏ੒ɿ AppComponent: ʢશମʣ ScreenComponent: ʢը໘୯Ґʣ ActivityComponent: ʢը໘୯Ґʣ ը໘ճసΛӽ͑ͯσʔλΛอ࣋͢ΔͨΊʹ ScreenίϯϙʔωϯτΛ௥Ճɻ

  32. Activity࠶ੜ੒ͷ໰୊఺ɿ •ϨΠΞ΢τঢ়ଶͷ෮ݩ 
 ʢChannelɺFullscreenɺ…ʣ • αʔόʔ͔Βऔಘͨ͠σʔλͷ࠶ར༻ • ॎʗԣͷը໘੾Γସ͑ͷγʔϜϨε͞

  33. ͓ΘΓʹ /** * Return the value previously returned from *

    {@link #onRetainCustomNonConfigurationInstance()}. */ @SuppressWarnings("deprecation") public Object getLastCustomNonConfigurationInstance() { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); return nc != null ? nc.custom : null; } d..deprecation!??
  34. ଎͞ͱϦεΫ͸ࢴҰॏ