Allison
Allison

Reputation: 501

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.

Upvotes: 12

Views: 3158

Answers (2)

Manisha
Manisha

Reputation: 863

Found the Answer for this question -

If you are launching customTab as follow

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

Then you need to unbind this mServiceConnection onDestroy method as -

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

That will stop throwing

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

Upvotes: 5

andreban
andreban

Reputation: 4976

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.

Upvotes: 1

Related Questions