Fix memory leak in ApplicationStates

- Use app context in dreambackend
- Use WeakReference in LocalBluetoothManager to track foreground
  activity
- Use WeakReference in ApplicationsState to track active sessions

Bug: 109894607
Test: hprof
Change-Id: I0e868182a364efbd110eb77ebd6cdbd83f164669
This commit is contained in:
Fan Zhang
2018-06-07 11:36:32 -07:00
parent 70b447a378
commit fb48015c0e
3 changed files with 65 additions and 35 deletions

View File

@@ -22,9 +22,6 @@ import android.app.AppGlobals;
import android.app.Application;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -49,7 +46,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.VisibleForTesting;
import android.text.format.Formatter;
import android.util.IconDrawableFactory;
import android.util.Log;
@@ -62,6 +58,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.text.Collator;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -74,6 +71,11 @@ import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
/**
* Keeps track of information about all installed applications, lazy-loading
* as needed.
@@ -132,7 +134,7 @@ public class ApplicationsState {
boolean mSessionsChanged;
// Temporary for dispatching session callbacks. Only touched by main thread.
final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
final ArrayList<WeakReference<Session>> mActiveSessions = new ArrayList<>();
final HandlerThread mThread;
final BackgroundHandler mBackgroundHandler;
@@ -618,7 +620,7 @@ public class ApplicationsState {
for (int i=0; i<mSessions.size(); i++) {
Session s = mSessions.get(i);
if (s.mResumed) {
mActiveSessions.add(s);
mActiveSessions.add(new WeakReference<>(s));
}
}
}
@@ -830,46 +832,70 @@ public class ApplicationsState {
rebuildActiveSessions();
switch (msg.what) {
case MSG_REBUILD_COMPLETE: {
Session s = (Session)msg.obj;
if (mActiveSessions.contains(s)) {
s.mCallbacks.onRebuildComplete(s.mLastAppList);
Session s = (Session) msg.obj;
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null && session == s) {
s.mCallbacks.onRebuildComplete(s.mLastAppList);
}
}
} break;
case MSG_PACKAGE_LIST_CHANGED: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onPackageListChanged();
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onPackageListChanged();
}
}
} break;
case MSG_PACKAGE_ICON_CHANGED: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onPackageIconChanged();
}
}
} break;
case MSG_PACKAGE_SIZE_CHANGED: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
(String)msg.obj);
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onPackageSizeChanged(
(String) msg.obj);
}
}
} break;
case MSG_ALL_SIZES_COMPUTED: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onAllSizesComputed();
}
}
} break;
case MSG_RUNNING_STATE_CHANGED: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
msg.arg1 != 0);
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onRunningStateChanged(
msg.arg1 != 0);
}
}
} break;
case MSG_LAUNCHER_INFO_CHANGED: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onLauncherInfoChanged();
}
}
} break;
case MSG_LOAD_ENTRIES_COMPLETE: {
for (int i=0; i<mActiveSessions.size(); i++) {
mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
for (WeakReference<Session> sessionRef : mActiveSessions) {
final Session session = sessionRef.get();
if (session != null) {
session.mCallbacks.onLoadEntriesCompleted();
}
}
} break;
}

View File

@@ -19,6 +19,8 @@ package com.android.settingslib.bluetooth;
import android.content.Context;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
* LocalBluetoothManager provides a simplified interface on top of a subset of
* the Bluetooth API. Note that {@link #getInstance} will return null
@@ -34,7 +36,7 @@ public class LocalBluetoothManager {
private final Context mContext;
/** If a BT-related activity is in the foreground, this will be it. */
private Context mForegroundActivity;
private WeakReference<Context> mForegroundActivity;
private final LocalBluetoothAdapter mLocalAdapter;
@@ -85,17 +87,19 @@ public class LocalBluetoothManager {
}
public Context getForegroundActivity() {
return mForegroundActivity;
return mForegroundActivity == null
? null
: mForegroundActivity.get();
}
public boolean isForegroundActivity() {
return mForegroundActivity != null;
return mForegroundActivity != null && mForegroundActivity.get() != null;
}
public synchronized void setForegroundActivity(Context context) {
if (context != null) {
Log.d(TAG, "setting foreground activity to non-null context");
mForegroundActivity = context;
mForegroundActivity = new WeakReference<>(context);
} else {
if (mForegroundActivity != null) {
Log.d(TAG, "setting foreground activity to null");

View File

@@ -36,12 +36,12 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -97,15 +97,15 @@ public class DreamBackend {
}
public DreamBackend(Context context) {
mContext = context;
mContext = context.getApplicationContext();
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
mComparator = new DreamInfoComparator(getDefaultDream());
mDreamsEnabledByDefault = context.getResources()
mDreamsEnabledByDefault = mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault);
mDreamsActivatedOnSleepByDefault = context.getResources()
mDreamsActivatedOnSleepByDefault = mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
mDreamsActivatedOnDockByDefault = context.getResources()
mDreamsActivatedOnDockByDefault = mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
}