diff options
Diffstat (limited to 'docs/html/guide')
-rw-r--r-- | docs/html/guide/components/activities.jd | 6 | ||||
-rw-r--r-- | docs/html/guide/components/bound-services.jd | 6 | ||||
-rw-r--r-- | docs/html/guide/components/fragments.jd | 9 | ||||
-rw-r--r-- | docs/html/guide/components/fundamentals.jd | 4 | ||||
-rw-r--r-- | docs/html/guide/components/processes-and-threads.jd | 7 | ||||
-rw-r--r-- | docs/html/guide/components/services.jd | 8 | ||||
-rw-r--r-- | docs/html/guide/components/tasks-and-back-stack.jd | 8 | ||||
-rw-r--r-- | docs/html/guide/guide_toc.cs | 9 | ||||
-rw-r--r-- | docs/html/guide/practices/verifying-apps-art.jd | 296 | ||||
-rw-r--r-- | docs/html/guide/topics/manifest/activity-element.jd | 14 | ||||
-rw-r--r-- | docs/html/guide/topics/media/exoplayer.jd | 514 | ||||
-rw-r--r-- | docs/html/guide/topics/ui/settings.jd | 42 |
12 files changed, 872 insertions, 51 deletions
diff --git a/docs/html/guide/components/activities.jd b/docs/html/guide/components/activities.jd index b4617fb..5e6917b 100644 --- a/docs/html/guide/components/activities.jd +++ b/docs/html/guide/components/activities.jd @@ -4,12 +4,6 @@ page.tags=activity,intent <div id="qv-wrapper"> <div id="qv"> -<h2>Quickview</h2> -<ul> - <li>An activity provides a user interface for a single screen in your application</li> - <li>Activities can move into the background and then be resumed with their state restored</li> -</ul> - <h2>In this document</h2> <ol> <li><a href="#Creating">Creating an Activity</a> diff --git a/docs/html/guide/components/bound-services.jd b/docs/html/guide/components/bound-services.jd index 653c7a0..4215f0f 100644 --- a/docs/html/guide/components/bound-services.jd +++ b/docs/html/guide/components/bound-services.jd @@ -6,12 +6,6 @@ parent.link=services.html <div id="qv-wrapper"> <ol id="qv"> -<h2>Quickview</h2> -<ul> - <li>A bound service allows other components to bind to it, in order to interact with it and -perform interprocess communication</li> - <li>A bound service is destroyed once all clients unbind, unless the service was also started</li> -</ul> <h2>In this document</h2> <ol> <li><a href="#Basics">The Basics</a></li> diff --git a/docs/html/guide/components/fragments.jd b/docs/html/guide/components/fragments.jd index 32c9f99..0cc5f72 100644 --- a/docs/html/guide/components/fragments.jd +++ b/docs/html/guide/components/fragments.jd @@ -5,15 +5,6 @@ parent.link=activities.html <div id="qv-wrapper"> <div id="qv"> - - <h2>Quickview</h2> - <ul> - <li>Fragments decompose application functionality and UI into reusable modules</li> - <li>Add multiple fragments to a screen to avoid switching activities</li> - <li>Fragments have their own lifecycle, state, and back stack</li> - <li>Fragments require API Level 11 or greater</li> - </ul> - <h2>In this document</h2> <ol> <li><a href="#Design">Design Philosophy</a></li> diff --git a/docs/html/guide/components/fundamentals.jd b/docs/html/guide/components/fundamentals.jd index 9ac063e..fd1a7a8 100644 --- a/docs/html/guide/components/fundamentals.jd +++ b/docs/html/guide/components/fundamentals.jd @@ -335,8 +335,8 @@ documentation. </p> {@link android.content.Intent} to start activities, services, and broadcast receivers. You can do so by explicitly naming the target component (using the component class name) in the intent. However, the real power of intents lies in the concept of <em>implicit intents</em>. An implicit intent -simply describe the type of action to perform (and optionally, the data upon which you’d like to -perform the action) and allow the system to find a component on the device that can perform the +simply describes the type of action to perform (and, optionally, the data upon which you’d like to +perform the action) and allows the system to find a component on the device that can perform the action and start it. If there are multiple components that can perform the action described by the intent, then the user selects which one to use.</p> diff --git a/docs/html/guide/components/processes-and-threads.jd b/docs/html/guide/components/processes-and-threads.jd index c8c3764..e7ef7ba 100644 --- a/docs/html/guide/components/processes-and-threads.jd +++ b/docs/html/guide/components/processes-and-threads.jd @@ -5,13 +5,6 @@ page.tags=lifecycle,background <div id="qv-wrapper"> <div id="qv"> -<h2>Quickview</h2> -<ul> - <li>Every application runs in its own process and all components of the application run in that -process, by default</li> - <li>Any slow, blocking operations in an activity should be done in a new thread, to avoid slowing -down the user interface</li> -</ul> <h2>In this document</h2> <ol> diff --git a/docs/html/guide/components/services.jd b/docs/html/guide/components/services.jd index da01d2c..6e22be8 100644 --- a/docs/html/guide/components/services.jd +++ b/docs/html/guide/components/services.jd @@ -3,14 +3,6 @@ page.title=Services <div id="qv-wrapper"> <ol id="qv"> -<h2>Quickview</h2> -<ul> - <li>A service can run in the background to perform work even while the user is in a different -application</li> - <li>A service can allow other components to bind to it, in order to interact with it and -perform interprocess communication</li> - <li>A service runs in the main thread of the application that hosts it, by default</li> -</ul> <h2>In this document</h2> <ol> <li><a href="#Basics">The Basics</a></li> diff --git a/docs/html/guide/components/tasks-and-back-stack.jd b/docs/html/guide/components/tasks-and-back-stack.jd index f818873..e054313 100644 --- a/docs/html/guide/components/tasks-and-back-stack.jd +++ b/docs/html/guide/components/tasks-and-back-stack.jd @@ -5,14 +5,6 @@ parent.link=activities.html <div id="qv-wrapper"> <div id="qv"> -<h2>Quickview</h2> -<ul> - <li>All activities belong to a task</li> - <li>A task contains a collection of activities in the order in which the user interacts with -them</li> - <li>Tasks can move to the background and retain the state of each activity in order for users -to perform other tasks without losing their work</li> -</ul> <h2>In this document</h2> <ol> diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 0a234aa..ea36405 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -373,6 +373,9 @@ <li><a href="<?cs var:toroot ?>guide/topics/media/mediarouteprovider.html"> <span class="en">Media Route Provider</span></a> </li> + <li><a href="<?cs var:toroot ?>guide/topics/media/exoplayer.html"> + <span class="en">ExoPlayer</span></a> + </li> <li><a href="<?cs var:toroot ?>guide/appendix/media-formats.html"> <span class="en">Supported Media Formats</span></a> </li> @@ -564,7 +567,11 @@ <li><a href="<?cs var:toroot ?>guide/practices/tablets-and-handsets.html"> <span class="en">Supporting Tablets and Handsets</span> </a></li> - + <li> + <a href="<?cs var:toroot ?>guide/practices/verifying-apps-art.html"> + <span class="en">Verifying App Behavior on ART</span> + </a> + </li> </ul> </li> diff --git a/docs/html/guide/practices/verifying-apps-art.jd b/docs/html/guide/practices/verifying-apps-art.jd new file mode 100644 index 0000000..8a88222 --- /dev/null +++ b/docs/html/guide/practices/verifying-apps-art.jd @@ -0,0 +1,296 @@ +page.title=Verifying App Behavior on the Android Runtime (ART) +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Quickview</h2> + <ul> + <li>The new Android runtime (ART) is available on some of the newest Android + devices, though all of them currently have Dalvik as the default + runtime.</li> + <li>App developers should make sure their apps are compatible with ART, + especially if you use JNI to run native code or if you use certain tools + that produce non-standard code (such as some obfuscators).</li> + </ul> + + <h2 id="Contents">In this document</h2> + <ol> + <li><a href="#GC_Migration">Addressing Garbage Collection (GC) Issues</a></li> + <li><a href="#JNI_Issues">Preventing JNI Issues</a> + <ol> + <li><a href="#JNI_and_GC">Checking JNI code for garbage-collection + issues</a></li> + <li><a href="#Error_Handling">Error handling</a></li> + <li><a href="#Object_Model_Changes">Object model changes</a></li> + </ol> + </li> + <li><a href="#Stack_Size">Preventing Stack Size Issues</a></li> + <li><a href="#AOT_Fails">Fixing AOT Compilation Issues</a></li> + <li><a href="#Reporting_Problems">Reporting Problems</a></li> + </ol> + <h2>See also</h2> + <ol> + <li><a href="http://source.android.com/devices/tech/dalvik/art.html">Introducing ART</a></li> + <li><a +href="http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html">Debugging +Android JNI with CheckJNI</a></li> + </ol> +</div> +</div> + +<p>With Android 4.4, we are beginning to roll out a new Android runtime, +<strong>ART</strong>. This runtime offers a number of new features that improve +performance and smoothness of the Android platform and apps. (You can find more +information about ART's new features in <a +href="http://source.android.com/devices/tech/dalvik/art.html">Introducing +ART</a>.)</p> + +<p>Currently, ART is available on a number of Android 4.4 devices, such as the +Nexus 4, Nexus 5, Nexus 7, and Google Play edition devices. +At this time, all devices still use Dalvik as the default runtime. We encourage +you to test your apps for ART compatibility and to take advantage of ART's new +features. However, for the time being, you should also take care to maintain +compatibility with Dalvik.</p> + +<p>This document lets you know about things to watch for when migrating an +existing app to be compatible with ART. Most apps should just work when +running with ART. However, some techniques that work on Dalvik do not work on +ART. This document discusses some of these issues.</p> + +<h2 id="GC_Migration">Addressing Garbage Collection (GC) Issues</h2> + +<p>Under Dalvik, apps frequently find it useful to explicitly call {@link +java.lang.System#gc() System.gc()} to prompt garbage collection (GC). This should be +far less necessary with ART, particularly if you're invoking garbage collection +to prevent <a +href="{@docRoot}/tools/debugging/debugging-memory.html#LogMessages"><code>GC_FOR_ALLOC</code></a>-type +occurrences or to reduce fragmentation. You can verify which runtime is in use +by calling {@link java.lang.System#getProperty(java.lang.String) +System.getProperty("java.vm.version")}. If ART is in use, the property's value +is <code>"2.0.0"</code> or higher.</p> + +<p>Furthermore, a compacting garbage collector is under development in the <a +href="https://source.android.com">Android Open-Source Project (AOSP)</a> to +improve memory management. Because of this, you should avoid using techniques +that are incompatible with compacting GC (such as saving pointers to object +instance data). This is particularly important for apps that make use of the +Java Native Interface (JNI). For more information, see <a +href="#JNI_Issues">Preventing JNI Issues</a>.</p> + +<h2 id="JNI_Issues">Preventing JNI Issues</h2> + +<p>ART's JNI is somewhat stricter than Dalvik's. It is an especially good idea +to use CheckJNI mode to catch common problems. If your app makes use of C/C++ +code, you should review the following article:</p> + +<p><a +href="http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html">Debugging +Android JNI with CheckJNI</a></p> + +<h3 id="JNI_and_GC">Checking JNI code for garbage-collection issues</h3> + +<p>ART has a compacting garbage collector under development on the +Android Open Source Project (AOSP). Once the compacting garbage collector is in +use, objects may be moved in memory. If you use C/C++ code, do not +perform operations that are incompatible with compacting GC. We have enhanced +CheckJNI to identify some potential issues (as described in <a +href="http://android-developers.blogspot.com/2011/11/jni-local-reference-changes-in-ics.html">JNI +Local Reference Changes in ICS</a>).</p> + +<p>One area to watch for in particular is the use of +<code>Get...ArrayElements()</code> and <code>Release...ArrayElements()</code> +functions. In runtimes with non-compacting GC, the +<code>Get...ArrayElements()</code> functions typically return a reference to the +actual memory backing the array object. If you make a change to one of the +returned array elements, the array object is itself changed (and the arguments +to <code>Release...ArrayElements()</code> are usually ignored). However, if +compacting GC is in use, the <code>Get...ArrayElements()</code> functions may +return a copy of the memory. If you misuse the reference when compacting GC is +in use, this can lead to memory corruption or other problems. For example:</p> + +<ul> + + <li>If you make any changes to the returned array elements, you must call the + appropriate <code>Release...ArrayElements()</code> function when you are done, + to make sure the changes you made are correctly copied back to the underlying + array object.</li> + + <li>When you release the memory array elements, you must use the appropriate + mode, depending on what changes you made: + + <ul> + + <li>If you did not make any changes to the array elements, use + <code>JNI_ABORT</code> mode, which releases the memory without copying + changes back to the underlying array object.</li> + + <li>If you made changes to the array, and do not need the reference any + more, use code <code>0</code> (which updates the array object and frees + the copy of the memory).</li> + + <li>If you made changes to the array that you want to commit, and you want + to keep the copy of the array, use <code>JNI_COMMIT</code> (which updates + the underlying array object and retains the copy).</li> + + </ul> + + </li> + + <li>When you call <code>Release...ArrayElements()</code>, return the same + pointer that was originally returned by <code>Get...ArrayElements()</code>. For + example, it's not safe to increment the original pointer (to scan through the + returned array elements) then pass the incremented pointer to + <code>Release...ArrayElements()</code>. Passing this modified pointer can cause + the wrong memory to be freed, resulting in memory corruption.</li> + +</ul> + +<h3 id="Error_Handling">Error handling</h3> + +<p>ART's JNI throws errors in a number of cases where Dalvik didn’t. (Once +again, you can catch many such cases by testing with CheckJNI.)</p> + +<p>For example, if <code>RegisterNatives</code> is called with a method that +does not exist (perhaps because the method was removed by a tool such as +<strong>ProGuard</strong>), ART now properly throws {@link +java.lang.NoSuchMethodError}:</p> + +<pre class="no-pretty-print"> +08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main +08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError: + no static or non-static method + "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I" +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.Runtime.nativeLoad(Native Method) +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.Runtime.doLoad(Runtime.java:421) +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.Runtime.loadLibrary(Runtime.java:362) +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.System.loadLibrary(System.java:526) +</pre> + +<p>ART also logs an error (visible in logcat) if <code>RegisterNatives</code> is +called with no methods:</p> + +<pre class="no-pretty-print"> +W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native +methods for <classname> +</pre> + +<p>In addition, the JNI functions <code>GetFieldID()</code> and +<code>GetStaticFieldID()</code> now properly throw {@link java.lang.NoSuchFieldError} +instead of simply returning null. Similarly, <code>GetMethodID()</code> and +<code>GetStaticMethodID()</code> now properly throw {@link java.lang.NoSuchMethodError}. +This can lead to CheckJNI failures because of the unhandled exceptions or the +exceptions being thrown to Java callers of native code. This makes it +particularly important to test ART-compatible apps with CheckJNI mode.</p> + +<p>ART expects users of the JNI <code>CallNonvirtual...Method()</code> methods +(such as <code>CallNonvirtualVoidMethod()</code>) to use the method's declaring +class, not a subclass, as required by the JNI specification.</p> + +<h2 id="Stack_Size">Preventing Stack Size Issues</h2> + +<p>Dalvik had separate stacks for native and Java code, with a default Java +stack size of 32KB and a default native stack size of 1MB. ART has a unified +stack for better locality. Ordinarily, the ART {@link java.lang.Thread} stack +size should be approximately the same as for Dalvik. However, if you explicitly +set stack sizes, you may need to revisit those values for apps running in +ART.</p> + +<ul> + + <li>In Java, review calls to the {@link + java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable, + java.lang.String, long) Thread} constructor that specify an explicit stack + size. For example, you will need to increase the size if {@link + java.lang.StackOverflowError} occurs.</li> + + <li>In C/C++, review use of <code>pthread_attr_setstack()</code> and + <code>pthread_attr_setstacksize()</code> for threads that also run Java code via + JNI. Here is an example of the error logged when an app attempts to call JNI + <code>AttachCurrentThread()</code> when the pthread size is too small: + +<pre class="no-pretty-print">F/art: art/runtime/thread.cc:435] + Attempt to attach a thread with a too-small stack (16384 bytes)</pre> + </li> + +</ul> + +<h2 id="Object_Model_Changes">Object model changes</h2> + +<p>Dalvik incorrectly allowed subclasses to override package-private methods. +ART issues a warning in such cases:</p> + +<pre class="no-pretty-print"> +Before Android 4.1, method void com.foo.Bar.quux() +would have incorrectly overridden the package-private method in +com.quux.Quux +</pre> + +<p>If you intend to override a class's method in a different package, declare the +method as <code>public</code> or <code>protected</code>.</p> + +<p>{@link java.lang.Object} now has private fields. Apps that reflect on fields +in their class hierarchies should be careful not to attempt to look at the +fields of {@link java.lang.Object}. For example, if you are iterating up a class +hierarchy as part of a serialization framework, stop when + +<pre>Class.getSuperclass() == java.lang.Object.class</pre> + +instead of continuing until the method returns <code>null</code>.</p> + +<p>Proxy {@link +java.lang.reflect.InvocationHandler#invoke(java.lang.Object,java.lang.reflect.Method,java.lang.Object[]) +InvocationHandler.invoke()} now receives <code>null</code> if there are no +arguments instead of an empty array. This behavior was documented previously but +not correctly handled in Dalvik. Previous versions of <a +href="https://code.google.com/p/mockito/">Mockito</a> have difficulties with +this, so use an updated Mockito version when testing with ART.</p> + +<h2 id="AOT_Fails">Fixing AOT Compilation Issues</h2> + +<p>ART's Ahead-Of-Time (AOT) Java compilation should work for all standard Java +code. Compilation is performed by ART's +<code>dex2oat</code> tool; if you encounter any issues related to +<code>dex2oat</code> at install time, let us know (see <a +href="#Reporting_Problems">Reporting Problems</a>) so we can fix them as quickly +as possible. A couple of issues to note:</p> + +<ul> + + <li>ART does tighter bytecode verification at install time than Dalvik does. + Code produced by the Android build tools should be fine. However, some + post-processing tools (especially tools that perform obfuscation) may produce + invalid files that are tolerated by Dalvik but rejected by ART. We have been + working with tool vendors to find and fix such issues. In many cases, getting + the latest versions of your tools and regenerating the DEX files can fix these + problems.</li> + + <li>Some typical problems that are flagged by the ART verifier include: + <ul> + <li>invalid control flow</li> + <li>unbalanced <code>moniterenter</code>/<code>moniterexit</code></li> + <li>0-length parameter type list size</li> + </ul> + </li> + + <li>Some apps have dependencies on the installed <code>.odex</code> file + format in <code>/system/framework</code>, <code>/data/dalvik-cache</code>, or + in {@link dalvik.system.DexClassLoader}’s optimized output directory. These + files are now ELF files and not an extended form of DEX files. While ART tries + to follow the same naming and locking rules as Dalvik, apps should not depend + on the file format; the format is subject to change without notice.</li> + + + +<h2 id="Reporting_Problems">Reporting Problems</h2> + +<p>If you run into any issues that aren’t due to app JNI issues, report +them via the Android Open Source Project Issue Tracker at <a +href="https://code.google.com/p/android/issues/list">https://code.google.com/p/android/issues/list</a>. +Include an <code>"adb bugreport"</code> and a link to the app in the Google +Play store if available. Otherwise, if possible, attach an APK that reproduces +the issue. Note that issues (including attachments) are publicly +visible.</p> diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index b648d48..c4d5083 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -5,7 +5,8 @@ parent.link=manifest-intro.html <dl class="xml"> <dt>syntax:</dt> -<dd><pre class="stx"><activity android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"] +<dd><pre class="stx"><activity android:<a href="#embedded">allowEmbedded</a>=["true" | "false"] + android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"] android:<a href="#always">alwaysRetainTaskState</a>=["true" | "false"] android:<a href="#clear">clearTaskOnLaunch</a>=["true" | "false"] android:<a href="#config">configChanges</a>=["mcc", "mnc", "locale", @@ -62,6 +63,17 @@ by the system and will never be run. <dt>attributes:</dt> <dd><dl class="attr"> +<dt><a name="embedded"></a>{@code android:allowEmbedded}</dt> +<dd> + Indicate that the activity can be launched as the embedded child of another + activity. Particularly in the case where the child lives in a container + such as a Display owned by another activity. For example, activities + that are used for Wear custom notifications must declare this so + Wear can display the activity in it's context stream, which resides + in another process. + + <p>The default value of this attribute is <code>false</code>. +</dd> <dt><a name="reparent"></a>{@code android:allowTaskReparenting}</dt> <dd>Whether or not the activity can move from the task that started it to the task it has an affinity for when that task is next brought to the diff --git a/docs/html/guide/topics/media/exoplayer.jd b/docs/html/guide/topics/media/exoplayer.jd new file mode 100644 index 0000000..17b4669 --- /dev/null +++ b/docs/html/guide/topics/media/exoplayer.jd @@ -0,0 +1,514 @@ +page.title=ExoPlayer +page.tags="audio","video","adaptive","streaming","DASH","smoothstreaming" +@jd:body + +<div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + <ol> + <li><a href="#overview">Overview</a></li> + <li><a href="#trackrenderer">TrackRenderer</a></li> + <li><a href="#samplesource">SampleSource</a> + <ol> + <li><a href="#mediaextractor">Providing media using MediaExtractor</a></li> + <li><a href="#adaptive-playback">Providing media for adaptive playback</a> + <ol> + <li><a href="#format-selection">Format selection for adaptive playback</a></li> + </ol> + </li> + </ol> + <li><a href="#events">Player Events</a> + <ol> + <li><a href="#high-events">High level events</a></li> + <li><a href="#low-events">Low level events</a></li> + </ol> + </li> + <li><a href="#sending-messages">Sending messages to components</a></li> + <li><a href="#customizing">Customizing ExoPlayer</a> + <ol> + <li><a href="#custom-guidelines">Custom component guidelines</a></li> + </ol> + </li> + <li><a href="#drm">Digital Rights Management</a></li> + </ol> + <h2>Key Classes</h2> + <ol> + <li>{@link android.media.MediaCodec}</li> + <li>{@link android.media.MediaExtractor}</li> + <li>{@link android.media.AudioTrack}</li> + </ol> + <h2>Related Samples</h2> + <ol> + <li><a class="external-link" href="https://github.com/google/ExoPlayer"> + ExoPlayer Project</a></li> + <li><a class="external-link" href="http://google.github.io/ExoPlayer/doc/reference/packages.html"> + Class Reference</a></li> + </ol> + </div> +</div> + + +<p>Playing videos and music is a popular activity on Android devices. The Android framework + provides {@link android.media.MediaPlayer} as a quick solution for playing media with minimal + code, and the {@link android.media.MediaCodec} and {@link android.media.MediaExtractor} classes + are provided for building custom media players. The open source project, ExoPlayer, is a + solution between these two options, providing a pre-built player that you can extend.</p> + +<p>ExoPlayer supports features not currently provided by + {@link android.media.MediaPlayer}, including Dynamic adaptive streaming + over HTTP (DASH), SmoothStreaming, and persistent caching. ExoPlayer can be extended + to handle additional media formats, and because you include it as part of your app code, + you can update it along with your app.</p> + +<p>This guide describes how to use ExoPlayer for playing Android supported media formats, as well as + DASH and SmoothStreaming playback. This guide also discusses ExoPlayer events, messages, DRM + support and guidelines for customizing the player.</p> + +<p class="note"> + <strong>Note:</strong> ExoPlayer is an open source project that is not part of the Android + framework and is distributed separately from the Android SDK. The project contains a library and + a demo app that shows both simple and more advanced use of ExoPlayer:</p> + +<ul> + <li><a class="external-link" href="https://github.com/google/ExoPlayer/tree/master/library"> + ExoPlayer Library</a> — This part of the project contains the core library classes.</li> + <li><a class="external-link" href="https://github.com/google/ExoPlayer/tree/master/demo/src/main/java/com/google/android/exoplayer/demo/simple"> + Simple Demo</a> — This part of the app demonstrates a basic use of ExoPlayer.</li> + <li><a class="external-link" href="https://github.com/google/ExoPlayer/tree/master/demo/src/main/java/com/google/android/exoplayer/demo/full"> + Full Demo</a> — This part of the app demonstrates more advanced features, + including the ability to select between multiple audio tracks, a background audio mode, + event logging and DRM protected playback. </li> +</ul> + + +<h2 id="overview">Overview</h2> + +<p>ExoPlayer is a media player built on top of the {@link android.media.MediaExtractor} and + {@link android.media.MediaCodec} APIs released in Android 4.1 (API level 16). At the core of this + library is the {@code ExoPlayer} class. This class maintains the player’s global state, but makes few + assumptions about the nature of the media being played, such as how the media data is obtained, + how it is buffered or its format. You inject this functionality through ExoPlayer’s {@code + prepare()} method in the form of {@code TrackRenderer} objects.</p> + +<p>ExoPlayer provides default {@code TrackRenderer} implementations for audio and + video, which make use of the {@link android.media.MediaCodec} and {@link android.media.AudioTrack} + classes in the Android framework. Both renderers require a {@code SampleSource} object, from which + they obtain individual media samples for playback. Figure 1 shows the high level object model for + an ExoPlayer implementation configured to play audio and video using these components.</p> + +<img src="{@docRoot}images/exoplayer/object-model.png" alt="" id="figure1" /> +<p class="img-caption"> + <strong>Figure 1.</strong> High level object model for an ExoPlayer configured to play audio + and video using {@code TrackRenderer} objects +</p> + + +<h2 id="trackrenderer">TrackRenderer</h2> + +<p>A {@code TrackRenderer} processes a component of media for playback, such as + video, audio or text. The ExoPlayer class invokes methods on its {@code TrackRenderer} instances from a + single playback thread, and by doing so causes each media component to be rendered as the global + playback position is advanced. The ExoPlayer library provides {@code MediaCodecVideoTrackRenderer} as + the default implementations rendering video and {@code MediaCodecAudioTrackRenderer} for audio. + Both implementations make use of {@link android.media.MediaCodec} to decode individual media + samples. They can handle all audio and video formats supported by a given Android device + (see <a href="http://developer.android.com/guide/appendix/media-formats.html">Supported Media + Formats</a> for details). The ExoPlayer library also provides an implementation for rendering + text called {@code TextTrackRenderer}. +</p> + +<p>The code example below outlines the main steps required to instantiate an ExoPlayer to play video + and audio using the standard {@code TrackRenderer} implementations.</p> + +<pre> +// 1. Instantiate the player. +player = ExoPlayer.Factory.newInstance(RENDERER_COUNT); +// 2. Construct renderers. +MediaCodecVideoTrackRenderer videoRenderer = … +MediaCodecAudioTrackRenderer audioRenderer = ... +// 3. Inject the renderers through prepare. +player.prepare(videoRenderer, audioRenderer); +// 4. Pass the surface to the video renderer. +player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, + surface); +// 5. Start playback. +player.setPlayWhenReady(true); +... +player.release(); // Don’t forget to release when done! +</pre> + +<p>For a complete example, see the {@code SimplePlayerActivity} in the ExoPlayer demo app, which + correctly manages an ExoPlayer instance with respect to both the {@link android.app.Activity} and + {@link android.view.Surface} lifecycles.</p> + + +<h2 id="samplesource">SampleSource</h2> + +<p>A standard {@code TrackRenderer} implementation requires a {@code SampleSource} to + be provided in its constructor. A {@code SampleSource} object provides format information and + media samples to be rendered. The ExoPlayer library provides {@code FrameworkSampleSource} and + {@code ChunkSampleSource}. The {@code FrameworkSampleSource} class uses {@link + android.media.MediaExtractor} to request, buffer and extract the media samples. The {@code + ChunkSampleSource} class provides adaptive playback using DASH or SmoothStreaming, and + implements networking, buffering and media extraction within the ExoPlayer library.</p> + + +<h3 id="mediaextractor">Providing media using MediaExtractor</h3> + +<p> + In order to render media formats supported by the Android framework, the {@code + FrameworkSampleSource} class uses {@link android.media.MediaExtractor} for networking, + buffering and sample extraction functionality. By doing so, it supports any media container format + supported by the version of Android where it is running. For more information about media formats + supported by Android, see <a href="{@docRoot}guide/appendix/media-formats.html">Supported + Media Formats</a>. +</p> + +<p>The diagram in Figure 2 shows the object model for an ExoPlayer implementation using + {@code FrameworkSampleSource}.</p> + +<img src="{@docRoot}images/exoplayer/frameworksamplesource.png" alt="" id="figure2" /> +<p class="img-caption"> + <strong>Figure 2.</strong> Object model for an implementation of ExoPlayer that renders + media formats supported by Android using {@code FrameworkSampleSource} +</p> + +<p>The following code example outlines how the video and audio renderers are constructed to + load the video from a specified URI.</p> + +<pre> +FrameworkSampleSource sampleSource = new FrameworkSampleSource( + activity, uri, null, 2); +MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer( + sampleSource, null, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 0, + mainHandler, playerActivity, 50); +MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer( + sampleSource, null, true); +</pre> + +<p>The ExoPlayer demo app provides a complete implementation of this code in + {@code DefaultRendererBuilder}. The {@code SimplePlaybackActivity} class uses it to play one + of the videos available in the demo app. Note that in the example, video and audio + are muxed, meaning they are streamed together from a single URI. The {@code FrameworkSampleSource} + instance provides video samples to the {@code videoRenderer} object and audio samples to the + {@code audioRenderer} object as they are extracted from the media container format. It is also + possible to play demuxed media, where video and audio are streamed separately from different URIs. + This functionality can be achieved by having two {@code FrameworkSampleSource} instances instead + of one.</p> + + +<h3 id="adaptive-playback">Providing media for adaptive playback</h3> + +<p>ExoPlayer supports adaptive streaming, which allows the quality of the + media data to be adjusted during playback based on the network conditions. DASH + and SmoothStreaming are examples of adaptive streaming technologies. Both these approaches + load media in small chunks (typically 2 to 10 seconds in duration). Whenever a chunk of media + is requested, the client selects from a number of possible formats. For example, a client may + select a high quality format if network conditions are good, or a low quality format if network + conditions are bad. In both techniques, video and audio are streamed separately.</p> + +<p>ExoPlayer supports adaptive playback through use of the {@code ChunkSampleSource} class, + which loads chunks of media data from which individual samples can be extracted. Each {@code + ChunkSampleSource} requires a {@code ChunkSource} object to be injected through its constructor, + which is responsible for providing media chunks from which to load and read samples. The {@code + DashMp4ChunkSource} and {@code SmoothStreamingChunkSource} classes provide DASH and SmoothStreaming + playback using the FMP4 container format. The {@code DashWebMChunkSource} class uses the WebM + container format to provide DASH playback.</p> + +<p>All of the standard {@code ChunkSource} implementations require a {@code FormatEvaluator} and + a {@code DataSource} to be injected through their constructors. The {@code FormatEvaluator} + objects select from the available formats before each chunk is loaded. The {@code DataSource} + objects are responsible for actually loading the data. Finally, the {@code ChunkSampleSources} + require a {@code LoadControl} object that controls the chunk buffering policy.</p> + +<p>The object model of an ExoPlayer configured for a DASH adaptive playback is shown in the + diagram below. This example uses an {@code HttpDataSource} object to stream the media over the + network. The video quality is varied at runtime using the adaptive implementation of {@code + FormatEvaluator}, while audio is played at a fixed quality level.</p> + +<img src="{@docRoot}images/exoplayer/adaptive-streaming.png" alt="" id="figure3" /> +<p class="img-caption"> + <strong>Figure 3.</strong> Object model for a DASH adaptive playback using ExoPlayer +</p> + +<p>The following code example outlines how the video and audio renderers are constructed.</p> + +<pre> +Handler mainHandler = playerActivity.getMainHandler(); +LoadControl loadControl = new DefaultLoadControl( + new BufferPool(BUFFER_SEGMENT_SIZE)); +BandwidthMeter bandwidthMeter = new BandwidthMeter(); + +// Build the video renderer. +DataSource videoDataSource = new HttpDataSource(userAgent, + HttpDataSource.REJECT_PAYWALL_TYPES, bandwidthMeter); +ChunkSource videoChunkSource = new DashMp4ChunkSource(videoDataSource, + new AdaptiveEvaluator(bandwidthMeter), videoRepresentations); +ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, + loadControl, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true); +MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer( + videoSampleSource, null, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, + 0, mainHandler, playerActivity, 50); + +// Build the audio renderer. +DataSource audioDataSource = new HttpDataSource(userAgent, + HttpDataSource.REJECT_PAYWALL_TYPES, bandwidthMeter); +ChunkSource audioChunkSource = new DashMp4ChunkSource(audioDataSource, + new FormatEvaluator.FixedEvaluator(), audioRepresentation); +SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, + loadControl, AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true); +MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer( + audioSampleSource, null, true); +</pre> + +<p>In this code, {@code videoRepresentations} and {@code audioRepresentation} are {@code + Representation} objects, each of which describes one of the available media streams. In the DASH + model, these streams are parsed from a media presentation description (MPD) file. The ExoPlayer + library provides a {@code MediaPresentationDescriptionParser} class to obtain {@code + Representation} objects from MPD files.</p> + +<p class="note"> + <strong>Note:</strong> Building Representation objects from MPD files is not required. You can + build Representation objects from other data sources if necessary. +</p> + +<p>The ExoPlayer demo app provides complete implementation of this code in + {@code DashVodRendererBuilder}. The {@code SimplePlaybackActivity} class uses this builder to + construct renderers for playing DASH sample videos in the demo app. It asynchronously fetches a + specified MPD file in order to construct the required {@code Representation} objects. For an + equivalent SmoothStreaming example, see the {@code SmoothStreamingRendererBuilder} class in the + demo app.</p> + + +<h4 id="format-selection">Format selection for adaptive playback</h4> + +<p>For DASH and SmoothStreaming playback, consider both static format selection at the + start of playback and dynamic format selection during playback. Static format selection should be + used to filter out formats that should not be used throughout the playback, for example formats + with resolutions higher than the maximum supported by the playback device. Dynamic selection varies + the selected format during playback, typically to adapt video quality in response to changes in + network conditions.</p> + +<h5 id="static-selection">Static format selection</h5> + +<p>When preparing a player, you should consider filtering out some of the available formats if + they are not useable for playback. Static format selection allows you to filter out + formats that cannot be used on a particular device or are not compatible with your player. + For audio playback, this often means picking a single format to play and discarding the others.</p> + +<p>For video playback, filtering formats can be more complicated. Apps should first + eliminate any streams that whose resolution is too high to be played by the device. For H.264, + which is normally used for DASH and SmoothStreaming playback, ExoPlayer’s {@code MediaCodecUtil} + class provides a {@code maxH264DecodableFrameSize()} method that can be used to determine what + resolution streams the device is able to handle, as shown in the following code example:</p> + +<pre> +int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize(); +Format format = representation.format; +if (format.width * format.height <= maxDecodableFrameSize) { + // The device can play this stream. + videoRepresentations.add(representation); +} else { + // The device isn't capable of playing this stream. +} +</pre> + +<p>This approach is used to filter {@code Representations} in the {@code DashVodRendererBuilder} + class of the ExoPlayer demo app, and similarly to filter track indices in {@code + SmoothStreamingRendererBuilder}.</p> + +<p>In addition to eliminating unsupported formats, it should be noted that the ability to + seamlessly switch between H.264 streams of different resolution is an optional decoder feature + available in Android 4.3 (API level 16) and higher, and so is not supported by all devices. The + availability of an adaptive H.264 decoder can be queried using {@code MediaCodecUtil}, as shown in + the following code example:</p> + +<pre> +boolean isAdaptive = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264).adaptive; +</pre> + +<p>The {@code MediaCodecVideoTrackRenderer} class is still able to handle resolution changes on + devices that do not have adaptive decoders, however the switch is not seamless. Typically, the + switch creates a small discontinuity in visual output lasting around 50-100ms. For devices that + do not provide an adaptive decoder, app developers may choose to adapt between formats at + a single fixed resolution so as to avoid discontinuities. The ExoPlayer demo app + implementation does not pick a fixed resolution.</p> + + +<h5 id="dynamic-selection">Dynamic format selection</h5> + +<p>During playback, you can use a {@code FormatEvaluator} to dynamically select from the + available video formats. The ExoPlayer library provides a {@code FormatEvaluator.Adaptive} + implementation for dynamically selecting between video formats based on the current network + conditions.</p> + +<p>This class provides a simple, general purpose reference implementation, however you are + encouraged to write your own {@code FormatEvaluator} implementation to best suit your particular + needs.</p> + + +<h2 id="events">Player Events</h2> + +<p>During playback, your app can listen for events generated by the ExoPlayer that indicate the + overall state of the player. These events are useful as triggers for updating the app user + interface such as playback controls. Many ExoPlayer components also report their own component + specific low level events, which can be useful for performance monitoring.</p> + + +<h3 id="high-events">High level events</h3> + +<p>ExoPlayer allows instances of {@code ExoPlayer.Listener} to be added and removed using its + {@code addListener()} and {@code removeListener()} methods. Registered listeners are notified of + changes in playback state, as well as when errors occur that cause playback to fail. For more + information about the valid playback states and the possible transitions between them, see the + ExoPlayer source code.</p> + +<p>Developers who implement custom playback controls should register a listener and use it to + update their controls as the player’s state changes. An app should also show an + appropriate error to the user if playback fails.</p> + +<h3 id="low-events">Low level events</h3> + +<p>In addition to high level listeners, many of the individual components provided by the + ExoPlayer library allow their own event listeners. For example, {@code + MediaCodecVideoTrackRenderer} has constructors that take a {@code + MediaCodecVideoTrackRenderer.EventListener}. In the ExoPlayer demo app, {@code SimplePlayerActivity} + acts as a listener so that it can adjust the dimensions of the target surface to have the correct + height and width ratio for the video being played:</p> + +<pre> +@Override +public void onVideoSizeChanged(int width, int height) { + surfaceView.setVideoWidthHeightRatio(height == 0 ? 1 : (float) width / height); +} +</pre> + +<p>The {@code RendererBuilder} classes in the ExoPlayer demo app inject the activity as the + listener, for example in the {@code DashVodRendererBuilder} class:</p> + +<pre> +MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer( + videoSampleSource, null, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, + 0, <strong>mainHandler, playerActivity</strong>, 50); +</pre> + +<p>Note that you must pass a {@link android.os.Handler} object to the renderer, which determines + the thread on which the listener’s methods are invoked. In most cases, you should use a + {@link android.os.Handler} associated with the app’s main thread, as is the case in this example. + </p> + +<p>Listening to individual components can be useful for adjusting UI based on player events, as + in the example above. Listening to component events can also be helpful for logging performance + metrics. For example, {@code MediaCodecVideoTrackRenderer} notifies its listener of dropped video + frames. A developer may wish to log such metrics to track playback performance in their + app.</p> + +<p>Many components also notify their listeners when errors occur. Such errors may or may not + cause playback to fail. If an error does not cause playback to fail, it may still result in + degraded performance, and so you may wish to log all errors in order to track playback + performance. Note that an ExoPlayer instance always notifies its high level listeners of errors that + cause playback to fail, in addition to the listener of the individual component from which the error + originated. Hence, you should display error messages to users only from high level listeners. + Within individual component listeners, you should use error notifications only for informational + purposes.</p> + + +<h2 id="sending-messages">Sending messages to components</h2> + +<p>Some ExoPlayer components allow changes in configuration during playback. By convention, you make + these changes by passing asynchronous messages through the ExoPlayer to the component. + This approach ensures both thread safety and that the configuration change is + executed in order with any other operations being performed on the player.</p> + +<p>The most common use of messaging is passing a target surface to + {@code MediaCodecVideoTrackRenderer}:</p> + +<pre> +player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, + surface); +</pre> + +<p>Note that if the surface needs to be cleared because + {@link android.view.SurfaceHolder.Callback#surfaceDestroyed + SurfaceHolder.Callback.surfaceDestroyed()} has been invoked, then you must send this + message using the blocking variant of {@code sendMessage()}:</p> +<p> + +<pre> +player.blockingSendMessage(videoRenderer, + MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, null); +</pre> + +<p>You must use a blocking message because the contract of {@link + android.view.SurfaceHolder.Callback#surfaceDestroyed surfaceDestroyed()} requires that the + app does not attempt to access the surface after the method returns. The {@code + SimplePlayerActivity} class in the demo app demonstrates how the surface should be set and + cleared.</p> + + +<h2 id="customizing">Customizing ExoPlayer</h2> + +<p>One of the main benefits of ExoPlayer over {@link android.media.MediaPlayer} is the ability to + customize and extend the player to better suit the developer’s use case. The ExoPlayer library + is designed specifically with this in mind, defining a number of abstract base classes and + interfaces that make it possible for app developers to easily replace the default implementations + provided by the library. Here are some use cases for building custom components:</p> + +<ul> + <li><strong>{@code TrackRenderer}</strong> - You may want to implement a custom + {@code TrackRenderer} to handle media types other than audio and video. The {@code + TextTrackRenderer} class within the ExoPlayer library is an example of how to implement a + custom renderer. You could use the approach it demonstrates to render custom + overlays or annotations. Implementing this kind of functionality as a {@code TrackRenderer} + makes it easy to keep the overlays or annotations in sync with the other media being played.</li> + <li><strong>{@code SampleSource}</strong> - If you need to support a container format not + already handled by {@link android.media.MediaExtractor} or ExoPlayer, consider implementing a + custom {@code SampleSource} class.</li> + <li><strong>{@code FormatEvaluator}</strong> - The ExoPlayer library provides {@code + FormatEvaluator.Adaptive} as a simple reference implementation that switches between different + quality video formats based on the available bandwidth. App developers are encouraged to + develop their own adaptive {@code FormatEvaluator} implementations, which can be designed to + suit their use specific needs.</li> + <li><strong>{@code DataSource}</strong> - ExoPlayer’s upstream package already contains a + number of {@code DataSource} implementations for different use cases, such as writing and + reading to and from a persistent media cache. You may want to implement you own + {@code DataSource} class to load data in another way, such as a custom + protocol or HTTP stack for data input.</li> +</ul> + + +<h3 id="custom-guidelines">Custom component guidelines</h3> + +<p>If a custom component needs to report events back to the app, we recommend that you + do so using the same model as existing ExoPlayer components, where an event listener is passed + together with a {@link android.os.Handler} to the constructor of the component.</p> + +<p>We recommended that custom components use the same model as existing ExoPlayer components to + allow reconfiguration by the app during playback, as described in + <a href="#sending-messages">Sending messages to components</a>. + To do this, you should implement a {@code ExoPlayerComponent} and receive + configuration changes in its {@code handleMessage()} method. Your app should pass + configuration changes by calling ExoPlayer’s {@code sendMessage()} and {@code + blockingSendMessage()} methods.</p> + + +<h2 id="drm">Digital Rights Management</h2> + +<p>On Android 4.3 (API level 18) and higher, ExoPlayer supports Digital Rights Managment (DRM) + protected playback. In order to play DRM protected content with ExoPlayer, your app must + inject a {@code DrmSessionManager} into the {@code MediaCodecVideoTrackRenderer} and {@code + MediaCodecAudioTrackRenderer} constructors. A {@code DrmSessionManager} object is responsible for + providing the {@code MediaCrypto} object required for decryption, as well as ensuring that the + required decryption keys are available to the underlying DRM module being used.</p> + +<p>The ExoPlayer library provides a default implementation of {@code DrmSessionManager}, called + {@code StreamingDrmSessionManager}, which uses {@link android.media.MediaDrm}. The session + manager supports any DRM scheme for which a modular DRM component exists on the device. All + Android devices are required to support Widevine modular DRM (with L3 security, although many + devices also support L1). Some devices may support additional schemes such as PlayReady.</p> + +<p>The {@code StreamingDrmSessionManager} class requires a {@code MediaDrmCallback} to be + injected into its constructor, which is responsible for actually making provisioning and key + requests. You should implement this interface to make network requests to your license + server and obtain the required keys. The {@code WidevineTestMediaDrmCallback} class in the + ExoPlayer demo app sends requests to a Widevine test server.</p> diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd index 1d36430..f454c4e 100644 --- a/docs/html/guide/topics/ui/settings.jd +++ b/docs/html/guide/topics/ui/settings.jd @@ -820,7 +820,8 @@ public class SettingsActivity extends PreferenceActivity public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType"; ... - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { if (key.equals(KEY_PREF_SYNC_CONN)) { Preference connectionPref = findPreference(key); // Set summary to be the user-description for the selected value @@ -863,7 +864,40 @@ protected void onPause() { } </pre> +<p class="caution"><strong>Caution:</strong> When you call {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}, the preference manager does not +currently store a strong reference to the listener. You must store a strong +reference to the listener, or it will be susceptible to garbage collection. We +recommend you keep a reference to the listener in the instance data of an object +that will exist as long as you need the listener.</p> + +<p>For example, in the following code, the caller does not keep a reference to +the listener. As a result, the listener will be subject to garbage collection, +and it will fail at some indeterminate time in the future:</p> + +<pre> +prefs.registerOnSharedPreferenceChangeListener( + // Bad! The listener is subject to garbage collection! + new SharedPreferences.OnSharedPreferenceChangeListener() { + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + // listener implementation + } +}); +</pre> + +<p>Instead, store a reference to the listener in an instance data field of an +object that will exist as long as the listener is needed:</p> +<pre> +SharedPreferences.OnSharedPreferenceChangeListener listener = + new SharedPreferences.OnSharedPreferenceChangeListener() { + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + // listener implementation + } +}; +prefs.registerOnSharedPreferenceChangeListener(listener); +</pre> <h2 id="NetworkUsage">Managing Network Usage</h2> @@ -1142,13 +1176,15 @@ protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); // Check whether this Preference is persistent (continually saved) if (isPersistent()) { - // No need to save instance state since it's persistent, use superclass state + // No need to save instance state since it's persistent, + // use superclass state return superState; } // Create instance of custom BaseSavedState final SavedState myState = new SavedState(superState); - // Set the state's value with the class member that holds current setting value + // Set the state's value with the class member that holds current + // setting value myState.value = mNewValue; return myState; } |