Bug 1257941 - Use LeakCanary to watch for fragment leaks. r?ahunt
MozReview-Commit-ID: GhqcSH1SML
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -50,16 +50,24 @@ public class GeckoApplication extends Ap
return instance;
}
public static RefWatcher getRefWatcher(Context context) {
GeckoApplication app = (GeckoApplication) context.getApplicationContext();
return app.mRefWatcher;
}
+ public static void watchReference(Context context, Object object) {
+ if (context == null) {
+ return;
+ }
+
+ getRefWatcher(context).watch(object);
+ }
+
@Override
public Context getContext() {
return this;
}
@Override
public SharedPreferences getSharedPreferences() {
return GeckoSharedPrefs.forApp(this);
--- a/mobile/android/base/java/org/mozilla/gecko/RemoteClientsDialogFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/RemoteClientsDialogFragment.java
@@ -55,16 +55,23 @@ public class RemoteClientsDialogFragment
dialog.setArguments(args);
return dialog;
}
public RemoteClientsDialogFragment() {
// Empty constructor is required for DialogFragment.
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ GeckoApplication.watchReference(getActivity(), this);
+ }
+
protected void notifyListener(List<RemoteClient> clients) {
RemoteClientsListener listener;
try {
listener = (RemoteClientsListener) getTargetFragment();
} catch (ClassCastException e) {
try {
listener = (RemoteClientsListener) getActivity();
} catch (ClassCastException f) {
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
@@ -4,16 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.home;
import java.util.EnumSet;
import org.mozilla.gecko.EditBookmarkDialog;
import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.R;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.Restrictions;
import org.mozilla.gecko.SnackbarHelper;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
@@ -111,16 +112,23 @@ public abstract class HomeFragment exten
} else {
mCanLoadHint = DEFAULT_CAN_LOAD_HINT;
}
mIsLoaded = false;
}
@Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ GeckoApplication.watchReference(getActivity(), this);
+ }
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
if (!(menuInfo instanceof HomeContextMenuInfo)) {
return;
}
HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
// Don't show the context menu for folders.
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferenceFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferenceFragment.java
@@ -4,16 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.preferences;
import java.util.Locale;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.BrowserLocaleManager;
+import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.LocaleManager;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.TelemetryContract.Method;
import org.mozilla.gecko.fxa.AccountLoaderNative;
@@ -30,16 +31,18 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
+import com.squareup.leakcanary.RefWatcher;
+
/* A simple implementation of PreferenceFragment for large screen devices
* This will strip category headers (so that they aren't shown to the user twice)
* as well as initializing Gecko prefs when a fragment is shown.
*/
public class GeckoPreferenceFragment extends PreferenceFragment {
public static final int ACCOUNT_LOADER_ID = 1;
private AccountLoaderCallbacks accountLoaderCallbacks;
@@ -252,16 +255,18 @@ public class GeckoPreferenceFragment ext
PrefsHelper.removeObserver(mPrefsRequest);
mPrefsRequest = null;
}
final int res = getResource();
if (res == R.xml.preferences) {
Telemetry.stopUISession(TelemetryContract.Session.SETTINGS);
}
+
+ GeckoApplication.watchReference(getActivity(), this);
}
private class AccountLoaderCallbacks implements LoaderManager.LoaderCallbacks<Account> {
@Override
public Loader<Account> onCreateLoader(int id, Bundle args) {
return new AccountLoaderNative(getActivity());
}
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabHistoryFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabHistoryFragment.java
@@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.tabs;
import java.util.ArrayList;
import java.util.List;
import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
@@ -102,16 +103,18 @@ public class TabHistoryFragment extends
super.onPause();
dismiss();
}
@Override
public void onDestroy() {
super.onDestroy();
dismiss();
+
+ GeckoApplication.watchReference(getActivity(), this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (backStackId >= 0) {
outState.putInt(BACK_STACK_ID, backStackId);
}
}
--- a/mobile/android/base/java/org/mozilla/gecko/widget/ExternalIntentDuringPrivateBrowsingPromptFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/ExternalIntentDuringPrivateBrowsingPromptFragment.java
@@ -1,15 +1,16 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package org.mozilla.gecko.widget;
import org.mozilla.gecko.ActivityHandlerHelper;
+import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -51,16 +52,23 @@ public class ExternalIntentDuringPrivate
public void onClick(final DialogInterface dialog, final int id) {
context.startActivity(intent);
}
})
.setNegativeButton(R.string.button_no, null /* we do nothing if the user rejects */ );
return builder.create();
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ GeckoApplication.watchReference(getActivity(), this);
+ }
+
/**
* @return true if the Activity is started or a dialog is shown. false if the Activity fails to start.
*/
public static boolean showDialogOrAndroidChooser(final Context context, final FragmentManager fragmentManager,
final Intent intent) {
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
if (selectedTab == null || !selectedTab.isPrivate()) {
return ActivityHandlerHelper.startIntentAndCatch(LOGTAG, context, intent);
--- a/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
@@ -5,16 +5,17 @@
package org.mozilla.search;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import android.support.v4.content.ContextCompat;
import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.search.providers.SearchEngine;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@@ -69,16 +70,23 @@ public class PostSearchFragment extends
public void onDestroyView() {
super.onDestroyView();
webview.setWebChromeClient(null);
webview.setWebViewClient(null);
webview = null;
progressBar = null;
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ GeckoApplication.watchReference(getActivity(), this);
+ }
+
public void startSearch(SearchEngine engine, String query) {
this.engine = engine;
final String url = engine.resultsUriForQuery(query);
// Only load urls if the url is different than the webview's current url.
if (!TextUtils.equals(webview.getUrl(), url)) {
resultsPageHost = null;
webview.loadUrl(Constants.ABOUT_BLANK);
--- a/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
@@ -21,16 +21,17 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener;
import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener.OnDismissCallback;
import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
@@ -88,16 +89,18 @@ public class PreSearchFragment extends F
}
@Override
public void onDestroy() {
super.onDestroy();
getLoaderManager().destroyLoader(LOADER_ID_SEARCH_HISTORY);
cursorAdapter.swapCursor(null);
cursorAdapter = null;
+
+ GeckoApplication.watchReference(getActivity(), this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
final View mainView = inflater.inflate(R.layout.search_fragment_pre_search, container, false);
// Initialize listview.
listView = (ListView) mainView.findViewById(R.id.list_view);