Ответ 1
У меня была аналогичная проблема с рекламными объявлениями AdMob (Google Play Services), которые пропускали огромные объемы памяти. Использовал MAT, чтобы узнать, что проблема в том, что каждое объявление gms сохранялось моим экземпляром приложения в массиве sComponentCallbacks. Поэтому я переопределяю registerComponentCallbacks()
и unregisterComponentCallbacks()
, чтобы отслеживать экземпляры, которые регистрируются сами, но никогда не регистрируют (когда я предполагаю, что они должны были это сделать). В этом примере кода предполагается, что пакет .gms.ads является проблематичным, но если вы обнаружите других, которые вызывают аналогичную утечку, вы также можете добавить эти пакеты в список:
public class MyApplication extends Application {
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
super.registerComponentCallbacks(callback);
ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksRegistered(callback);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksUnregistered(callback);
super.unregisterComponentCallbacks(callback);
}
public void forceUnregisterComponentCallbacks() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.unregisterAll(this);
}
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private static class ComponentCallbacksBehavioralAdjustmentToolIcs {
static ComponentCallbacksBehavioralAdjustmentToolIcs INSTANCE = new ComponentCallbacksBehavioralAdjustmentToolIcs();
private WeakHashMap<ComponentCallbacks, ApplicationErrorReport.CrashInfo> mCallbacks = new WeakHashMap<>();
private boolean mSuspended = false;
public void onComponentCallbacksRegistered(ComponentCallbacks callback) {
Throwable thr = new Throwable("Callback registered here.");
ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(thr);
if (BuildConfig.DEBUG) Log.w(TAG, "registerComponentCallbacks: " + callback, thr);
if (!mSuspended) {
if (callback.getClass().getName().startsWith("com.google.android.gms.ads")) {
mCallbacks.put(callback, ci);
}
// TODO: other classes may still prove to be problematic? For now, only watch for .gms.ads, since we know those are misbehaving
} else {
if (BuildConfig.DEBUG) Log.e(TAG, "ComponentCallbacks was registered while tracking is suspended!");
}
}
public void onComponentCallbacksUnregistered(ComponentCallbacks callback) {
if (!mSuspended) {
if (BuildConfig.DEBUG) {
Log.i(TAG, "unregisterComponentCallbacks: " + callback, new Throwable());
}
mCallbacks.remove(callback);
}
}
public void unregisterAll(Context context) {
mSuspended = true;
for (Map.Entry<ComponentCallbacks, ApplicationErrorReport.CrashInfo> entry : mCallbacks.entrySet()) {
ComponentCallbacks callback = entry.getKey();
if (callback == null) continue;
if (BuildConfig.DEBUG) {
Log.w(TAG, "Forcibly unregistering a misbehaving ComponentCallbacks: " + entry.getKey());
Log.w(TAG, entry.getValue().stackTrace);
}
try {
context.unregisterComponentCallbacks(entry.getKey());
} catch (Exception exc) {
if (BuildConfig.DEBUG) Log.e(TAG, "Unable to unregister ComponentCallbacks", exc);
}
}
mCallbacks.clear();
mSuspended = false;
}
}
}
Затем в моем методе BaseActivity onPause()
(или onDestroy()
) я вызываю свой метод forceUnregisterComponentCallbacks()
:
@Override
public void onPause() {
((MyApplication) getApplicationContext()).forceUnregisterComponentCallbacks()
super.onPause();
}
Обратите внимание, что ComponentCallbacks был введен в ICS, поэтому, если вы видите проблемы с версиями до ICS, это не проблема.
(Я также понимаю, что это не касается точной проблемы, идентифицированной в OP, поскольку это связано с bindService()
, а не ComponentCallbacks
. Но это избавило нас от довольно значительной утечки памяти, которая заставила нас отключить AdMob полностью, пока не будет выпущено исправление.)