Подтвердить что ты не робот

Утечка памяти, обнаруженная в пользовательских вкладках 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?

Спасибо.

4b9b3361

Ответ 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