Bug 1401737 - 6. Handle GeckoApplication recreation; r=esawin
authorJim Chen <nchen@mozilla.com>
Tue, 10 Oct 2017 15:13:46 -0400
changeset 385485 be0cfcefed9773a94043e0970206f25d8579d672
parent 385484 ea3bce8585831403a2a9b4760cf5488b5c538143
child 385486 f4946d9cb4180e809ccc5587d97caf318e67e818
push id32656
push userarchaeopteryx@coole-files.de
push dateWed, 11 Oct 2017 09:50:40 +0000
treeherdermozilla-central@20d9ad08dd36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersesawin
bugs1401737
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1401737 - 6. Handle GeckoApplication recreation; r=esawin Handle rare cases where GeckoApplication is recreated when Fennec is alive. In such cases, we need to unregister certain event listeners from the old GeckoApplication so we can register again under the new one. Also make GeckoActivityMonitor and TelemetryBackgroundReceiver better handle GeckoApplication instance changes. MozReview-Commit-ID: CrV8R9CyCRm
mobile/android/base/java/org/mozilla/gecko/GeckoActivityMonitor.java
mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryBackgroundReceiver.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoActivityMonitor.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoActivityMonitor.java
@@ -14,44 +14,36 @@ import java.lang.ref.WeakReference;
 
 public class GeckoActivityMonitor implements Application.ActivityLifecycleCallbacks {
     private static final String LOGTAG = "GeckoActivityMonitor";
 
     private static final GeckoActivityMonitor instance = new GeckoActivityMonitor();
 
     private GeckoApplication appContext;
     private WeakReference<Activity> currentActivity = new WeakReference<>(null);
-    private boolean mInitialized;
 
     public static GeckoActivityMonitor getInstance() {
         return instance;
     }
 
     private GeckoActivityMonitor() { }
 
     private void updateActivity(final Activity activity) {
         if (currentActivity.get() == null) {
-            appContext.onApplicationForeground();
+            ((GeckoApplication) activity.getApplication()).onApplicationForeground();
         }
         currentActivity = new WeakReference<>(activity);
     }
 
     public Activity getCurrentActivity() {
         return currentActivity.get();
     }
 
-    public synchronized void initialize(final Context context) {
-        if (mInitialized) {
-            return;
-        }
-
-        appContext = (GeckoApplication) context.getApplicationContext();
-
-        appContext.registerActivityLifecycleCallbacks(this);
-        mInitialized = true;
+    public synchronized void initialize(final GeckoApplication app) {
+        app.registerActivityLifecycleCallbacks(this);
     }
 
     @Override
     public void onActivityCreated(Activity activity, Bundle savedInstanceState) { }
 
     @Override
     public void onActivityStarted(Activity activity) {
         updateActivity(activity);
@@ -69,17 +61,17 @@ public class GeckoActivityMonitor implem
     public void onActivityStopped(Activity activity) {
         // onStop for the previous activity is called after onStart/onResume for
         // the new/resumed activity, so if we're switching activities within our
         // app, mCurrentActivity should already refer to the next activity at
         // this point.
         // If it doesn't, it means we've been backgrounded.
         if (currentActivity.get() == activity) {
             currentActivity.clear();
-            appContext.onApplicationBackground();
+            ((GeckoApplication) activity.getApplication()).onApplicationBackground();
         }
     }
 
     @Override
     public void onActivitySaveInstanceState(Activity activity, Bundle outState) { }
 
     @Override
     public void onActivityDestroyed(Activity activity) { }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -60,16 +60,18 @@ public class GeckoApplication extends Ap
     private boolean mInBackground;
     private boolean mPausedGecko;
     private boolean mIsInitialResume;
 
     private LightweightTheme mLightweightTheme;
 
     private RefWatcher mRefWatcher;
 
+    private final EventListener mListener = new EventListener();
+
     private static String sSessionUUID = null;
 
     public GeckoApplication() {
         super();
     }
 
     public static RefWatcher getRefWatcher(Context context) {
         GeckoApplication app = (GeckoApplication) context.getApplicationContext();
@@ -197,16 +199,21 @@ public class GeckoApplication extends Ap
         mInBackground = false;
     }
 
     @Override
     public void onCreate() {
         Log.i(LOG_TAG, "zerdatime " + SystemClock.elapsedRealtime() +
               " - application start");
 
+        final Context oldContext = GeckoAppShell.getApplicationContext();
+        if (oldContext instanceof GeckoApplication) {
+            ((GeckoApplication) oldContext).onDestroy();
+        }
+
         final Context context = getApplicationContext();
         GeckoAppShell.ensureCrashHandling();
         GeckoAppShell.setApplicationContext(context);
 
         // PRNG is a pseudorandom number generator.
         // We need to apply PRNG Fixes before any use of Java Cryptography Architecture.
         // We make use of various JCA methods in data providers for generating GUIDs, as part of FxA
         // flow and during syncing. Note that this is a no-op for devices running API>18, and so we
@@ -276,30 +283,47 @@ public class GeckoApplication extends Ap
         NotificationHelper.getInstance(context).init();
 
         MulticastDNSManager.getInstance(context).init();
 
         GeckoService.register();
 
         IntentHelper.init();
 
-        final EventListener listener = new EventListener();
-        EventDispatcher.getInstance().registerUiThreadListener(listener,
+        EventDispatcher.getInstance().registerUiThreadListener(mListener,
                 "Gecko:Exited",
                 "RuntimePermissions:Check",
                 "Snackbar:Show",
                 "Share:Text",
                 null);
-        EventDispatcher.getInstance().registerBackgroundThreadListener(listener,
+        EventDispatcher.getInstance().registerBackgroundThreadListener(mListener,
                 "Profile:Create",
                 null);
 
         super.onCreate();
     }
 
+    /**
+     * May be called when a new GeckoApplication object
+     * replaces an old one due to assets change.
+     */
+    private void onDestroy() {
+        EventDispatcher.getInstance().unregisterUiThreadListener(mListener,
+                "Gecko:Exited",
+                "RuntimePermissions:Check",
+                "Snackbar:Show",
+                "Share:Text",
+                null);
+        EventDispatcher.getInstance().unregisterBackgroundThreadListener(mListener,
+                "Profile:Create",
+                null);
+
+        GeckoService.unregister();
+    }
+
     public void onDelayedStartup() {
         if (AppConstants.MOZ_ANDROID_GCM) {
             // TODO: only run in main process.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     // It's fine to throw GCM initialization onto a background thread; the registration process requires
                     // network access, so is naturally asynchronous.  This, of course, races against Gecko page load of
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryBackgroundReceiver.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryBackgroundReceiver.java
@@ -70,23 +70,29 @@ public class TelemetryBackgroundReceiver
     private static final String PREF_LAST_ATTEMPTED_UPLOADED = "last_attempted_upload";
 
     // We don't currently support passing profile along with background telemetry. Profile is used to
     // identify where pings are persisted locally.
     private static final String DEFAULT_PROFILE = "default";
 
     private static final TelemetryBackgroundReceiver instance = new TelemetryBackgroundReceiver();
 
+    private boolean initialized;
+
     public static TelemetryBackgroundReceiver getInstance() {
         return instance;
     }
 
     public void init(Context context) {
+        if (initialized) {
+            return;
+        }
         LocalBroadcastManager.getInstance(context).registerReceiver(
                 this, new IntentFilter(ACTION_BACKGROUND_TELEMETRY));
+        initialized = true;
     }
 
     @Override
     public void onReceive(final Context context, final Intent intent) {
         Log.i(LOG_TAG, "Handling background telemetry broadcast");
 
         // Do not process incoming background telemetry if user disabled telemetry in preferences.
         // Background telemetry is opt-out by default. We're using the old FHR pref as that's our