Skip to content

Instantly share code, notes, and snippets.

@andaag
Last active April 9, 2018 12:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andaag/b05ab66ed0f06167d6e0 to your computer and use it in GitHub Desktop.
Save andaag/b05ab66ed0f06167d6e0 to your computer and use it in GitHub Desktop.
workaround for shareactionprovider leak
package no.finn.android.ui.objectpage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.content.Context;
import android.content.Intent;
import android.support.v7.internal.widget.ActivityChooserModel;
import android.widget.ShareActionProvider;
public class SharedActionProviderMemoryLeakHack {
private static Class activityChooserModel = null;
private static Method activityChooserGetMethod = null;
public static void fix(Context context, ShareActionProvider actionProvider) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
actionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
@Override
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
return false;
}
});
ActivityChooserModel dataModel = ActivityChooserModel.get(context, ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
dataModel.setOnChooseActivityListener(new ActivityChooserModel.OnChooseActivityListener() {
@Override
public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
return false;
}
});
fixInternalClass(context);
}
private static void fixInternalClass(Context context) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
/*
Android 5.x seems to use the internal activityChooserModel
*/
if (activityChooserGetMethod == null) {
activityChooserModel = Class.forName("android.widget.ActivityChooserModel");
activityChooserGetMethod = activityChooserModel.getMethod("get", new Class[]{Context.class, String.class});
}
Object activityChooserObject = activityChooserGetMethod.invoke(null, new Object[]{context, ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME});
for (Method method : activityChooserModel.getMethods()) {
if (method.getName().equals("setOnChooseActivityListener")) {
method.invoke(activityChooserObject, new Object[]{null});
break;
}
}
}
}
@apkelly
Copy link

apkelly commented May 27, 2016

Where should I apply the fix method? Is this in my Fragment that deals with the ShareActionProvider? Do I add it to the onDetachFromView callback? Or does this need to go elsewhere e.g. my Activity?

@apkelly
Copy link

apkelly commented May 30, 2016

Also, Android Studio 2.1 doesn't resolve the import for
import android.support.v7.internal.widget.ActivityChooserModel;

@samkirton
Copy link

From testing with LeakCanary, I've found that the fixInternalClass used onStop of any activity that uses the SharedActionProvider is sufficient for the latest versions of the support library.

public class SharedActionProviderContextLeak {

    public static void patch(Context context) {
        try {
            fixShareActionProvider(context);
        } catch (ClassNotFoundException ignored) {
        } catch (NoSuchMethodException ignored) {
        } catch (InvocationTargetException ignored) {
        } catch (IllegalAccessException ignored) { }
    }

    @SuppressWarnings({"RedundantArrayCreation", "unchecked", "PrivateApi"})
    private static void fixShareActionProvider(Context context) throws ClassNotFoundException,
            NoSuchMethodException,
            InvocationTargetException,
            IllegalAccessException {
        /*
        Android 5.x seems to use the internal activityChooserModel
         */
        Class activityChooserModel = Class.forName("android.widget.ActivityChooserModel");
        Method activityChooserGetMethod = activityChooserModel.getMethod("get", new Class[]{Context.class, String.class});

        Object activityChooserObject = activityChooserGetMethod.invoke(null,
                new Object[] { context, ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME });

        for (Method method : activityChooserModel.getMethods()) {
            if (method.getName().equals("setOnChooseActivityListener")) {
                method.invoke(activityChooserObject, new Object[]{null});
                break;
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment