Search code examples
androidgoogle-chromememory-leakschrome-custom-tabs

Memory leak detected in Chrome Custom Tabs


I'm attempting to implement Chrome Custom Tabs and detecting a memory leak through LeakCanary.

The demo application does not appear to leak unless we add another Activity layer (i.e. MainActivity launches Activity2, which binds/unbinds to the custom tab service and launches the url -- everything the MainActivity does in the demo app).

MainActivity looks like this:

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

Returning from Activity2 to MainActivity will cause this leak:

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

My question is: is this a bug in the demo application? (Maybe unbindCustomTabsService() is missing some needed teardown?) Or is this a bug in the Chrome Custom Tabs library itself?

Thank you.


Solution

  • The MainActivity in the sample creates an instance of CustomTabsServiceConnection and CustomTabsCallback as anonymous inner classes.

    If you change them to be static inner classes, therefore removing the this reference to the MainActivity, and set the references to the MainActivity as WeakReferences, you will see that LeakCanary stops reporting about the MainActivity leaking.

    Now, you may still see leak canary report about the ServiceConnection leaking if you set it to watch that object. The reason is that it is linked to the Chrome service and cannot be cleaned by GC until GC also runs on the server side.

    I created a test that binds and unbinds the Service in a loop and I've confirmed that the ServiceConnections are indeed being collected after a while.

    So, the Demo can be improved in order to avoid the ServiceConnection holding a reference to the MainActivity, avoiding having a heavy object like an Activity being alive a long time after the service is disconnected, and it's not a problem with the Custom Tabs library.