5

The context:

I use AdMob mediation to display banner ads in my app. I integrated Millennial ad network SDK and Millennial AdMob adapter.

Problem: my app supports Android API 9+, whereas Millennial SDK supports API 16+. Worse, instead of gracefully failing (returning no ad to the AdMob mediation layer so that it can continue going down the mediation waterfall), the SDK crashes on devices running Android < 16 (Fatal Exception: java.lang.NoSuchMethodError android.webkit.WebSettings.setAllowUniversalAccessFromFileURLs)

Apparently Millennial developers are not planning to fix this, they recommend publishing 2 distinct APKs ("<16" without their SDK and "16+" with their SDK), which is a troublesome solution.

I would prefer a simpler solution: on devices running Android API < 16, I'd like to reproduce what happens when an AdMob adapter is missing: AdMob mediation just goes to the next network. This would mean unloading or erasing the Millennial adapter class before I instantiate the AdMod mediation banner.

The question:

Is there any way to prevent any future instantation of a given class (from a 3rd party library) at runtime? (e.g. by forcing a ClassNotFound exception)

Sébastien
  • 13,831
  • 10
  • 55
  • 70
  • Do you have a constructor on this class already? If not, implement a default one (with no parameter) that throws your exception. – dotvav Nov 06 '15 at 11:45
  • @dotvav Yes the class `MillennialAdapter` has a constructor, which is called by the AdMob mediation library. I could achieve my purpose by decompiling the adapter class and modifying it, but I'd like to avoid this. – Sébastien Nov 06 '15 at 11:48
  • Oh then you mean _preventing any future instantiation of a class *you don't control*_. – dotvav Nov 06 '15 at 11:49
  • Its not easier to just check the OS version to not use admob below the minimum sdk? – Nanoc Nov 06 '15 at 11:49
  • There are a few hints here about Java classes unloading or reloading that may help: http://stackoverflow.com/questions/2095974/how-to-unload-a-already-loaded-class-in-java – dotvav Nov 06 '15 at 11:53
  • @dotvav Yes, exactly, I didn't write (and don't control the code of) the class I want to make un-instantiable. I made minor edits to make this clearer. – Sébastien Nov 06 '15 at 12:30
  • @Nanoc I *need* AdMob mediation to run on SDKs lower than 16. It just needs to ignore/skip Millennial network. – Sébastien Nov 06 '15 at 12:32

3 Answers3

2

Use two ad units. You can set up two banner ad units at AdMob.com, one with MillennialMedia in the mediation stack and one without. You can then check the API level of the device at runtime as Bonatti suggests, and set the ad unit ID on your AdView as appropriate prior to requesting ads.

If MillennialMedia is not in the mediation configuration for the ad unit being used, their adapter will not be instantiated by the Google Mobile Ads SDK.

RedBrogdon
  • 5,113
  • 2
  • 24
  • 31
  • This workaround would do the job. BUT it is not satisfactory for me : I have a lot of ad networks in the mediation waterfall of my main ad unit ID, each with some country-specific CPM settings, which I update manually on a regular basis. Duplicating this ad unit would make the process of updating its mediation settings almost twice as long... – Sébastien Nov 09 '15 at 09:04
1

I am exactly in the same situation as you.

After doing a lot of research and black magic with class loaders, etc., I've found a dirty but working solution:

// At onCreate() or wherever it makes sense

if (Build.VERSION.SDK_INT < 16) {
    // Must be done before requesting the first ad
    disableMMediaAdapter();
}
m_adView.loadAd(adRequest);

// ...

private void disableMMediaAdapter()
{
    try {
        Field fInitialized = MMSDK.class.getField("initialized");
        fInitialized.setAccessible(true);
        fInitialized.set(null, true);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

By tricking the SDK into believing it's initialized it will fail miserabily when an ad is requested through it and the next mediation adapter will be called.

This will work as long as they don't change their class desing too much.

Doodler
  • 167
  • 1
  • 7
  • Interesting approach! In the end my solution was to decompile their SDK and add a SDK check to throw an exception when API < 16. – Sébastien Jan 05 '16 at 16:31
0

Is there any way to prevent any future instantation of a given class at runtime? (e.g. by forcing a ClassNotFound exception)

You can, before loading the Adapter, check the OS version, and if its below your threshold

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.THE_VERSION_I_WANT_MINIMUM) {
   // doStuffs()
} else {
   throw new MyException("Stuffs have stuffened...");
}
Bonatti
  • 2,778
  • 5
  • 23
  • 42
  • Sorry if my question was unclear but this does not answer it. The important point is not checking the OS version, it is disabling a class from an external library. – Sébastien Nov 06 '15 at 12:29