This guide is intended for publishers who want to use AdMob Mediation to load and display native ads from a network that isn't one of AdMob's supported ad networks. (See Custom Events for information about using AdMob Mediation to load and display banner, interstitial, or rewarded display ads.) Custom events are mediation adapter classes capable of requesting ads from another network. When you add the name of one of these classes to the mediation settings for an ad unit, the SDK can instantiate and use it to retrieve ads. Native custom event classes need to be able to do the following:
- Request native ads from a mediated network.
- Forward events from the mediated network's SDK to the Google Mobile Ads SDK.
- Use a
NativeAdMapperto 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 a sample custom event project in our GitHub repository.
Prerequisites
To implement an AdMob custom event, you must first
integrate the Mobile Ads SDK
into your project. You may also want to first read about making an
AdRequest
and how mediation works.
Sample SDK
The code snippets in this guide come from our sample custom event project which includes a "Sample SDK". This mock SDK is used in our example of how to build a custom event that mediates another ad network's SDK.
Sample SDK features classes similar to what you'd find in an ad
network's production SDK. There are request objects like
SampleNativeAdRequest, ad loaders like SampleNativeAdLoader,
and other classes, constants, and interfaces used to simulate a real
network's SDK. The ads that it produces are mocks, though, and no
additional network traffic is generated.
Request a native ad
The requestNativeAd() method
Custom event classes must implement the CustomEventNative interface,
which includes a method used by the Google Mobile Ads SDK to request
native ads from the custom event:
void requestNativeAd(Context context,
CustomEventNativeListener listener,
String serverParameter,
NativeMediationAdRequest mediationAdRequest,
Bundle customEventExtras);
When this method is called, the custom event should asynchronously
request a native ad from the mediated network. The following parameters
for requestNativeAd() carry information that the custom event can use
in making its request:
serverParameter- When adding a custom event to an ad unit's mediation configuration, publishers can enter a string value to be passed along with each request. This parameter holds that value (typically another ad unit ID, which was issued by the mediated network).mediationAdRequest- ANativeMediationAdRequestobject, which contains properties specific to a single request, such as which native formats are requested.NativeMediationAdRequestis a subclass ofMediationAdRequest, so it also includes properties for other targeting information provided by publishers at request time.customEventExtras- ABundlecontaining any "extra" information relevant to the request provided by the publisher. When creating anAdRequest, publishers can use theaddNetworkExtrasBundle()method to include information for specific mediation adapters and custom events. Any extras included by the publisher for a given custom event class are found in this parameter.
In addition to the parameters carrying request information, there are two others:
context- AContextobject which can be used if the custom event requires one.listener- ACustomEventNativeListenerobject that the custom event 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.
Here's a code snippet from our example custom event
project that
shows an implemented requestNativeAd() method:
SampleCustomEvent.java (excerpt)
@Override
public void requestNativeAd(Context context,
CustomEventNativeListener customEventNativeListener,
String serverParameter,
NativeMediationAdRequest nativeMediationAdRequest,
Bundle extras) {
SampleNativeAdLoader loader = new SampleNativeAdLoader(context);
loader.setAdUnit(serverParameter);
SampleNativeAdRequest request = new SampleNativeAdRequest();
NativeAdOptions options = nativeMediationAdRequest.getNativeAdOptions();
if (options != null) {
request.setShouldDownloadImages(options.shouldReturnUrlsForImageAssets());
} else {
// Set a default value for the one native ad option the Sample SDK supports.
request.setShouldDownloadImages(true);
}
request.setAppInstallAdsRequested(nativeMediationAdRequest.isAppInstallAdRequested());
request.setContentAdsRequested(nativeMediationAdRequest.isContentAdRequested());
loader.setNativeAdListener(
new SampleCustomNativeEventForwarder(customEventNativeListener, options));
loader.fetchAd(request);
}
This is a sample custom event that mediates to an imaginary ad network
via the mock Sample SDK. The custom event uses the information provided
in the parameters of requestNativeAd() to build a SampleNativeAdRequest
(a class provided by Sample SDK), then uses it to request an ad from
Sample SDK's SampleNativeAdLoader. It also creates a
SampleCustomEventNativeForwarder to handle forwarding events back to
the Google Mobile Ads SDK. That process is the topic of the next section.
Native ad formats
As noted above, the NativeMediationAdRequest object includes information
about which native ad formats the publisher is requesting. It's
important that custom events 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.
For example, if the mediated ad network only ever provides app install ads and
the publisher requests content ads alone, your custom event 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
CustomEventNativeListener 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 custom event needs to examine those
preferences and respect them. Custom events can retrieve a request's
NativeAdOptions object using the getNativeAdOptions() method provided
by the NativeMediationAdRequest object:
NativeAdOptions options = mediationAdRequest.getNativeAdOptions();
After retrieving the NativeAdOptions, custom events can read its
properties and act accordingly. For example, if the
shouldReturnUrlsForImageAssets value is false in NativeAdOptions
(which is the default), the custom event must return actual image
assets rather than just the URLs. In this case, if the SDK being
mediated only provides URLs for images, then the custom event 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 custom event 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 CustomEventNativeListener
object as a parameter to the requestNativeAd() method.
It's part of a custom event's job to act as a "man-in-the-middle,"
listening for events from the mediated SDK, and then calling the
corresponding CustomEventNativeListener() method as appropriate.
Your custom event needs to be aware of the following methods :
onAdLoaded()- This method should be called when a custom event successfully loads a native ad. It takes a single parameter of typeNativeAdMapper, which the custom event should use to pass a mapped version of the native ad it just loaded (NativeAdMapperclasses are covered in the next section).onAdFailedToLoad()- When the custom event tries and fails to load a native ad, it should use this method to report the failure. It takes a single 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, this method should be invoked. This includes an external browser, in which case your custom event should callonAdOpenedjust beforeonAdLeftApplication.onAdClosed()- If an overlay or separate activity covering the interface is closed, this method should be invoked, signifying that control is being transferred back to the application containing the native ad.
The listener also offers methods for reporting clicks and impressions from custom events that choose to track clicks or impressions for themselves (see Override the default click handling and impression recording for details). One is for reporting a click to the Google Mobile Ads SDK, and one is for reporting an impression:
onAdClicked()- This method should be called when a user clicks on a native ad.onAdImpression()- Similarly, this method should be called 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. Here's one from our custom event sample project:
SampleCustomNativeEventForwarder.java
public class SampleCustomNativeEventForwarder extends SampleNativeAdListener {
private CustomEventNativeListener mNativeListener;
private NativeAdOptions mNativeAdOptions;
public SampleCustomNativeEventForwarder(
CustomEventNativeListener listener, NativeAdOptions adOptions) {
this.mNativeListener = listener;
this.mNativeAdOptions = adOptions;
}
@Override
public void onNativeAppInstallAdFetched(SampleNativeAppInstallAd ad) {
SampleNativeAppInstallAdMapper mapper = new SampleNativeAppInstallAdMapper(ad, mNativeAdOptions);
mNativeListener.onAdLoaded(mapper);
}
@Override
public void onNativeContentAdFetched(SampleNativeContentAd ad) {
SampleNativeContentAdMapper mapper = new SampleNativeContentAdMapper(ad, mNativeAdOptions);
mNativeListener.onAdLoaded(mapper);
}
@Override
public void onAdFetchFailed(SampleErrorCode errorCode) {
switch (errorCode) {
case UNKNOWN:
mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_INTERNAL_ERROR);
break;
case BAD_REQUEST:
mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST);
break;
case NETWORK_ERROR:
mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_NETWORK_ERROR);
break;
case NO_INVENTORY:
mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL);
break;
}
}
}
Notice that the class implements Sample SDK's SampleNativeAdListener
interface.
In SampleCustomEvent.java (excerpt)
an instance of this forwarder class was given to the SampleNativeAdLoader
in its setNativeAdListener() method:
loader.setNativeAdListener(new SampleCustomNativeEventForwarder(customEventNativeListener));
This ensures that Sample SDK calls the SampleCustomNativeEventForwarder
object's listener methods (onNativeAppInstallAdFetched(),
onAdFetchFailed(), and so on) when it has an event to report. The forwarder
then calls the appropriate method on the Google Mobile Ads SDK's
CustomEventNativeListener, forwarding the event. In short, 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 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
of the CustomEventNativeListener 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 "headline." Additionally, the methods used to track impressions
and process clicks may vary from one SDK to another. It's the job of the
NativeAdMapper
to smooth out these wrinkles and adapt 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. Custom events should subclass these to create their own
mappers specific to their mediated SDK.
Here's a sample app install ad mapper from our example custom event 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(),
SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>();
imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),
SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
setImages(imagesList);
NumberFormat formatter = NumberFormat.getCurrencyInstance();
String priceString = formatter.format(mSampleAd.getPrice());
setPrice(priceString);
Bundle extras = new Bundle();
extras.putString(SampleCustomEvent.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 object
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 types of 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-to-dpi scales can also vary. To help
custom event 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 its mapped image class in this
line to set the mapper's icon image asset:
setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),
SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
Here's the code for the custom event'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 custom event's mapped image classes can, however, include other code necessary for the conversion.
Add additional fields to the extras Bundle
Some mediated SDKs may provide additional assets beyond those in the
AdMob native ad formats. The NativeAdMapper classes
include a Bundle property called extras that is used to pass these
assets on to publishers. The mapper above makes use of this for Sample
SDK's "degree of awesomeness" asset:
Bundle extras = new Bundle();
extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());
this.setExtras(extras);
Bundles are key-value data structures, so multiple assets should be
included in a single bundle with distinct keys. Publishers can retrieve
the data using the NativeAd class's getExtras() method.
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(SampleCustomEvent.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(SampleCustomEvent.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 custom event developers report these events. There are two different approaches custom events 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 custom event's mapper classes can use them. This is the
most common approach. The NativeAdMapper includes two methods,
recordImpression() and handleClick(), which custom events should
override and use 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, 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 in Forward events to the Mobile Ads
SDK,
custom events 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 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 custom event example project (from which this guide's code snippets
have been taken) doesn't use this approach, but if it did, the custom
event code would look like this:
public void trackView(View view) {
mSampleAd.setNativeAdViewForTracking(view);
}
The NativeAdMapper offers a corresponding
untrackView()
method that custom events should override for the opposite purpose
(releasing any references to the view and disassociating it from the
native ad object).
Use a custom event
To use a custom event, you need to add it to the mediation configuration for an ad unit. This is done in the AdMob interface. (Create a custom event provides detailed instructions for editing an ad unit's mediation configuration).
When you add the custom event to an ad unit's mediation configuration, you are asked for three pieces of information:
Class Name- This is the class name of your custom event, with the full package name included.Label- This is the label you'd like AdMob's interface to use to represent the custom event when it displays the ad unit's mediation sources. It's just for you, as it only ever appears on AdMob.com.Parameter- This is a string value that is passed to the custom event whenever requests for this ad unit are made. Typically this value is set to an ad unit ID from the mediated network.
Here's a screenshot of an example custom event entry:

That's it! You now have everything you need to write your own Android custom events for AdMob.