原生广告自定义事件

前提条件

完成自定义事件设置

请求原生广告

在达到广告瀑布流中介链内的自定义事件订单项时,系统会对您在创建自定义事件时提供的类名称调用 loadNativeAd() 方法。在这种情况下,该方法位于 SampleCustomEvent 中,后者随后会调用 SampleNativeCustomEventLoader 中的 loadNativeAd() 方法。

若要请求原生广告,请创建或修改扩展 Adapter 以实现 loadNativeAd() 的类。如果扩展 Adapter 的类已存在,请在该类中实现 loadNativeAd()。此外,请创建一个新类来实现 UnifiedNativeAdMapper

在我们的自定义事件示例中,SampleCustomEvent 会扩展 Adapter 类,然后委托给 SampleNativeCustomEventLoader

Java

package com.google.ads.mediation.sample.customevent;

import com.google.android.gms.ads.mediation.Adapter;
import com.google.android.gms.ads.mediation.MediationAdConfiguration;
import com.google.android.gms.ads.mediation.MediationAdLoadCallback;

import com.google.android.gms.ads.mediation.MediationNativeAdCallback;
...
public class SampleCustomEvent extends Adapter {
  private SampleNativeCustomEventLoader nativeLoader;

  @Override
  public void loadNativeAd(
      @NonNull MediationNativeAdConfiguration adConfiguration,
      @NonNull MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback> callback) {
    nativeLoader = new SampleNativeCustomEventLoader(adConfiguration, callback);
    nativeLoader.loadAd();
  }
}

SampleNativeCustomEventLoader 负责执行以下任务:

  • 加载原生广告。

  • 实现 UnifiedNativeAdMapper 类。

  • 接收广告事件回调并将其报告给 Google 移动广告 SDK。

Ad Manager 界面中定义的可选参数包含在广告配置中。此参数可通过 adConfiguration.getServerParameters().getString(MediationConfiguration.CUSTOM_EVENT_SERVER_PARAMETER_FIELD) 进行访问。通常,此参数是实例化广告对象时广告联盟 SDK 所需的广告单元标识符。

Java

package com.google.ads.mediation.sample.customevent;

import com.google.android.gms.ads.mediation.Adapter;
import com.google.android.gms.ads.mediation.MediationNativeAdConfiguration;
import com.google.android.gms.ads.mediation.MediationAdLoadCallback;
import com.google.android.gms.ads.mediation.MediationNativeAdCallback;
...

public class SampleNativeCustomEventLoader extends SampleNativeAdListener {
  /** Configuration for requesting the native ad from the third-party network. */
  private final MediationNativeAdConfiguration mediationNativeAdConfiguration;

  /** Callback that fires on loading success or failure. */
  private final MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback>
      mediationAdLoadCallback;

  /** Callback for native ad events. */
  private MediationNativeAdCallback nativeAdCallback;

  /** Constructor */
  public SampleNativeCustomEventLoader(
      @NonNull MediationNativeAdConfiguration mediationNativeAdConfiguration,
      @NonNull MediationAdLoadCallback<MediationNativeAd, MediationNativeAdCallback>
              mediationAdLoadCallback) {
    this.mediationNativeAdConfiguration = mediationNativeAdConfiguration;
    this.mediationAdLoadCallback = mediationAdLoadCallback;
  }

  /** Loads the native ad from the third-party ad network. */
  public void loadAd() {
    // Create one of the Sample SDK's ad loaders to request ads.
    Log.i("NativeCustomEvent", "Begin loading native ad.");
    SampleNativeAdLoader loader =
        new SampleNativeAdLoader(mediationNativeAdConfiguration.getContext());

    // All custom events have a server parameter named "parameter" that returns
    // back the parameter entered into the UI when defining the custom event.
    String serverParameter = mediationNativeAdConfiguration
        .getServerParameters()
        .getString(MediationConfiguration
        .CUSTOM_EVENT_SERVER_PARAMETER_FIELD);
    Log.d("NativeCustomEvent", "Received server parameter.");

    loader.setAdUnit(serverParameter);

    // Create a native request to give to the SampleNativeAdLoader.
    SampleNativeAdRequest request = new SampleNativeAdRequest();
    NativeAdOptions options = mediationNativeAdConfiguration.getNativeAdOptions();
    if (options != null) {
      // If the NativeAdOptions' shouldReturnUrlsForImageAssets is true, the adapter should
      // send just the URLs for the images.
      request.setShouldDownloadImages(!options.shouldReturnUrlsForImageAssets());

      request.setShouldDownloadMultipleImages(options.shouldRequestMultipleImages());
      switch (options.getMediaAspectRatio()) {
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE:
          request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_LANDSCAPE);
          break;
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_PORTRAIT:
          request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_PORTRAIT);
          break;
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_SQUARE:
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_ANY:
        case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_UNKNOWN:
        default:
          request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_ANY);
      }
    }

    loader.setNativeAdListener(this);

    // Begin a request.
    Log.i("NativeCustomEvent", "Start fetching native ad.");
    loader.fetchAd(request);
  }
}

您需要调用 onSuccess()onFailure(),具体取决于广告是已成功提取还是遇到错误。通过传入实现 MediationNativeAd 的类的实例调用 onSuccess()

通常,这些方法是在您的适配器所实现的第三方 SDK 的回调中实现的。在此示例中,示例 SDK 具有一个 SampleAdListener 及相关回调:

Java

@Override
public void onNativeAdFetched(SampleNativeAd ad) {
  SampleUnifiedNativeAdMapper mapper = new SampleUnifiedNativeAdMapper(ad);
  mediationNativeAdCallback = mediationAdLoadCallback.onSuccess(mapper);
}

@Override
public void onAdFetchFailed(SampleErrorCode errorCode) {
  mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));
}

映射原生广告

对于原生广告,不同的 SDK 都有自己独特的广告格式。例如,一个 SDK 可能返回包含“title”字段的对象,而另一个 SDK 返回的对象可能包含“headline”字段。此外,用于跟踪展示和处理点击的方法也可能会因 SDK 而异。

UnifiedNativeAdMapper 负责调和这些差异,并调整参与中介的 SDK 的原生广告对象,使其与 Google 移动广告 SDK 预期的接口相匹配。自定义事件应扩展此类,以创建专属于它们参与中介的 SDK 的映射器。以下是我们的示例自定义事件项目中的一个示例广告映射器:

Java

package com.google.ads.mediation.sample.customevent;

import com.google.android.gms.ads.mediation.UnifiedNativeAdMapper;
import com.google.android.gms.ads.nativead.NativeAd;
...

public class SampleUnifiedNativeAdMapper extends UnifiedNativeAdMapper {

  private final SampleNativeAd sampleAd;

  public SampleUnifiedNativeAdMapper(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);
  }

  // The Sample SDK doesn't do its own impression/click tracking, instead relies on its
  // publishers calling the recordImpression and handleClick methods on its native ad object. So
  // there's no need to pass a reference to the View being used to display the native ad. If
  // your mediated network does need a reference to the view, the following method can be used
  // to provide one.

  @Override
  public void trackViews(View containerView, Map<String, View> clickableAssetViews,
      Map<String, View> nonClickableAssetViews) {
    super.trackViews(containerView, clickableAssetViews, nonClickableAssetViews);
    // If your ad network SDK does its own impression tracking, here is where you can track the
    // top level native ad view and its individual asset views.
  }

  @Override
  public void untrackView(View view) {
    super.untrackView(view);
    // Here you would remove any trackers from the View added in trackView.
  }
}

现在,我们来详细了解一下构造函数代码。

保留对参与中介的原生广告对象的引用

该构造函数接受 SampleNativeAd 参数,即示例 SDK 对其原生广告采用的原生广告类。映射器需要引用参与中介的广告才能传递点击和展示事件。SampleNativeAd 会存储为局部变量。

设置映射的素材资源属性

构造函数使用 SampleNativeAd 对象在 UnifiedNativeAdMapper 中填充素材资源。

以下代码段可获取参与中介的广告的价格数据,然后用该数据设置映射器的价格:

Java

if (sampleAd.getPrice() != null) {
    NumberFormat formatter = NumberFormat.getCurrencyInstance();
    String priceString = formatter.format(sampleAd.getPrice());
    setPrice(priceString);
}

在本例中,参与中介的广告以 double 格式存储价格,而 Ad Manager 对同一素材资源使用的却是 String 格式。映射器会负责处理这些类型转换工作。

映射图片素材资源

与映射 doubleString 等数据类型相比,映射图片素材资源会更加复杂。图片既可以自动下载,也可以作为网址值返回,而且其“像素与 dpi 的比例”可能也不尽相同。

为了帮助您管理这些详细信息,Google 移动广告 SDK 提供了 NativeAd.Image 类。您需要创建 UnifiedNativeAdMapper 的子类来映射参与中介的原生广告,还应采用几乎与此相同的方式创建 NativeAd.Image 的子类来映射图片素材资源。

下面是自定义事件的 SampleNativeMappedImage 类示例:

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;
  }
}

以下代码中的 SampleNativeAdMapper 使用了其映射的图片类来设置映射器的图标图片素材资源:

Java

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

向 extras Bundle 添加字段

除了 Ad Manager 原生广告格式中的素材资源之外,一些参与中介的 SDK 还会提供额外的素材资源。UnifiedNativeAdMapper 类包含一个 setExtras() 方法,用于将这些素材资源传递给发布商。SampleNativeAdMapper 就是采用此方法来传递示例 SDK 的“degree of awesomeness”素材资源:

Java

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

发布商可以使用 NativeAd 类的 getExtras() 方法检索数据。

广告选择

您的自定义事件负责使用 UnifiedNativeAdMapper 中的 setAdChoicesContent() 方法提供“广告选择”图标。以下是 SampleNativeAdMapper 中的一段代码,展示了如何提供“广告选择”图标:

Java

public SampleNativeAdMapper(SampleNativeAd ad) {
    ...
    setAdChoicesContent(sampleAd.getInformationIcon());
}

展示和点击事件

Google 移动广告 SDK 和参与中介的 SDK 都需要知道展示或点击发生的时间,但只需要有一个 SDK 跟踪这些事件。有两种不同做法可供自定义事件选用,具体取决于参与中介的 SDK 是否支持自行跟踪展示和点击。

使用 Google 移动广告 SDK 跟踪点击和展示

如果参与中介的 SDK 不自行执行展示和点击跟踪,但提供了记录点击和展示的方法,则 Google 移动广告 SDK 可以跟踪这些事件并通知适配器。UnifiedNativeAdMapper 类包含 recordImpression()handleClick() 这两个方法,自定义事件可通过实现这两个方法来调用参与中介的原生广告对象中的相应方法:

Java

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

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

SampleNativeAdMapper 中包含对示例 SDK 的原生广告对象的引用,因此可调用该对象中的相应方法来报告点击或展示。请注意,handleClick() 方法仅接受一个参数(即 View 对象),该对象与获得点击的原生广告素材资源相对应。

使用参与中介的 SDK 跟踪点击和展示

一些参与中介的 SDK 可能偏好自行跟踪点击和展示。在这种情况下,您应在 UnifiedNativeAdMapper 的构造函数中执行以下两项调用,以替换默认的点击和展示跟踪方法:

Java

setOverrideClickHandling(true);
setOverrideImpressionRecording(true);

替换点击和展示跟踪方法的自定义事件需要向 Google 移动广告 SDK 报告 onAdClicked()onAdImpression() 事件。

如需跟踪展示和点击次数,参与中介的 SDK 可能需要访问相应视图才能启用跟踪功能。自定义事件应替换 trackViews() 方法,并使用它将原生广告的视图传递给参与中介的 SDK 进行跟踪。我们自定义事件示例项目(本指南中的代码段均选自这一项目)中的示例 SDK 并没有采用这种做法。但如要采用这种做法,则自定义事件代码会大致如下:

Java

@Override
public void trackViews(View containerView,
    Map<String, View> clickableAssetViews,
    Map<String, View> nonClickableAssetViews) {
  sampleAd.setNativeAdViewForTracking(containerView);
}

如果参与中介的 SDK 支持跟踪具体素材资源,则可查看 clickableAssetViews 内部,以了解应设置哪些视图供用户点击。此映射的键是 NativeAdAssetNames 中的素材资源名称。UnifiedNativeAdMapper 提供相应的 untrackView() 方法,自定义事件可以通过替换该方法来释放对视图的所有引用并解除它与原生广告对象之间的关联。

将中介事件转发至 Google 移动广告 SDK

您可以在 MediationNativeAdCallback 文档中找到中介支持的所有回调。

您的自定义事件必须尽可能多地转发这些回调,以便您的应用从 Google 移动广告 SDK 接收这些等效事件。以下示例展示了如何使用回调:

到这里,我们已经实现针对原生广告的自定义事件。GitHub 上提供了完整的示例。