在 Android 应用中添加原生广告

原生广告 API 可帮助您为应用中展示的广告打造定制体验。使用原生广告 API 时,您将收到一组广告属性(例如标题、图片、行动号召),而不是将要展示的广告,您必须使用这些广告属性来构建展示广告的自定义视图。

请确保先阅读 Audience Network 入门指南Android 入门指南,然后再继续操作。

在本指南中,我们将实现以下原生广告版位。您将创建包含下列组件的原生广告:

图 1:广告图标

图 2:广告标题

图 3:赞助内容标签

图 4:AdOptionsView

图 5:MediaView

图 6:社交内容

图 7:广告正文

图 8:行动号召按钮




原生广告创建步骤

第 1 步:请求原生广告

第 2 步:创建原生广告布局

第 3 步:使用广告的元数据填充布局

第 4 步:使用 MediaView

第 5 步:在不自动缓存的情况下加载广告

对 Audience Network SDK 进行初始化

Android Audience Network SDK 5.1 版中已添加此方法。

5.3.0 及以上版本的 Audience Network SDK 需要进行显式初始化。有关如何初始化 Audience Network Android SDK 的信息,请参阅此文档

在创建广告对象和加载广告前,您应初始化 Audience Network SDK。建议您在启动应用时执行这项操作。

public class YourApplication extends Application {
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        // Initialize the Audience Network SDK
        AudienceNetworkAds.initialize(this);       
    }
    ...
}

第 1 步:请求原生广告

在 Activity 的顶部添加以下代码,以导入 Facebook 广告 SDK:

import com.facebook.ads.*;

然后实例化 NativeAd 对象,创建 NativeAdListener,并使用广告侦听器调用 loadAd()

private final String TAG = "NativeAdActivity".getClass().getSimpleName();
private NativeAd nativeAd;

private void loadNativeAd() {
    // Instantiate a NativeAd object.
    // NOTE: the placement ID will eventually identify this as your App, you can ignore it for
    // now, while you are testing and replace it later when you have signed up.
    // While you are using this temporary code you will only get test ads and if you release
    // your code like this to the Google Play your users will not receive ads (you will get a no fill error).
    nativeAd = new NativeAd(this, "YOUR_PLACEMENT_ID");

    NativeAdListener nativeAdListener = new NativeAdListener() {
        @Override
        public void onMediaDownloaded(Ad ad) {
            // Native ad finished downloading all assets
            Log.e(TAG, "Native ad finished downloading all assets.");
        }

        @Override
        public void onError(Ad ad, AdError adError) {
        // Native ad failed to load
            Log.e(TAG, "Native ad failed to load: " + adError.getErrorMessage());
        }

        @Override
        public void onAdLoaded(Ad ad) {
            // Native ad is loaded and ready to be displayed
            Log.d(TAG, "Native ad is loaded and ready to be displayed!");
        }

        @Override
        public void onAdClicked(Ad ad) {
            // Native ad clicked
            Log.d(TAG, "Native ad clicked!");
        }

        @Override
        public void onLoggingImpression(Ad ad) {
            // Native ad impression
            Log.d(TAG, "Native ad impression logged!");
        }
    };

    // Request an ad
    nativeAd.loadAd(
            nativeAd.buildLoadAdConfig()
                    .withAdListener(nativeAdListener)
                    .build());
}

我们稍后再在 onAdLoaded() 方法中添加代码。

第 2 步:创建原生广告布局

接下来,提取广告元数据,并使用其属性构建自定义原生用户界面。您可以在布局 .xml 中创建自定义视图,也可以在代码中添加元素。

设计应用中的原生广告时,请参阅原生广告准则

在 Activity 的布局 activity_main.xml 中,添加 Native Ad 的容器。此容器应为 com.facebook.ads.NativeAdLayout,即 FrameLayout 顶部的封装程序,其附加功能可帮助我们在广告上方呈现原生广告报告流程。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:paddingTop="50dp">
    ...
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="50dp">

        <com.facebook.ads.NativeAdLayout
             android:id="@+id/native_ad_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical" />
     </ScrollView>
    ...
</RelativeLayout>

为原生广告创建自定义布局 native_ad_layout.xml



原生广告的自定义布局示例如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ad_unit"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:orientation="vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingBottom="10dp"
        android:paddingTop="10dp">

        <com.facebook.ads.MediaView
            android:id="@+id/native_ad_icon"
            android:layout_width="35dp"
            android:layout_height="35dp" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingLeft="5dp"
            android:paddingRight="5dp">

        <TextView
            android:id="@+id/native_ad_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:lines="1"
            android:textColor="@android:color/black"
            android:textSize="15sp" />

        <TextView
            android:id="@+id/native_ad_sponsored_label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:lines="1"
            android:textColor="@android:color/darker_gray"
            android:textSize="12sp" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/ad_choices_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="end"
        android:orientation="horizontal" />

    </LinearLayout>

    <com.facebook.ads.MediaView
        android:id="@+id/native_ad_media"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="5dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:orientation="vertical">

            <TextView
                android:id="@+id/native_ad_social_context"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:textColor="@android:color/darker_gray"
                android:textSize="12sp" />

            <TextView
                android:id="@+id/native_ad_body"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:gravity="center_vertical"
                android:lines="2"
                android:textColor="@android:color/black"
                android:textSize="12sp" />

        </LinearLayout>

        <Button
            android:id="@+id/native_ad_call_to_action"
            android:layout_width="100dp"
            android:layout_height="30dp"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:background="#4286F4"
            android:paddingLeft="3dp"
            android:paddingRight="3dp"
            android:textColor="@android:color/white"
            android:textSize="12sp"
            android:visibility="gone" />

    </LinearLayout>

</LinearLayout>

第 3 步:使用广告的元数据填充布局

情景 1:在广告成功加载后立即显示。修改上述 onAdLoaded() 方法,以检索 Native Ad's 属性,并以下列方式显示广告:

private NativeAdLayout nativeAdLayout;
private LinearLayout adView;
private NativeAd nativeAd;

private void loadNativeAd() {
    // Instantiate a NativeAd object. 
    // NOTE: the placement ID will eventually identify this as your App, you can ignore it for
    // now, while you are testing and replace it later when you have signed up.
    // While you are using this temporary code you will only get test ads and if you release
    // your code like this to the Google Play your users will not receive ads (you will get a no fill error).
    nativeAd = new NativeAd(this, "YOUR_PLACEMENT_ID");

    NativeAdListener nativeAdListener = new NativeAdListener() {
        ...
        @Override
        public void onAdLoaded(Ad ad) {
            // Race condition, load() called again before last ad was displayed
            if (nativeAd == null || nativeAd != ad) {
                return;
            }
            // Inflate Native Ad into Container
            inflateAd(nativeAd);
        }
        ...
    };

    // Request an ad
    nativeAd.loadAd(
            nativeAd.buildLoadAdConfig()
                    .withAdListener(nativeAdListener)
                    .build());
}

private void inflateAd(NativeAd nativeAd) {

    nativeAd.unregisterView();

    // Add the Ad view into the ad container.
    nativeAdLayout = findViewById(R.id.native_ad_container);
    LayoutInflater inflater = LayoutInflater.from(NativeAdActivity.this);
    // Inflate the Ad view.  The layout referenced should be the one you created in the last step.
    adView = (LinearLayout) inflater.inflate(R.layout.native_ad_layout_1, nativeAdLayout, false);
    nativeAdLayout.addView(adView);

    // Add the AdOptionsView
    LinearLayout adChoicesContainer = findViewById(R.id.ad_choices_container);
    AdOptionsView adOptionsView = new AdOptionsView(NativeAdActivity.this, nativeAd, nativeAdLayout);
    adChoicesContainer.removeAllViews();
    adChoicesContainer.addView(adOptionsView, 0);

    // Create native UI using the ad metadata.
    MediaView nativeAdIcon = adView.findViewById(R.id.native_ad_icon);
    TextView nativeAdTitle = adView.findViewById(R.id.native_ad_title);
    MediaView nativeAdMedia = adView.findViewById(R.id.native_ad_media);
    TextView nativeAdSocialContext = adView.findViewById(R.id.native_ad_social_context);
    TextView nativeAdBody = adView.findViewById(R.id.native_ad_body);
    TextView sponsoredLabel = adView.findViewById(R.id.native_ad_sponsored_label);
    Button nativeAdCallToAction = adView.findViewById(R.id.native_ad_call_to_action);

    // Set the Text.
    nativeAdTitle.setText(nativeAd.getAdvertiserName());
    nativeAdBody.setText(nativeAd.getAdBodyText());
    nativeAdSocialContext.setText(nativeAd.getAdSocialContext());
    nativeAdCallToAction.setVisibility(nativeAd.hasCallToAction() ? View.VISIBLE : View.INVISIBLE);
    nativeAdCallToAction.setText(nativeAd.getAdCallToAction());
    sponsoredLabel.setText(nativeAd.getSponsoredTranslation());

    // Create a list of clickable views
    List<View> clickableViews = new ArrayList<>();
    clickableViews.add(nativeAdTitle);
    clickableViews.add(nativeAdCallToAction);

    // Register the Title and CTA button to listen for clicks.
    nativeAd.registerViewForInteraction(
            adView, nativeAdMedia, nativeAdIcon, clickableViews);
}

SDK 将自动记录广告展示情况并处理点击操作。请注意,必须利用 NativeAd 实例注册广告视图才能启用此功能。若要让视图的所有广告元素均可点击,请使用下列代码注册此视图:

registerViewForInteraction(View view, MediaView adMediaView, MediaView adIconView)

对 NativeAd 使用 registerViewForInteraction 时,SDK 会检查系统是否在主线程运行此调用,以避免争用条件。我们使用 Preconditions.checkIsOnMainThread() 执行检查。请确保您的实现方式符合此标准,如果您尝试在后台线程中调用 registerViewForInteraction,您的应用会崩溃。

情景 2:在广告成功加载几秒或几分钟后显示。显示广告前,您应检查广告是否已经失效。

如果广告在加载后没有立即显示,开发者需负责检查广告是否已经无效。成功加载后,广告的有效期为 60 分钟。如果您展示无效广告,则将无法收款。您应调用 isAdInvalidated() 来验证广告。

您应该遵循下列思路,但请不要把代码复制到自己的项目中,因为这只是一个示例:

private NativeAd nativeAd;

private void loadNativeAd() {
    // Instantiate a NativeAd object. 
    // NOTE: the placement ID will eventually identify this as your App, you can ignore it for
    // now, while you are testing and replace it later when you have signed up.
    // While you are using this temporary code you will only get test ads and if you release
    // your code like this to the Google Play your users will not receive ads (you will get a no fill error).
    nativeAd = new NativeAd(this, "YOUR_PLACEMENT_ID");

    NativeAdListener nativeAdListener = new NativeAdListener() {
        ...
    };

    // Request an ad
    nativeAd.loadAd(
            nativeAd.buildLoadAdConfig()
                    .withAdListener(nativeAdListener)
                    .build());

    // Here is just an example for displaying the ad with delay
    // Please call this method at appropriate timing in your project
    showNativeAdWithDelay();
}

private void showNativeAdWithDelay() {
    /**
     * Here is an example for displaying the ad with delay;
     * Please do not copy the Handler into your project
     */
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        public void run() {
            // Check if nativeAd has been loaded successfully
            if(nativeAd == null || !nativeAd.isAdLoaded()) {
                return;
            }
            // Check if ad is already expired or invalidated, and do not show ad if that is the case. You will not get paid to show an invalidated ad.
            if(nativeAd.isAdInvalidated()) {
                return;
            }
            inflateAd(nativeAd); // Inflate NativeAd into a container, same as in previous code examples
        }
    }, 1000 * 60 * 15); // Show the ad after 15 minutes
}

控制可点击的区域

为打造更优质的用户体验,并获得更理想的成效,建议您始终考虑控制可点击的广告区域,避免无意点击。请参阅 Audience Network SDK 政策页面,详细了解有关空白区域不可点击的规定。

为了更好地控制可点击的内容,您可以使用 registerViewForInteraction(View view, MediaView adMediaView, MediaView adIconView, List<View> clickableViews) 注册可点击视图的列表。例如,如果只希望前面示例中的广告标题和行动号召按钮可以点击,则可编写如下代码:

@Override
public void onAdLoaded(Ad ad) {
    ...
    // Create a list of clickable views
    List<View> clickableViews = new ArrayList<>();
    clickableViews.add(nativeAdTitle);
    clickableViews.add(nativeAdCallToAction);

    // Register the Title and CTA button to listen for clicks.
    nativeAd.registerViewForInteraction(
            adView, nativeAdMedia, nativeAdIcon, clickableViews);
    ...
}

如果需要在一定时间以后重复使用相同的视图来显示不同广告,请确保在使用不同的 NativeAd 实例注册相同视图之前调用 unregisterView()

运行代码,即可显示原生广告:

第 4 步:使用 MediaView

若要显示原生广告封面图片,必须使用可显示图片和视频素材的 Meta Audience Network MediaView。如需了解原生视频广告单元设计准则,请点击此处

默认情况下,在加载原生广告时,系统都会预缓存图片和视频素材,因此 MediaView 可在 nativeAd 完成加载后立即播放视频。

private void loadNativeAd() {
    ...
    nativeAd.loadAd();
}

此外,在加载原生广告时,还可明确指定 NativeAd.MediaCacheFlag.ALL

private void loadNativeAd() {
    ...
    nativeAd.loadAd(
            nativeAd.buildLoadAdConfig()
                    .withMediaCacheFlag(NativeAdBase.MediaCacheFlag.ALL)
                    .build());
}

对于原生广告,Audience Network 支持两个缓存选项,具体介绍如 NativeAd.MediaCacheFlag 枚举的定义所述:

缓存常量 描述

ALL

全部预缓存(图标、图片和视频),默认值

NONE

不预缓存

加载广告后,下列属性将包含一些值:titleiconcoverImagecallToAction。其他属性则可能为 null 值或空值。请确保您的代码足够强大,能够处理这些情况。

无广告展示时,系统将调用 onError,其中会设置 error.code。如果您使用自己的自定义报告或中介层,则可能需要检查代码值并检测这一情况。在这种情况下,您可回退至另一个广告网络,但不能立即重新请求广告。

您可以缓存接收的广告元数据,并可重复使用长达 1 小时。如果您计划在此时段后使用此元数据,请通过调用加载新广告。

第 5 步:在不自动缓存的情况下加载广告

  • 强烈建议在所有情况下均默认开启素材缓存功能。但您可以使用 loadAd 方法中的 MediaCacheFlag.NONE 替换默认值。如果决定替换默认的素材缓存设置,请谨慎处理。
private final String TAG = NativeAdActivity.class.getSimpleName();
private NativeAd nativeAd;

private void loadNativeAd() {
    // Instantiate a NativeAd object. 
    // NOTE: the placement ID will eventually identify this as your App, you can ignore it for
    // now, while you are testing and replace it later when you have signed up.
    // While you are using this temporary code you will only get test ads and if you release
    // your code like this to the Google Play your users will not receive ads (you will get a no fill error).
    nativeAd = new NativeAd(this, "YOUR_PLACEMENT_ID");
    NativeAdListener nativeAdListener = new NativeAdListener() {
        ...
    };

    // Request an ad without auto cache
    nativeAd.loadAd(
            nativeAd.buildLoadAdConfig()
                    .withAdListener(nativeAdListener)
                    .withMediaCacheFlag(NativeAdBase.MediaCacheFlag.NONE)
                    .build());
}
  • 对广告成功调用 onAdLoaded 后,可在适当时手动调用 downloadMedia 方法,以开始下载原生广告的所有素材。
@Override
public void onAdLoaded(Ad ad) {
    if (nativeAd == null || nativeAd != ad) {
        return;
    }

    nativeAd.downloadMedia();
}
  • 最后,通过 onMediaDownloaded 回调加载完素材后,可调用 registerViewForInteraction 方法并展示广告。
@Override
public void onMediaDownloaded(Ad ad) {
    if (nativeAd == null || nativeAd != ad) {
        return;
    }

    inflateAd(nativeAd); // Inflate NativeAd into a container, same as in previous code examples
}

如果在不自动缓存的情况下加载广告,并且不手动调用 downloadMedia 开始下载素材,则系统仅会在您调用 registerViewForInteraction 时才开始下载素材。所有素材均需加载并展示才能算作一次符合条件的展示。

视频广告硬件加速

Audience Network 视频广告要求启用硬件加速渲染,否则您可能会在观看视频时遇到黑屏。此要求适用于

  • 原生广告中的视频广告创意
  • 插屏广告中的视频广告创意
  • 视频插播广告
  • 奖励式视频广告

如果您使用的 API 的级别大于等于 14(Ice Cream Sandwich,Android 4.0.1),系统将默认启用硬件加速,但您也可以在应用层级或活动层级显式启用这一功能。

应用层级

在 Android 清单文件中,向 <application> 标签添加下列属性,为整个应用程序启用硬件加速功能:

<application android:hardwareAccelerated="true" ...>

活动层级

如果您只想为应用程序中的特定活动启用此功能,您可以向 Android 清单文件中的 <activity> 标签添加下列功能。以下示例将为用于显示插屏广告和奖励式视频广告的 AudienceNetworkActivity 启用硬件加速功能:

<activity android:name="com.facebook.ads.AudienceNetworkActivity" android:hardwareAccelerated="true" .../>

后续步骤

  • 查看原生广告模板指南,以在您的应用中添加原生广告。

  • 查看代码示例,了解如何使用原生广告。NativeAdSample 现已成为 SDK 的组成部分,可在 AudienceNetwork/samples 文件夹中找到。将项目导入您的 IDE,然后在设备或模拟器上运行。

更多资源

入门指南

Audience Network 入门技术指南

代码示例

Audience Network 广告集成示例

常见问题

Audience Network 常见问题

原生广告模板

更简便的原生广告集成方法