So, I just spent the better part of a day trying to figure this out and debug a memory leak, while my app is crashing in the wild.
- I did try to contract Avocarrot. They did not reply. I don’t know what time they go home.
- I hope they fix this FAST, since I replaced all my ads with Native.
Here is the leak:
First, I am using the Mopub Adapter found here: https://github.com/Avocarrot/android-adapters/blob/master/avocarrotadapter/src/main/java/com/mopub/nativeads/AvocarrotNativeMopub.java
Thanks guys! I wish more companies would provide MoPub adapters!
So, lets dive into the code…
The MoPub adapter calls AvocarrotCustom like this:
mAvocarrotCustom = new AvocarrotCustom((Activity)context, appId, placement, "mopub");
Notice how they are specifically passing in the context as an Activity? Well, what are they doing with it ?
public AvocarrotCustom(Activity activity, String apiKey, String placementName, String mediationType)
{
super(activity, apiKey, placementName, mediationType);
}
Looks like just passing it to the base class… which does the following:
BaseController(Activity activity, String apiKey, String placementName, String mediationType)
{
this.avocarrot = new Avocarrot(activity);
.. blah... snipped...
if (activity != null) {
initializationsWithContext(activity);
}
}
void initializationsWithContext(Activity activity)
{
this.imageManager = new ImageManager(activity);
this.weakActivity = new WeakReference(activity);
}
AND… in a heinous piece of sinful code, they don’t actually use the weakReference to the Activity!
Ok, so moving on, what does Avocarrot(activity) do ? First, it does not even care about the Activity… you could be passing it the application context!
Avocarrot(Context context) {
... other stuff snipped....
if (context != null) {
initContextRequired(context);
}
}
void initContextRequired(Context context)
{
if (this.deviceInfo == null) {
this.deviceInfo = new DeviceInfo(context);
}
if (!AvocarrotSentry.isInitialized()) {
try
{
AvocarrotSentry.init(context, .... blah blah blah.....
Ok, so here we see the Activity has been cast to a Context and passed to AvocarrotSentry… which is a Singleton btw…
public static void init(Context context, String dsn)
{
getInstance().context = context;
Yep, that’s right… the fscker just stored the context!!! … and just like that, we leaked the Activity! All they needed to do was try context.getApplicationContext() and they could leak the Application instead.
So, you might not want to use Avocarrot until they fix this…
To recap:
- They force you to use an Activity for context
- They they cast away the Activity and create a singleton and store the context in that, thereby leaking the Activity
- Have a nice day! I know I did… not!