Question Fuite de mémoire détectée dans les onglets personnalisés Chrome


J'essaie d'implémenter les onglets personnalisés Chrome et de détecter une fuite de mémoire via LeakCanary.

L'application de démonstration ne semble pas avoir de fuite à moins d'ajouter une autre couche d'activité (c'est-à-dire MainActivity les lancements Activity2, qui lie / déconnecte le service de tabulation personnalisé et lance l'url - tout ce que le MainActivity fait dans le application de démonstration).

MainActivity ressemble à ceci:

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);
        }
    }
}

De retour de Activity2 à MainActivity causera cette fuite:

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

Ma question est la suivante: est-ce un bug dans l'application de démonstration? (Peut être unbindCustomTabsService() manque-t-il un démontage nécessaire?) Ou s'agit-il d'un bogue dans la bibliothèque Chrome Custom Tabs elle-même?

Je vous remercie.


11
2017-09-04 21:10


origine


Réponses:


MainActivity dans l'exemple crée une instance de CustomTabsServiceConnection et CustomTabsCallback en tant que classes internes anonymes.

Si vous les modifiez en classes internes statiques, supprimez donc la this référence à la MainActivity, et définir les références à la MainActivity comme WeakReferences, vous verrez que LeakCanary cesse de générer des rapports sur la fuite de MainActivity.

Maintenant, vous pouvez toujours voir un rapport de fuite sur le ServiceConnection qui fuit si vous le configurez pour regarder cet objet. La raison en est qu’elle est liée au service Chrome et qu’elle ne peut pas être nettoyée par GC tant que le GC n’est pas exécuté côté serveur.

J'ai créé un test qui lie et dissocie le service dans une boucle et j'ai confirmé que les connexions de service sont effectivement collectées après un certain temps.

Ainsi, la démo peut être améliorée afin d'éviter que ServiceConnection ne contienne une référence à MainActivity, évitant qu'un objet lourd comme une activité ne soit actif longtemps après la déconnexion du service, et que la bibliothèque d'onglets personnalisés ne pose aucun problème.


2
2017-09-09 10:33



Trouvé la réponse à cette question -

Si vous lancez customTab comme suit

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);
}

Ensuite, vous devez dissocier cette méthode mServiceConnection onDestroy comme -

@Override
protected void onDestroy() {
    super.onDestroy();
    this.unbindService(mServiceConnection);
    mServiceConnection = null;
}

Cela arrêtera de lancer

android.app.ServiceConnectionLeaked: Activity <Your_Activity> has leaked ServiceConnection 

3
2018-02-22 00:47