Save 37% off PRO during our Black Friday Sale! »

How to keep data between orientation changes

How to keep data between orientation changes

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

9f4db21673dc0e7cbc313df8310309fb?s=128

Masaki Ogata

April 20, 2017
Tweet

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. ଎͞ͱϦεΫ͸ࢴҰॏ