You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by rk...@apache.org on 2015/12/02 19:20:56 UTC

docs commit: CB-8917: Added docs for Android lifecycle considerations

Repository: cordova-docs
Updated Branches:
  refs/heads/master fc483207b -> d7e985235


CB-8917: Added docs for Android lifecycle considerations


Project: http://git-wip-us.apache.org/repos/asf/cordova-docs/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-docs/commit/d7e98523
Tree: http://git-wip-us.apache.org/repos/asf/cordova-docs/tree/d7e98523
Diff: http://git-wip-us.apache.org/repos/asf/cordova-docs/diff/d7e98523

Branch: refs/heads/master
Commit: d7e985235035f8fb164c3c738c6229b336329b28
Parents: fc48320
Author: riknoll <ri...@gmail.com>
Authored: Tue Dec 1 15:59:52 2015 -0800
Committer: riknoll <ri...@gmail.com>
Committed: Tue Dec 1 15:59:52 2015 -0800

----------------------------------------------------------------------
 .../en/dev/guide/platforms/android/lifecycle.md | 125 +++++++++++++++++++
 .../en/dev/guide/platforms/android/plugin.md    | 109 +++++++++++++++-
 www/docs/en/dev/guide/platforms/index.md        |   1 +
 3 files changed, 232 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-docs/blob/d7e98523/www/docs/en/dev/guide/platforms/android/lifecycle.md
----------------------------------------------------------------------
diff --git a/www/docs/en/dev/guide/platforms/android/lifecycle.md b/www/docs/en/dev/guide/platforms/android/lifecycle.md
new file mode 100644
index 0000000..ab29d92
--- /dev/null
+++ b/www/docs/en/dev/guide/platforms/android/lifecycle.md
@@ -0,0 +1,125 @@
+---
+license: >
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+title: Android Lifecycle Guide
+---
+
+# Android Lifecycle Guide
+
+Native Android apps typically consist of a series of "activities" that the user interacts with. Activities can be thought of as the individual screens that make up an application; different tasks in an app will often have their own activity. Each activity has a lifecycle, and can be created and destroyed as the OS sees fit.
+
+In contrast, Cordova applications on the Android platform are executed within a Webview that is embedded in a *single* Android activity. The lifecycle of this activity is exposed to your application through the document events that are fired. These events are not guaranteed to line up with Android's lifecycle, but they can provide guidelines for saving and restoring your state. These events roughly map to Android callbacks as follows:
+
+Cordova Event   | Rough Android Equivalent
+----------------|-----------------------------------
+`deviceready`   | `onCreate()`
+`pause`         | `onPause()`
+`resume`        | `onResume()`
+
+Often, Cordova applications are confined to the single activity that contains the Webview. However, there are instances in which other activities may be launched that push the Cordova activity to the background. It is important in these cases to be aware of the Android lifecycle and properly maintain your application's state by respecting it.
+
+## Low memory and the Activity lifecycle
+
+Plugins have the ability to launch activities beyond the Cordova activity in order to perform some tasks. For example, the Apache camera plugin, cordova-plugin-camera, launches the device's camera activity in order to take photos.
+
+The flow of events in this case looks something like this:
+
+1. The user is interacting with your app and needs to take a picture
+2. The camera plugin launches the native camera activity
+    * *The Cordova activity is pushed to the background (`pause` event is fired)*
+3. The user takes a photo
+4. The camera activity finishes
+    * *The Cordova activity is moved to the foreground (`resume` event is fired)*
+5. The user is returned to your application where they left off
+
+However, this flow of events can be disrupted if a device is low on memory. The Android OS will often kill background activities in order to free up memory if necessary. Unfortunately, when the activity holding your application is killed, the Webview in which your application lives will be destroyed as well. Any state that your application is maintaining will be lost in this case. When the user navigates back to your the application, the Activity and Webview will be recreated by the OS, but state will not be automatically restored.
+
+If state is not properly saved when the Activity is destroyed, the above sequence of events instead plays out as follows:
+
+1. The user is interacting with your app and needs to take a picture
+2. The camera plugin launches the native camera activity
+    * *The OS destroys the Cordova activity (`pause` event is fired)*
+3. The user takes a photo
+4. The camera activity finishes
+    * *The OS recreates the Cordova activity (`deviceready` and `resume` events are fired)*
+5. The user is confused as to why they are suddenly back at your app's login screen
+
+In this instance, the photo that was taken is lost and the user is given the confusing and frustrating experience of having your application appear to randomly restart. The key to preventing this experience is subscribing to events and properly maintaining state as part of the activity lifecycle.
+
+## Maintaining state
+
+In the examples above, the javascript events that are fired are noted in italics. These events are your opportunity to save and restore your application's state. You should register callbacks in your application's `bindEvents` function that respond to the lifecycle events by saving state. What information you save and how you save it is left to your discretion, but you should be sure to save enough information so that you can restore the user to exactly where they left off when they return to your application.
+
+## Testing the Activity Lifecycle
+
+Android provides a developer setting for debugging Activity destruction on low memory. Enable the "Don't keep activities" setting in the Developer Options menu on your device or emulator to simulate low memory scenarios. If your application launches external activities, you should always do some testing with this setting enabled to ensure that you are properly handling low memory scenarios.
+
+## Retrieving plugin callback results
+
+When the OS destroys the Cordova activity in the above example, any pending callbacks are lost as well. This means that if you passed a callback to the plugin that launched the new activity (e.g. cordova-plugin-camera), that callback will NOT be fired when the application is recreated. However, there is a way for plugins to pass the result of this call to your application. The `resume` event's payload will contain any pending plugin results from the plugin request that launched the external activity made prior to the activity being destroyed.
+
+The payload for the `resume` event adheres to the following format:
+
+```
+{
+    action: "resume",
+    pendingResult: {
+        pluginServiceName: <name of the plugin e.g. "Camera">,
+        pluginStatus: <description of the result's status (see below)>,
+        result: <argument(s) that would have been given to the callback>
+    }
+}
+```
+
+The possible plugin statuses in the `pendingResult` field include the following values:
+
+```
+"No result"
+"OK"
+"Class not found"
+"Illegal access"
+"Instantiation error"
+"Malformed url"
+"IO error"
+"Invalid action"
+"JSON error"
+"Error
+```
+
+>**NOTE:** It is up to the plugin to decide what is contained in the `result` field and the meaning of the `pluginStatus` that is returned. Reference the API of the plugin you are using to see what you should expect those fields to contain and how to use their values
+
+A `resume` callback that makes use of the event payload might look something like this:
+
+```javascript
+onResume(event) {
+    // Restore your application's state here. It's up to you to keep track of
+    // where this plugin result is coming from (i.e. what part of your code made
+    // the call) and what arguments you provided to the plugin
+
+    // Next, check if there is a plugin result in the event object
+    if(event.pendingResult) {
+        // Figure out whether or not the plugin call was successful
+        if(event.pendingResult.pluginStatus === "OK") {
+            successCallback(event.result);
+        } else {
+            failCallback(event.result);
+        }
+    }
+}
+```

http://git-wip-us.apache.org/repos/asf/cordova-docs/blob/d7e98523/www/docs/en/dev/guide/platforms/android/plugin.md
----------------------------------------------------------------------
diff --git a/www/docs/en/dev/guide/platforms/android/plugin.md b/www/docs/en/dev/guide/platforms/android/plugin.md
index e232ba6..eb077b5 100644
--- a/www/docs/en/dev/guide/platforms/android/plugin.md
+++ b/www/docs/en/dev/guide/platforms/android/plugin.md
@@ -346,16 +346,119 @@ In addition to asking for permission for a single permission, it is also possibl
     String [] permissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION };
 
 Then when requesting the permission, all that needs to be done is the following:
-    
+
     cordova.requestPermissions(this, 0, permissions);
 
-This requests the permissions specified in the array.  It's a good idea to provide a publicly accessible permissions array since this can be used by plugins that use your plugin as a 
+This requests the permissions specified in the array.  It's a good idea to provide a publicly accessible permissions array since this can be used by plugins that use your plugin as a
 dependency, although this is not required.
 
 ## Debugging Android Plugins
 
 Android debugging can be done with either Eclipse or Android Studio, although Android
 studio is recommended.  Since Cordova-Android is currently used as a library project,
-and plugins are supported as source code, it is possible to debug the Java code inside 
+and plugins are supported as source code, it is possible to debug the Java code inside
 a Cordova application just like a native Android application.
 
+## Launching Other Activities
+
+There are special considerations to be made if your plugin launches an Activity
+that pushes the Cordova Activity to the background. The Android OS will destroy
+Activities in the background if the device is running low on memory. In that
+case, the CordovaPlugin instance will be destroyed as well. If your plugin is
+waiting on a result from the Activity it launched, a new instance of your plugin
+will be created when the Cordova Activity is brought back to the foreground and
+the result is obtained. However, state for the plugin will not be automatically
+saved or restored and the CallbackContext for the plugin will be lost. There are
+two methods that your CordovaPlugin may implement to handle this situation:
+
+```java
+/**
+ * Called when the Activity is being destroyed (e.g. if a plugin calls out to an
+ * external Activity and the OS kills the CordovaActivity in the background).
+ * The plugin should save its state in this method only if it is awaiting the
+ * result of an external Activity and needs to preserve some information so as
+ * to handle that result; onRestoreStateForActivityResult() will only be called
+ * if the plugin is the recipient of an Activity result
+ *
+ * @return  Bundle containing the state of the plugin or null if state does not
+ *          need to be saved
+ */
+public Bundle onSaveInstanceState() {}
+
+/**
+ * Called when a plugin is the recipient of an Activity result after the
+ * CordovaActivity has been destroyed. The Bundle will be the same as the one
+ * the plugin returned in onSaveInstanceState()
+ *
+ * @param state             Bundle containing the state of the plugin
+ * @param callbackContext   Replacement Context to return the plugin result to
+ */
+public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {}
+```
+
+It is important to note that the above methods should only be used if your
+plugin launches an Activity for a result and should only restore the state
+necessary to handle that Activity result. The state of the plugin will *NOT* be
+restored except in the case where an Activity result is obtained that your
+plugin requested using the CordovaInterface's `startActivityForResult()` method
+and the Cordova Activity was destroyed by the OS while in the background.
+
+As part of `onRestoreStateForActivityResult()`, your plugin will be passed a
+replacement CallbackContext. It is important to realize that this
+CallbackContext *IS NOT* the same one that was destroyed with the Activity. The
+original callback is lost, and will not be fired in the javascript application.
+Instead, this replacement CallbackContext will return the result as part of the
+`resume` event that is fired when the application resumes. The payload of the
+`resume` event follows this structure:
+
+```
+{
+    action: "resume",
+    pendingResult: {
+        pluginServiceName: <name of the plugin e.g. "Camera">,
+        pluginStatus: <description of the result's status>,
+        result: <argument(s) that would have been given to the callback>
+    }
+}
+```
+
+* `pluginServiceName` will match the `name` attribute from your plugin.xml
+* `pluginStatus` will be a String describing the status of the PluginResult
+   passed to the CallbackContext. See PluginResult.java for the String values
+   that correspond to plugin statuses
+* `result` will be whatever result the plugin passes to the CallbackContext
+   (e.g. a String, a number, a JSON object, etc.)
+
+This resume payload will be passed to any callbacks that the javascript
+application has registered for the `resume` event. This means that the result is
+going *directly* to the Cordova application; your plugin will not have a chance
+to process the result with javascript before the application receives it.
+Consequently, you should strive to make the result returned by the native code
+as complete as possible and not rely on any javascript callbacks when launching
+activities.
+
+Be sure to communicate how the Cordova application should interpret the result
+they receive in the `resume` event. It is up to the Cordova application to
+maintain their own state and remember what requests they made and what arguments
+they provided if necessary. However, you should still clearly communicate the
+meaning of `pluginStatus` values and what sort of data is being returned in the
+`result` field as part of your plugin's API.
+
+The complete sequence of events for launching an Activity is as follows
+
+1. The Cordova application makes a call to your plugin
+2. Your plugin launches an Activity for a result
+3. The Android OS destroys the Cordova Activity and your plugin instance
+    * *`onSaveInstanceState()` is called*
+4. The user interacts with your Activity and the Activity finishes
+5. The Cordova Activity is recreated and the Activity result is received
+    * *`onRestoreStateForActivityResult()` is called*
+6. `onActivityResult()` is called and your plugin passes a result to the new
+    CallbackContext
+7. The `resume` event is fired and received by the Cordova application
+
+Android provides a developer setting for debugging Activity destruction on low
+memory. Enable the "Don't keep activities" setting in the Developer Options menu
+on your device or emulator to simulate low memory scenarios. If your plugin
+launches external activities, you should always do some testing with this
+setting enabled to ensure that you are properly handling low memory scenarios.

http://git-wip-us.apache.org/repos/asf/cordova-docs/blob/d7e98523/www/docs/en/dev/guide/platforms/index.md
----------------------------------------------------------------------
diff --git a/www/docs/en/dev/guide/platforms/index.md b/www/docs/en/dev/guide/platforms/index.md
index 286e6fd..9e19e3f 100644
--- a/www/docs/en/dev/guide/platforms/index.md
+++ b/www/docs/en/dev/guide/platforms/index.md
@@ -55,6 +55,7 @@ a lower-level alternative to the `cordova` command-line utility.
 * [Android Plugins](android/plugin.html)
 * [Android WebViews](android/webview.html)
 * [Upgrading Android](android/upgrade.html)
+* [Android Lifecycle Guide](android/lifecycle.html)
 
 ## BlackBerry 10
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org