Утечка памяти, обнаруженная в пользовательских вкладках Chrome
Я пытаюсь внедрить пользовательские вкладки Chrome и обнаруживать утечку памяти через LeakCanary.
Демонстрационное приложение не похоже на утечку, если мы не добавим еще один уровень активности (т.е. MainActivity
запускает Activity2
), который связывает/отвязывает службу пользовательской вкладки и запускает URL-адрес - все, что MainActivity
делает в демонстрационное приложение).
MainActivity выглядит следующим образом:
public class MainActivity extends Activity implements OnClickListener {
private Button mLaunchButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LeakCanary.install(getApplication());
setContentView(R.layout.main);
mLaunchButton = (Button) findViewById(R.id.launch_button);
mLaunchButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int viewId = v.getId();
if (viewId == R.id.launch_button) {
Intent intent = new Intent(getApplicationContext(), Activity2.class);
startActivity(intent);
}
}
}
Возврат от Activity2
до MainActivity
приведет к этой утечке:
09-04 13:49:26.783 10456-12161/org.chromium.customtabsclient.example D/LeakCanary﹕ In org.chromium.customtabsclient.example:1.0:1.
09-04 13:49:26.783 10456-12161/org.chromium.customtabsclient.example D/LeakCanary﹕ * org.chromium.customtabsclient.Activity2 has leaked:
09-04 13:49:26.783 10456-12161/org.chromium.customtabsclient.example D/LeakCanary﹕ * GC ROOT android.support.customtabs.CustomTabsClient$1.val$callback (anonymous class extends android.support.customtabs.ICustomTabsCallback$Stub)
09-04 13:49:26.783 10456-12161/org.chromium.customtabsclient.example D/LeakCanary﹕ * references org.chromium.customtabsclient.Activity2$2.this$0 (anonymous class extends android.support.customtabs.CustomTabsCallback)
09-04 13:49:26.783 10456-12161/org.chromium.customtabsclient.example D/LeakCanary﹕ * leaks org.chromium.customtabsclient.Activity2 instance
https://gist.github.com/abvanpelt/ddbc732f31550b09fc27
Мой вопрос: это ошибка в демонстрационном приложении? (Может быть, unbindCustomTabsService()
отсутствует необходимое удаление)? Или это ошибка в самой библиотеке пользовательских вкладок Chrome?
Спасибо.
Ответы
Ответ 1
MainActivity в образце создает экземпляр CustomTabsServiceConnection и CustomTabsCallback в качестве анонимных внутренних классов.
Если вы измените их на статические внутренние классы, поэтому удалите ссылку this
на MainActivity и установите ссылки на MainActivity как WeakReferences, вы увидите, что LeakCanary перестает сообщать об утечке MainActivity.
Теперь вы все равно можете увидеть утечку утечки из отчета о утечке ServiceConnection, если вы установите его для просмотра этого объекта. Причина в том, что он связан с сервисом Chrome и не может быть очищен GC до тех пор, пока GC не будет работать на стороне сервера.
Я создал тест, который связывает и отвязывает службу в цикле, и я подтвердил, что ServiceConnections действительно собираются через некоторое время.
Таким образом, Demo может быть улучшено, чтобы избежать того, что ServiceConnection имеет ссылку на MainActivity, избегая того, чтобы тяжелый объект, например Activity, был жив долго после отключения службы, и это не проблема с Custom Библиотека вкладок.
Ответ 2
Нашел ответ на этот вопрос -
Если вы запускаете customTab, следуйте
private void launchChromeCustomTab(final Context context, final Uri uri) {
mServiceConnection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
client.warmup(0L);
final CustomTabsIntent intent = new CustomTabsIntent.Builder().build();
intent.launchUrl(context, uri);
mIsCustomTabsLaunched = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
CustomTabsClient.bindCustomTabsService(context, "com.android.chrome", mServiceConnection);
}
Затем вам нужно отвязать этот метод mServiceConnection onDestroy как -
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(mServiceConnection);
mServiceConnection = null;
}
Это перестанет бросать
android.app.ServiceConnectionLeaked: Activity <Your_Activity> has leaked ServiceConnection