Prerequisites
To implement Ad Manager custom events, you must first integrate the Mobile Ads SDK and integrate the native ad format into your app.
You may also want to first read about building an AdLoader
and loading a
native ad, and how
mediation works.
Configure 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 Google Ad Manager UI. Create and manage yield groups provides instructions on how to configure a yield group with a custom event.
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 Ad Manager'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 the Ad Manager UI.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:

Sample SDK
The code snippets in this guide are taken 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.
The Sample SDK features classes similar to what you'd find in an ad network's
production SDK. There are request objects such as SampleNativeAdRequest
, ad
loaders such as 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:
Java
void requestNativeAd(Context context, CustomEventNativeListener listener, String serverParameter, NativeMediationAdRequest nativeMediationAdRequest, Bundle customEventExtras);
Kotlin
override fun requestNativeAd(context: Context, listener: CustomEventNativeListener, serverParameter: String, nativeMediationAdRequest: NativeMediationAdRequest, customEventExtras: Bundle?)
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).nativeMediationAdRequest
- ANativeMediationAdRequest
object, which contains properties specific to a single request, such as which native formats are requested.NativeMediationAdRequest
is a subclass ofMediationAdRequest
, so it also includes properties for other targeting information provided by publishers at request time.customEventExtras
- ABundle
containing any extra information relevant to the request provided by the publisher. When creating an ad request, 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
- AContext
object which can be used if the custom event requires one.listener
- ACustomEventNativeListener
object 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 Forward events to the Mobile Ads SDK.
Here's a code snippet from our example custom event
project that
shows an implemented requestNativeAd()
method:
SampleCustomEvent (excerpt)
Java
@Override public void requestNativeAd(Context context, CustomEventNativeListener customEventNativeListener, String serverParameter, NativeMediationAdRequest nativeMediationAdRequest, Bundle extras) { // Create one of the Sample SDK's ad loaders from which to request ads. SampleNativeAdLoader loader = new SampleNativeAdLoader(context); loader.setAdUnit(serverParameter); // Create a native request to give to the SampleNativeAdLoader. SampleNativeAdRequest request = new SampleNativeAdRequest(); NativeAdOptions options = nativeMediationAdRequest.getNativeAdOptions(); if (options != null) { // If the NativeAdOptions' shouldReturnUrlsForImageAssets is true, the adapter should // send just the URLs for the images. request.setShouldDownloadImages(!options.shouldReturnUrlsForImageAssets()); // If your network does not support any of the following options, please make sure // that it is documented in your adapter's documentation. request.setShouldDownloadMultipleImages(options.shouldRequestMultipleImages()); switch (options.getImageOrientation()) { case NativeAdOptions.ORIENTATION_LANDSCAPE: request.setPreferredImageOrientation( SampleNativeAdRequest.IMAGE_ORIENTATION_LANDSCAPE); break; case NativeAdOptions.ORIENTATION_PORTRAIT: request.setPreferredImageOrientation( SampleNativeAdRequest.IMAGE_ORIENTATION_PORTRAIT); break; case NativeAdOptions.ORIENTATION_ANY: default: request.setPreferredImageOrientation( SampleNativeAdRequest.IMAGE_ORIENTATION_ANY); } } loader.setNativeAdListener( new SampleCustomNativeEventForwarder(customEventNativeListener, options)); // Begin a request. loader.fetchAd(request); }
Kotlin
override fun requestNativeAd(context: Context, customEventNativeListener: CustomEventNativeListener, serverParameter: String, nativeMediationAdRequest: NativeMediationAdRequest, extras: Bundle) { // Create one of the Sample SDK's ad loaders from which to request ads. val loader = SampleNativeAdLoader(context) loader.setAdUnit(serverParameter) // Create a native request to give to the SampleNativeAdLoader. val request = SampleNativeAdRequest() val options = nativeMediationAdRequest.nativeAdOptions if (options != null) { // If the NativeAdOptions' shouldReturnUrlsForImageAssets is true, the adapter should // send just the URLs for the images. request.shouldDownloadImages = !options.shouldReturnUrlsForImageAssets() // If your network does not support any of the following options, please make sure // that it is documented in your adapter's documentation. request.setShouldDownloadMultipleImages(options.shouldRequestMultipleImages()) when (options.imageOrientation) { NativeAdOptions.ORIENTATION_LANDSCAPE -> request.setPreferredImageOrientation( SampleNativeAdRequest.IMAGE_ORIENTATION_LANDSCAPE) NativeAdOptions.ORIENTATION_PORTRAIT -> request.setPreferredImageOrientation( SampleNativeAdRequest.IMAGE_ORIENTATION_PORTRAIT) NativeAdOptions.ORIENTATION_ANY -> request.setPreferredImageOrientation( SampleNativeAdRequest.IMAGE_ORIENTATION_ANY) else -> request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_ANY) } } loader.setNativeAdListener( SampleCustomNativeEventForwarder(customEventNativeListener, options)) // Begin a request. 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 the 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.
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:
Java
NativeAdOptions options = nativeMediationAdRequest.getNativeAdOptions();
Kotlin
val options = nativeMediationAdRequest.nativeAdOptions
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 listen for events from the mediated
SDK and map them to the appropriate CustomEventNativeListener
callbacks.
Your custom event needs to be aware of the following
CustomEventNativeListener
callbacks:
onAdLoaded()
- This method should be called when a custom event successfully loads a native ad. It takes aUnifiedNativeAdMapper
, which maps your ad network SDK's native ad to an object the Google Mobile Ads SDK understands. Map native ads covers how to create aUnifiedNativeAdMapper
.onAdFailedToLoad()
- When the custom event tries and fails to load a native ad, it should use this method to report the failure.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 callonAdOpened
just 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 app 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
// Domain for your custom event. Typically represented by the package name. private static final String DOMAIN = "com.google.ads.mediation.sample.customevent"; public class SampleCustomNativeEventForwarder extends SampleNativeAdListener { private CustomEventNativeListener nativeListener; private NativeAdOptions nativeAdOptions; public SampleCustomNativeEventForwarder( CustomEventNativeListener listener, NativeAdOptions options) { this.nativeListener = listener; this.nativeAdOptions = options; } @Override public void onNativeAdFetched(SampleNativeAd ad) { SampleNativeAdMapper mapper = new SampleNativeAdMapper(ad, nativeAdOptions); nativeListener.onAdLoaded(mapper); } @Override public void onAdFetchFailed(SampleErrorCode errorCode) { AdError error = new AdError( errorCode.ordinal(), "Sample SDK returned an error: " + errorCode.name(), DOMAIN); nativeListener.onAdFailedToLoad(error); } }
Kotlin
// Domain for your custom event. Typically represented by the package name. const val DOMAIN = "com.google.ads.mediation.sample.customevent" class SampleCustomNativeEventForwarder( private val nativeListener: CustomEventNativeListener, private val nativeAdOptions: NativeAdOptions) : SampleNativeAdListener() { fun onNativeAdFetched(ad: SampleNativeAd) { val mapper = SampleNativeAdMapper(ad, nativeAdOptions) nativeListener.onAdLoaded(mapper) } fun onAdFetchFailed(errorCode: SampleErrorCode) { val error = AdError( errorCode.ordinal, "Sample SDK returned an error: " + errorCode.name, DOMAIN) nativeListener.onAdFailedToLoad(error) } }
Notice that the class implements Sample SDK's SampleNativeAdListener
interface.
In SampleCustomEvent (excerpt)
an instance of this forwarder class was given to the SampleNativeAdLoader
in its setNativeAdListener()
method:
Java
loader.setNativeAdListener(new SampleCustomNativeEventForwarder(customEventNativeListener, options));
Kotlin
loader.setNativeAdListener(SampleCustomNativeEventForwarder(customEventNativeListener, options))
This ensures that Sample SDK calls the SampleCustomNativeEventForwarder
object's listener methods (onNativeAdFetched()
,
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
UnifiedNativeAdMapper
to reconcile these differences and adapt a mediated SDK's native ad object to
match the interface expected by the Google Mobile Ads SDK. Custom events should
extend this class to create their own mappers specific to their mediated SDK.
Here's a sample ad mapper from our example custom event project:
SampleNativeAdMapper
Java
public class SampleNativeAdMapper extends UnifiedNativeAdMapper { private final SampleNativeAd sampleAd; public SampleNativeAdMapper(SampleNativeAd ad) { sampleAd = ad; setHeadline(sampleAd.getHeadline()); setBody(sampleAd.getBody()); setCallToAction(sampleAd.getCallToAction()); setStarRating(sampleAd.getStarRating()); setStore(sampleAd.getStoreName()); setIcon(new SampleNativeMappedImage(ad.getIcon(), ad.getIconUri(), SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE)); setAdvertiser(ad.getAdvertiser()); List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>(); imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(), SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE)); setImages(imagesList); if (sampleAd.getPrice() != null) { NumberFormat formatter = NumberFormat.getCurrencyInstance(); String priceString = formatter.format(sampleAd.getPrice()); setPrice(priceString); } Bundle extras = new Bundle(); extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness()); this.setExtras(extras); setOverrideClickHandling(false); setOverrideImpressionRecording(false); setAdChoicesContent(sampleAd.getInformationIcon()); } @Override public void recordImpression() { sampleAd.recordImpression(); } @Override public void handleClick(View view) { sampleAd.handleClick(view); } }
Kotlin
class SampleNativeAdMapper(private val sampleAd: SampleNativeAd) : NativeAdMapper() { init { headline = sampleAd.headline body = sampleAd.body callToAction = sampleAd.callToAction starRating = sampleAd.starRating store = sampleAd.storeName icon = SampleNativeMappedImage(sampleAd.icon, sampleAd.iconUri, SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE) advertiser = sampleAd.advertiser val imagesList = ArrayList<NativeAd.Image>() imagesList.add(SampleNativeMappedImage(sampleAd.image, sampleAd.imageUri, SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE)) images = imagesList if (sampleAd.price != null) { val formatter = NumberFormat.getCurrencyInstance() val priceString = formatter.format(sampleAd.price) price = priceString } val extras = Bundle() extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, sampleAd.degreeOfAwesomeness) this.extras = extras overrideClickHandling = false overrideImpressionRecording = false adChoicesContent = sampleAd.informationIcon } override fun recordImpression() { sampleAd.recordImpression() } override fun handleClick(view: View?) { sampleAd.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 SampleNativeAd
and NativeAdOptions
parameters.
SampleNativeAd
is the native ad class used by the Sample SDK for
its native 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
The constructor uses the SampleNativeAd
object to populate assets in the
UnifiedNativeAdMapper
. This code, for example, get the mediated ad's price
data and uses it to set the mapper's price:
Java
if (sampleAd.getPrice() != null) { NumberFormat formatter = NumberFormat.getCurrencyInstance(); String priceString = formatter.format(sampleAd.getPrice()); setPrice(priceString); }
Kotlin
if (sampleAd.price != null) { val formatter = NumberFormat.getCurrencyInstance() val priceString = formatter.format(sampleAd.price) price = priceString }
In this case, the mediated ad stores the price as a double
, while
Ad Manager 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 UnifiedNativeAdMapper
to map a mediated native ad,
they should also create a subclass of NativeAd.Image
to help them map
its image assets.
Here's the code for the custom event's SampleNativeMappedImage
class:
Java
public class SampleNativeMappedImage extends NativeAd.Image { private Drawable drawable; private Uri imageUri; private double scale; public SampleNativeMappedImage(Drawable drawable, Uri imageUri, double scale) { this.drawable = drawable; this.imageUri = imageUri; this.scale = scale; } @Override public Drawable getDrawable() { return drawable; } @Override public Uri getUri() { return imageUri; } @Override public double getScale() { return scale; } }
Kotlin
class SampleNativeMappedImage(private val drawable: Drawable, private val imageUri: Uri, private val scale: Double) : NativeAd.Image() { override fun getDrawable(): Drawable { return drawable } override fun getUri(): Uri { return imageUri } override fun getScale(): Double { return scale } }
The SampleNativeAdMapper
uses its mapped image class in this
line to set the mapper's icon image asset:
Java
setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(), SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
Kotlin
icon = SampleNativeMappedImage(sampleAd.appIcon, sampleAd.appIconUri, SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE)
Add additional fields to the extras Bundle
Some mediated SDKs may provide additional assets beyond those in the
Ad Manager native ad format. The
UnifiedNativeAdMapper
class includes a
setExtras()
method that is used to pass these assets on to publishers. The
SampleNativeAdMapper
makes use of this for the Sample SDK's
"degree of awesomeness" asset:
Java
Bundle extras = new Bundle(); extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness()); this.setExtras(extras);
Kotlin
val extras = Bundle() extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, sampleAd.degreeOfAwesomeness) this.extras = extras
Publishers can retrieve the data using the NativeAd
class's
getExtras()
method.
AdChoices
Your custom event is responsible for providing an AdChoices icon via the
setAdChoicesContent()
method on UnifiedNativeAdMapper
.
Here's a snippet from SampleNativeAdMapper
showing how to provide the AdChoices icon:
Java
public SampleNativeAdMapper(SampleNativeAd ad) { ... setAdChoicesContent(sampleAd.getInformationIcon()); }
Kotlin
init { ... adChoicesContent = sampleAd.informationIcon }
Impression and click events
Both the Google Mobile Ads SDK and the mediated SDK need to know when an impression or click occurs, but only one SDK needs to track these events. There are two different approaches custom events can use, depending on whether the mediated SDK supports tracking impressions and clicks on its own.
Handle clicks and impressions with handleClick and recordImpression
If the mediated SDK does not do its own impression and click tracking but
provide methods to record clicks and
impressions, the Google Mobile Ads SDK can track these events and notify the
adapter. The UnifiedNativeAdMapper
includes two methods, recordImpression()
and handleClick()
, which custom events can implement to call the
corresponding method on the mediated native ad object.
Here's how the SampleNativeAdMapper
handles this:
Java
@Override public void recordImpression() { sampleAd.recordImpression(); } @Override public void handleClick(View view) { sampleAd.handleClick(view); }
Kotlin
override fun recordImpression() { sampleAd.recordImpression() } override fun handleClick(view: View) { sampleAd.handleClick(view) }
Because the SampleNativeAdMapper
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
UnifiedNativeAdMapper
:
Java
setOverrideClickHandling(true); setOverrideImpressionRecording(true);
Kotlin
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 these events to the Google Mobile Ads SDK via the
onAdClicked()
and
onAdImpression()
methods.)
You should also override the trackViews()
method and use it to pass the native
ad's view to the mediated SDK to track.
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 something like this:
Java
@Override public void trackViews(View containerView, Map<String, View> clickableAssetViews, Map<String, View> nonClickableAssetViews) { sampleAd.setNativeAdViewForTracking(containerView); }
Kotlin
override fun trackViews(containerView: View?, clickableAssetViews: Map<String, View>?, nonClickableAssetViews: Map<String, View>?) { sampleAd.setNativeAdViewForTracking(containerView) }
If the mediated SDK supports tracking individual assets, it can look inside
clickableAssetViews
to see which views should be made clickable. This map is
keyed by an asset name in
NativeAdAssetNames
.
The UnifiedNativeAdMapper
offers a corresponding
untrackView()
method that custom events can override for the opposite purpose (releasing
any references to the view and disassociating it from the native ad object).
This completes the custom events implementation for native ads. A complete example is available on GitHub.