Native Mediation Adapter

A native mediation adapter allows ad networks to request Native Ads. This guide is intended for networks who want to create an AdMob mediation adapter to load and display native ads from one of AdMob's Supported ad networks.

The native mediation adapter needs to be able to do the following things:

  • Request native ads from a mediated network.
  • Forward events from the mediated network's SDK to the Google Mobile Ads SDK.
  • Use a NativeAdMapper to map mediated native ads to AdMob's native ad interface.

Each of these tasks is covered below. You can also find the full source for the sample native mediation adapter at our GitHub repository.

Prerequisites

To implement an AdMob native mediation adapter you must first integrate the Mobile Ads SDK into your project. You may also want to read about making an AdRequest and how mediation works.

The Sample SDK

The code used in this guide is taken from our sample adapter project, which also includes a "Sample SDK." In order for the project to illustrate how to build a native mediation adapter that mediates another ad network's SDK, it needed an SDK to mediate, so we created a mock called the Sample SDK.

The Sample SDK features classes similar to what you'd find in an ad network's production SDK. There are request classes like SampleNativeAdRequest, ad loaders like SampleNativeAdLoader, and other classes, constants and interfaces used to simulate a real network's SDK

Request native ads

The requestNativeAd() method

Native mediation adapter classes must implement the MediationNativeAdapter interface, which includes a method used by the Google Mobile Ads SDK to request native ads from the native mediation adapter:

void requestNativeAd(Context context,
        MediationNativeListener listener,
        Bundle serverParameters,
        NativeMediationAdRequest mediationAdRequest,
        Bundle mediationExtras);

When this method is called, the native mediation adapter asynchronously requests a native ad from the mediated network. The following parameters for requestNativeAd() carry information that the native mediation adapter can use in making its request:

  • serverParameters() - Specifies a Bundle containing values that a publisher on the mediation server side can pass along with each native ad request. Typically, another ad unit ID, which was issued by the mediated network, is included as one of the values in this Bundle.
  • mediationAdRequest() - Specifies a NativeMediationAdRequest object, which contains properties specific to a single request, such as which native formats are requested. NativeMediationAdRequest is a subclass of MediationAdRequest, so it also includes properties for other targeting information provided by publishers at request time.
  • mediationExtras - Specifies a Bundle containing any "extra" information relevant to the request provided by the publisher. When creating an AdRequest, publishers can use the addNetworkExtrasBundle() method to include information for a specific native mediation adapter. Any extras included by the publisher for a given native mediation adapter class are found in this parameter.

In addition to the parameters carrying request information, there are two others:

  • context - Specifies a Context object which can be used if the native mediation adapter requires one.
  • listener - Specifies a MediationNativeListener object that the native mediation adapter should use to forward events.

The listener object is particularly important, as it's used to report events to the Google Mobile Ads SDK. This guide covers how to do so in a later section.

SampleAdapter.java (excerpt)

The following code snippet (from SampleAdapter.java in our sample native mediation adapter project) shows an implemented requestNativeAd() method:

@Override
public void requestNativeAd(Context context,
        MediationNativeListener listener,
        Bundle serverParameters,
        NativeMediationAdRequest mediationAdRequest,
        Bundle mediationExtras) {

    SampleNativeAdLoader loader = new SampleNativeAdLoader(context);
    if (serverParameters.containsKey(_SAMPLE_AD_UNIT_KEY_)) {
        loader.setAdUnit(serverParameters.getString(_SAMPLE_AD_UNIT_KEY_));
    } else {
        listener.onAdFailedToLoad(this, AdRequest._ERROR_CODE_INVALID_REQUEST_);
    }

    loader.setNativeAdListener(new SampleNativeMediationEventForwarder(listener, this));
    SampleNativeAdRequest request = new SampleNativeAdRequest();
    mNativeAdOptions = mediationAdRequest.getNativeAdOptions();

    if (mNativeAdOptions != null) {
        request.setShouldDownloadImages(mNativeAdOptions.shouldReturnUrlsForImageAssets());
    } else {
        request.setShouldDownloadImages(true);
    }

    request.setAppInstallAdsRequested(mediationAdRequest.isAppInstallAdRequested());
    request.setContentAdsRequested(mediationAdRequest.isContentAdRequested());

    loader.fetchAd(request);
}

This is a sample mediation adapter that mediates to an imaginary ad network via a mock "Sample SDK." The mediation adapter uses the information provided in the requestNativeAd parameters to build a SampleNativeAdRequest (a class provided by the Sample SDK), then uses it to request an ad from the Sample SDK's SampleNativeAdLoader. It also creates a SampleNativeMediationEventForwarder to handle forwarding events back to the Google Mobile Ads SDK. That process is the topic of the next section.

Respect native ad formats

As noted above, the NativeMediationAdRequest object includes information about which native ad formats the publisher is requesting. It is extremely important that adapters respect this choice and return only those formats that the publisher indicated they're prepared to display. Otherwise, they could cause the publisher's rendering code to fail.

If, for example, your ad network only ever provides app install ads, and the publisher requests content ads alone, your adapter should not request an app install ad anyway and then try to map it to the content ad format. Instead, it should call the onAdFailedToLoad() method on the MediationNativeListener object with an error code of AdRequest.ERROR_CODE_INVALID_REQUEST.

NativeAdOptions

When making a request for native ads, publishers use a NativeAdOptions object to specify their preferences for that request, such as how image assets should be returned. Your adapter needs to examine those preferences and respect them. Adapters can retrieve a request's NativeAdOptions object using the getNativeAdOptions() method provided by the NativeMediationAdRequest object:

mNativeAdOptions = mediationAdRequest.getNativeAdOptions();

After retrieving the NativeAdOptions, adapters can read its properties and act accordingly. For example, if the shouldReturnUrlsForImageAssets value in NativeAdOptions is false (which is the default), the adapter must return actual image assets rather than just the URLs. In this case, if the SDK being adapted only provides URLs for images, then the adapter must use those URLs to download the image files and make their data available to the publisher in its mapped native ads.

Forward events to the Mobile Ads SDK

A few things can happen when a native mediation adapter tries to load a native ad from its mediated network. The SDK could successfully return a native ad, it could come back with an error, or it might simply report that no ads are available. Similarly, when the user taps on an ad, the mediated SDK might open an overlay or leave the application entirely to start an external browser. It's important for the Google Mobile Ads SDK to be aware of these events, so it provides a MediationNativeListener object as a parameter to the requestNativeAd() method.

It's part of a native mediation adapter's job to act as a "man-in-the-middle," listening for events from the mediated SDK, by calling the MediationNativeListener() method. Your native mediation adapter must be aware of the following methods:

  • onAdLoaded() - This method should be called when a native mediation adapter successfully loads a native ad. It takes a parameter of type MediationNativeAdapter and a parameter of type NativeAdMapper, which the native mediation adapter should use to pass a mapped version of the native ad it just loaded (NativeAdMapper classes are covered in the next section).
  • onAdFailedToLoad() - When the native mediation adapter tries and fails to load a native ad, it should use this method to report the failure. It takes a parameter of type MediationNativeAdapter and an integer parameter, which should be set to one of the error constants: ERROR_CODE_INTERNAL_ERROR, ERROR_CODE_INVALID_REQUEST, ERROR_CODE_NETWORK_ERROR, or ERROR_CODE_NO_FILL.
  • onAdLeftApplication() - Invoke this method when the mediated SDK causes user focus to leave the publisher's application (most commonly to open an external browser).
  • onAdOpened() - If the native ad opens any overlay or separate activity that covers the interface in response to a user's tap, invoke this method. This includes an external browser, in which case your native mediation adapter should call onAdOpened() just before onAdLeftApplication().
  • onAdClosed() - If an overlay or separate activity covering the interface is closed, invoke this method to signify that control is being transferred back to the application containing the native ad. It takes a parameter of type MediationNativeAdapter which is the native mediation adapter that raised the event.

The listener also offers methods for reporting clicks and impressions from adapters that choose to track clicks or impressions for themselves (see the section on overriding click and impression handling below for details). One is for reporting a click to the Google Mobile Ads SDK, and one is for reporting an impression:

  • onAdClicked() - Call this method when a user clicks on a native ad. It takes a parameter of type MediationNativeAdapter which is the native mediation adapter that raised the event.
  • onAdImpression() - Similarly, call this method when an impression is recorded for the ad by the mediated SDK.

There are a number of ways to make sure events are forwarded correctly, but one of the simplest is to create a dedicated class to act as forwarder. The following code snippet (from SampleNativeMediationEventForwarder.java) shows you how:

public class SampleNativeMediationEventForwarder extends SampleNativeAdListener {
    private MediationNativeListener mNativeListener;
    private SampleAdapter mAdapter;

    public SampleNativeMediationEventForwarder(
                MediationNativeListener listener, SampleAdapter adapter) {
        this.mNativeListener = listener;
        this.mAdapter = adapter;
    }

    @Override
    public void onNativeAppInstallAdFetched(SampleNativeAppInstallAd ad) {
        SampleNativeAppInstallAdMapper mapper = new SampleNativeAppInstallAdMapper(ad, mAdapter.getNativeAdOptions());
        mNativeListener.onAdLoaded(mAdapter, mapper);
    }

    @Override
    public void onNativeContentAdFetched(SampleNativeContentAd ad) {
        SampleNativeContentAdMapper mapper = new SampleNativeContentAdMapper(ad, mAdapter.getNativeAdOptions());
        mNativeListener.onAdLoaded(mAdapter, mapper);
    }

    @Override
    public void onAdFetchFailed(SampleErrorCode errorCode) {
        switch (errorCode) {
            case _UNKNOWN_:
                mNativeListener.onAdFailedToLoad(mAdapter, AdRequest._ERROR_CODE_INTERNAL_ERROR_);
                break;
            case _BAD_REQUEST_:
                mNativeListener.onAdFailedToLoad(mAdapter,
                        AdRequest._ERROR_CODE_INVALID_REQUEST_);
                break;
            case _NETWORK_ERROR_:
                mNativeListener.onAdFailedToLoad(mAdapter, AdRequest._ERROR_CODE_NETWORK_ERROR_);
                break;
            case _NO_INVENTORY_:
                mNativeListener.onAdFailedToLoad(mAdapter, AdRequest._ERROR_CODE_NO_FILL_);
                break;
        }
    }
}

Notice that the class extends the SampleNativeAdListener class of the Sample SDK. In SampleAdapter.java (excerpt) an instance of this forwarder class is given to the SampleNativeAdLoader in its setNativeAdListener() method:

loader.setNativeAdListener(new SampleNativeMediationEventForwarder(listener, this));

This ensures that the Sample SDK calls the SampleNativeMediationEventForwarder object's listener methods (onNativeAppInstallAdFetched(), onNativeContentAdFetched(), and onAdFetchFailed()) when it has an event to report. The forwarder then calls the appropriate method on the Google Mobile Ads SDK's MediationNativeListener, forwarding the event. The Google Mobile Ads SDK listens to the forwarder, which is listening to the mediated SDK.

The onAdFetchFailed() method above is a great example of how this works. When the Sample SDK fails to load an ad, it calls the forwarder's onAdFetchFailed() method and gives it an error code. The forwarder examines the error code and calls the onAdFailedToLoad() method from MediationNativeListener with one of the error codes used by the Google Mobile Ads SDK. In this way, a Sample SDK event is turned into a Google Mobile Ads SDK event.

Map native ads

Different SDKs have their own unique formats for native ads. One might return objects that contain a "title" field, for instance, while another might have a "headline" field. Additionally, the methods used to track impressions and process clicks may vary from one SDK to another. The NativeAdMapper smooths out these wrinkles and adapts a mediated SDK's native ad object to match the interface expected by the Google Mobile Ads SDK.

Each of AdMob's system-defined native ad formats has a corresponding NativeAdMapper class: NativeAppInstallAdMapper maps app install ads and NativeContentAdMapper maps content ads.

Native mediation adapters should subclass these to create their own mappers specific to their mediated SDK.

Here's a sample native app install ad mapper from our sample native mediation adapter project:

SampleNativeAppInstallAdMapper.java

public class SampleNativeAppInstallAdMapper extends NativeAppInstallAdMapper {

    private final SampleNativeAppInstallAd mSampleAd;
    private NativeAdOptions mNativeAdOptions;
    private ImageView mInformationIconView;

    public SampleNativeAppInstallAdMapper(SampleNativeAppInstallAd ad, NativeAdOptions adOptions) {
        mSampleAd = ad;
        mNativeAdOptions = adOptions;
        setHeadline(mSampleAd.getHeadline());
        setBody(mSampleAd.getBody());
        setCallToAction(mSampleAd.getCallToAction());
        setStarRating(mSampleAd.getStarRating());
        setStore(mSampleAd.getStoreName());
        setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),
                SampleAdapter.SAMPLE_SDK_IMAGE_SCALE));

        List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>();
        imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),
                SampleAdapter.SAMPLE_SDK_IMAGE_SCALE));
        setImages(imagesList);

        NumberFormat formatter = NumberFormat._getCurrencyInstance_();
        String priceString = formatter.format(mSampleAd.getPrice());
        setPrice(priceString);

        Bundle extras = new Bundle();
        extras.putString(SampleAdapter._DEGREE_OF_AWESOMENESS_, ad.getDegreeOfAwesomeness());
        this.setExtras(extras);

        setOverrideClickHandling(false);
        setOverrideImpressionRecording(false);
    }

    @Override
    public void recordImpression() {
        mSampleAd.recordImpression();
    }

    @Override
    public void handleClick(View view) {
        mSampleAd.handleClick(view);
    }
}

Let's take a look at the constructor method and some of the work it's doing:

Hold a reference to the mediated native ad and options objects

The constructor accepts SampleNativeAppInstallAd and NativeAdOptions parameters. SampleNativeAppInstallAd is the native ad class used by the Sample SDK for its app install ads. The mapper needs a reference to the mediated ad so it can pass on click and impression events. NativeAdOptions contains publisher preferences about the native ad. Both parameters are stored as local variables.

Set mapped asset properties

This mapper class inherits some app install-specific asset properties from its parent, NativeAppInstallAdMapper. The constructor retrieves asset data from the mediated ad and uses it to populate those properties. These lines, for example, get the mediated ad's price data and uses it to set the mapper's property of the same name:

NumberFormat formatter = NumberFormat.getCurrencyInstance();
String priceString = formatter.format(mSampleAd.getPrice());
setPrice(priceString);

In this case, the mediated ad stores the price as a double, while AdMob uses a String for the same asset. The mapper is responsible for handling these datatype conversions.

Map image assets

Mapping image assets is more complicated than simpler data types like double or String. Images might be downloaded automatically or simply returned as URL values. Their pixel density can also vary. To help native mediation adapter developers manage these details, the Google Mobile Ads SDK provides the NativeAd.Image class. In much the same way that developers need to create a subclass of NativeAdMapper to map a mediated native ad, they should also create a subclass of NativeAd.Image to help them map its image assets.

The SampleNativeAppInstallAdMapper uses the SampleNativeMappedImage class to set the mapper's icon asset:

setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),
        SampleAdapter.SAMPLE_SDK_IMAGE_SCALE));

Also, to set the mapper's images asset:

List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>();
imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),
        SampleAdapter.SAMPLE_SDK_IMAGE_SCALE));
setImages(imagesList);

Here's the code for the native mediation adapter's SampleNativeMappedImage class:

public class SampleNativeMappedImage extends NativeAd.Image {

    private Drawable mDrawable;
    private Uri mImageUri;
    private double mScale;

    public SampleNativeMappedImage(Drawable drawable, Uri imageUri, double scale) {
        mDrawable = drawable;
        mImageUri = imageUri;
        mScale = scale;
    }

    @Override
    public Drawable getDrawable() {
        return mDrawable;
    }

    @Override
    public Uri getUri() {
        return mImageUri;
    }

    @Override
    public double getScale() {
        return mScale;
    }
}

In this case, the mapped image class merely provides a convenient structure for holding the image and its metadata. Your native mediation adapter's mapped image class can, however, include other code necessary for converting the image.

Add additional fields to the extras Bundle

Some mediated SDKs may provide additional assets beyond those in the AdMob native ad formats. The NativeAdMapper class includes a Bundle property called extras that is used to pass these assets on to publishers. For example, the SampleNativeAppInstallAdMapper makes use of this for the Sample SDK's "degree of awesomeness" asset:

Bundle extras = new Bundle();
extras.putString(SampleAdapter.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());
this.setExtras(extras);

A Bundle is a data structure that stores key-value pairs, so multiple assets can be included in a single bundle with distinct keys. Publishers can retrieve the data using the getExtras() method of NativeAdMapper.

AdChoices

Your adapter is responsible for placing your AdChoices icon in Google's native ad view. You should use the trackView and untrackView APIs provided in NativeAppInstallAdMapper and NativeContentAdMapper to add and remove your AdChoices view.

The adapter must also respect the publisher's preference of AdChoices location by checking at the getAdChoicesPlacement() method in NativeAdOptions and rendering the ad in the publisher's preferred corner. If a preferred corner is not set, AdChoices should be rendered in the top right corner.

Here's a snippet from SampleNativeAppInstallAdMapper.java showing how to respect the publisher's AdChoices placement and render the AdChoices icon:

@Override
public void trackView(View view) {
    super.trackView(view);

    if (!(view instanceof ViewGroup)) {
        Log.w(SampleAdapter.TAG, "Failed to show information icon. Ad view not a ViewGroup.");
        return;
    }

    ViewGroup adView = (ViewGroup) view;
    // Find the overlay view in the given ad view. The overlay view will always be the
    // top most view in the hierarchy.
    View overlayView = adView.getChildAt(adView.getChildCount() - 1);
    if (overlayView instanceof FrameLayout) {

        // The sample SDK returns a view for AdChoices asset. If your SDK provides image and
        // click through URLs instead of the view asset, the adapter is responsible for
        // downloading the icon image and creating the AdChoices icon view.

        // Get the information icon provided by the Sample SDK.
        mInformationIconView = mSampleAd.getInformationIcon();

        // Add the view to the overlay view.
        ((ViewGroup) overlayView).addView(mInformationIconView);

        // We know that the overlay view is a FrameLayout, so we get the FrameLayout's
        // LayoutParams from the AdChoicesView.
        FrameLayout.LayoutParams params =
                (FrameLayout.LayoutParams) mInformationIconView.getLayoutParams();

        // Note: The NativeAdOptions' getAdChoicesPlacement() preference requires
        // Google Play Services 9.6.0 and higher.
        if (mNativeAdOptions == null) {
            // Default to top right if native ad options are not provided.
            params.gravity = Gravity.TOP | Gravity.RIGHT;
        } else {
            switch (mNativeAdOptions.getAdChoicesPlacement()) {
                case NativeAdOptions.ADCHOICES_TOP_LEFT:
                    params.gravity = Gravity.TOP | Gravity.LEFT;
                    break;
                case NativeAdOptions.ADCHOICES_BOTTOM_RIGHT:
                    params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
                    break;
                case NativeAdOptions.ADCHOICES_BOTTOM_LEFT:
                    params.gravity = Gravity.BOTTOM | Gravity.LEFT;
                    break;
                case NativeAdOptions.ADCHOICES_TOP_RIGHT:
                default:
                    params.gravity = Gravity.TOP | Gravity.RIGHT;
            }
        }
        adView.requestLayout();
    } else {
        Log.w(SampleAdapter.TAG, "Failed to show information icon. Overlay view not found.");
    }
}

@Override
public void untrackView(View view) {
    super.untrackView(view);

    // Remove the previously added AdChoices view from the ad view.
    ViewParent parent = mInformationIconView.getParent();
    if (parent != null && parent instanceof ViewGroup) {
        ((ViewGroup) parent).removeView(mInformationIconView);
    }
}

Impression and click events

It's important that the mediated SDK be notified any time an impression or click occurs, so the Google Mobile Ads SDK helps native mediation adapter developers report these events. There are two different approaches native mediation adapters can use, depending on what the mediated network's SDK requires.

Handle clicks and impressions with handleClick and recordImpression

If the mediated native ad objects provide methods to record clicks and impressions, a native mediation adapter's mapper classes can use them. This is the most common approach. The NativeAdMapper includes two methods, recordImpression() and handleClick(), which native mediation adapters should implement to call the corresponding method on the mediated native ad object.

Here's how the SampleNativeAppInstallAdMapper handles this:

@Override
public void recordImpression() {
    mSampleAd.recordImpression();
}

@Override
public void handleClick(View view) {
    mSampleAd.handleClick(view);
}

Because the SampleNativeAppInstallAdMapper holds a reference to the Sample SDK's native ad object (mSampleAd), it can simply call the appropriate method on that object to report a click or impression. Note that the handleClick() method has a single parameter, which is the View object corresponding to the native ad asset that received the click.

Override the default click handling and impression recording

Some mediated SDKs may prefer to track clicks and impressions on their own. In that case, you should override the default click and impression tracking by making the following two calls in the constructor of your NativeAdMapper:

setOverrideClickHandling(true);
setOverrideImpressionRecording(true);

(As noted above, adapters that track clicks and impressions for themselves are required to report them to the Google Mobile Ads SDK via the onAdClicked() and onAdImpression() methods.)

You should also override the trackView() method in NativeAdMapper and use it to pass the native ad's view to the mediated SDK's native ad object, so the mediated SDK can use it for tracking purposes. The sample SDK from our native mediation adapter sample project (from which this guide's code snippets have been taken) doesn't use this approach, but if it did, the native mediation adapter code would look like this:

@Override
public void trackView(View view) {
    mSampleAd.setNativeAdViewForTracking(view);
}

The NativeAdMapper offers a corresponding untrackView() method that adapters should override for the opposite purpose (releasing any references to the view and disassociating it from the native ad object).

You now have everything you need to write your own Android native mediation adapter for AdMob.

Send feedback about...

AdMob for Android
AdMob for Android
Need help? Visit our support page.