Android memory leak
Leak because of system bug
PhoneStateListener leak
Below 7.0 a non static inner class IPhoneStateListener.Stub callback in PhoneStateListener references to outside PhoneStateListener, even caller has been destroyed and “un-registered” the PhoneStateListener, the references coming from: Native Stack –> PhoneStateListener –> Context(Activity).
A wrapper class wraps a weak reference of PhoneStateListener can be used to avoid this memory leak.
Caution: The original
PhoneStateListenerobject must be referenced by caller class, otherwise the weak reference in the wrapper class will getnull.
public class PhoneStateListenerWrapper extends PhoneStateListener {
private WeakReference<PhoneStateListener> mPhoneStateListenerRef;
public PhoneStateListenerWrapper(PhoneStateListener phoneStateListener) {
mPhoneStateListenerRef = new WeakReference<>(phoneStateListener);
}
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
PhoneStateListener phoneStateListener = mPhoneStateListenerRef.get();
if (phoneStateListener != null) {
phoneStateListener.onCallStateChanged(state, incomingNumber);
}
}
}
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//...
}
};
private PhoneStateListenerWrapper mPhoneStateListenerWrapper = new PhoneStateListenerWrapper(mPhoneStateListener);
((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)).listen(mPhoneStateListenerWrapper, PhoneStateListener.LISTEN_CALL_STATE);
((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)).listen(mPhoneStateListenerWrapper, PhoneStateListener.LISTEN_NONE);
AudioManager leak
Below 6.0 AudioManager reference to the incoming context of constructor directly, the related issue is here.
A simple workaround is using Application context to get AudioManager.
mAudioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
InputMethodManager leak
Refer to issue 37043700 and issue 37090736, mCurRootView or mServedView or mNextServedView in InputMethodManager may reference to last focused view on a wide system version range. But I have not found this kind of leak in official system. However, mLastSrvView of InputMethodManager may cause leak on huawei 6.0 and above device, and mLastSrvView field is not found in official source code, I think this is a leak for certain manufacturer.
To fix this kind of leak, a straight way is set the relative field of InputMethodManager to null when activity destroyed. Considering there may be multiple activities have this leak, a better way is fix this leak in the onActivityDestroyed(Activity activity) method of ActivityLifecycleCallbacks.
@Override
public void onActivityDestroyed(Activity activity) {
MemoryLeakFixer.fixInputMethodManagerLeak(activity);
}
public class MemoryLeakFixer {
public static void fixInputMethodManagerLeak(Activity activity) {
//Surround with a appropriate if condition to make sure execute fix when leak real exists is better.
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) activity.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable t) {
t.printStackTrace();
}
if (inputMethodManager!= null) {
String[] fieldNames = new String[]{"mCurRootView", "mServedView", "mNextServedView", "mLastSrvView"/*huawei*/};
Class clazz = inputMethodManager.getClass();
for (String fieldName : fieldNames) {
try {
Field field = clazz.getDeclaredField(fieldName);
if (field != null) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
Object obj = field.get(inputMethodManager);
if (obj instanceof View) {
View view = (View) obj;
if (view.getContext() == activity) {
field.set(inputMethodManager, null);
}
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
}
TextLine leak
An activity context may be referenced by sCached in TextLine, a simple solution is set every element of sCached to null in activity’s onDestroy method.
public static void fixTextLineLeak() {
try {
Field field = Class.forName("android.text.TextLine").getDeclaredField("sCached");
field.setAccessible(true);
Object[] sCached = (Object[]) field.get(null);
if (sCached != null) {
for (int i = 0; i < sCached.length; i++) {
sCached[i] = null;
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
Leak because of misuse
Unremoved ViewTreeObserver listener leak
The scenario is after create a new item view for a ViewPager by the instantiateItem method of PageAdapter, a listener reference to the item view indirectly have been add to the ViewTreeObserver of the item view, and the listener never be removed from the ViewTreeObserver, cause the item view leak even the view has been removed from the view hierarchy in the destroyItem method of PageAdapter.
Never forgot remove listeners from ViewTreeObserver.
final ViewTreeObserver observer = view.getViewTreeObserver();
if (observer != null) {
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//do something
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
observer.removeOnGlobalLayoutListener(this);
} else {
observer.removeGlobalOnLayoutListener(this);
}
}
});
}