原生广告
大约 8 分钟
原生广告
概述
原生广告是一种与应用内容和设计自然融合的广告形式,开发者只需按照标准集成步骤即可在应用中无缝展示广告内容。原生广告能够与应用的界面和用户体验协调一致,不会干扰用户的正常使用。用户可以在浏览应用内容时自然地接触到这些广告,从而提高广告的接受度和互动性。开发者可以根据应用的风格和用户的需求定制广告的展示方式,以实现最佳的广告效果和用户体验。
模版渲染广告和开发者自渲染广告,统一使用 SAFeedAdManager 加载
SAFeedAdManager 方法介绍
| 方法 | 说明 | 
|---|---|
| 构造方法,传入广告位ID。 | |
| 设置原生广告中的视频是否静音 | |
| 设置所在的activity | |
| 设置期望的宽高,单位dp,当高度传0时,原生模版渲染广告会根据平台模版和宽度自适应调整高度 | |
| 设置加载结果回调监听,在 之前设置  | |
| 加载广告 | |
| 获取本次瀑布流加载过程中失败的代码位信息 | 
SAFeedAdManager.LoadListener 方法介绍
| 方法 | 说明 | 
|---|---|
| 加载成功 | |
| 加载失败 | 
SAFeedAd 方法介绍
| 属性 | 说明 | 
|---|---|
| 是否是原生模版渲染广告 | |
| 获取模版渲染广告对象 | |
| 获取开发者自渲染广告数据对象 | 
模版渲染
由广告网络提供多种的广告模板样式,上下图文、左右图文、三小图+文等
场景常见于应用的内容信息流中与应用内容穿插展示,如资讯列表页每隔几条资讯穿插一条广告。
SANativeExpressAd 方法介绍
| 方法 | 说明 | 
|---|---|
| 设置模版渲染广告回调事件监听,在调用render前设置 | |
| 渲染广告视图,在收到方法回调后,可以获取广告视图 | |
| 获取广告视图,在收到方法回调后调用 | |
| 销毁广告 | |
| 对于已经加载成功,但未及时展示的广告,在展示前用此方法判断广告是否还可用 | |
| 获取价格,单位为 分 | |
| 获取本次广告填充的代码位信息 | 
SAExpressFeedAd.InteractionListener 方法说明
| 方法 | 说明 | 
|---|---|
| void sa_feedAdRenderSuccess() | 广告渲染成功 | 
| void sa_feedAdRenderFailure(AdError error) | 广告渲染失败 | 
| void sa_feedAdShowFail(AdError error) | 广告展示失败 | 
| void sa_feedAdDidShow() | 广告已经展示 | 
| void sa_feedAdDidClick() | 广告点击 | 
| void sa_feedAdDidExposure() | 广告曝光 | 
| void sa_feedAdDidClose() | 广告点击关闭 | 
开发者自渲染
当广告网络提供的原生模版渲染广告的样式和展示场景无法满足需求时,可以采用原生开发者自渲染广告
使用自渲染的API,可以为应用打造自定义的样式和展示场景。
SANativeAdData 方法说明
| 方法 | 说明 | 
|---|---|
| String getTitle() | 广告标题 | 
| String getDescription() | 广告文字内容 | 
| Bitmap getAdLogo() | 获取广告平台logo bitmap | 
| String getAdLogoUrl() | 获取广告平台logo url | 
| String getIconUrl() | 获取icon url | 
List<String>getImageList() | 获取图片url列表 | 
| SANativeADMediaMode getFeedAdMode() | 获取广告媒体素材类型,一图、多图、视频等形式 | 
| String getEcpm() | 获取价格,单位为分 | 
| boolean isDownAPPAd() | 是否是下载类广告 | 
| SADownAppInfo getDownAppInfo() | 下载应用的信息六要素 | 
| SAAdSourceInfo getFillAdSourceInfo() | 获取填充广告源信息 | 
SADownAppInfo 下载类六要素方法说明
| 方法 | 说明 | 
|---|---|
| String getAppName() | 应用名称 | 
| String getAuthorName() | 开发者名称 | 
| String getPrivacyUrl() | 隐私协议链接 | 
| String getAppVersionName() | 应用版本 | 
| String getAppPermissionUrl() | 权限说明链接 | 
| String getAppFunctionUrl() | 功能介绍链接 | 
SANativeADMediaMode 媒体素材类型枚举说明
| 枚举 | 说明 | 
|---|---|
| OneImage | 单张图片 | 
| GroupImage | 多张图片 | 
| Video | 视频类型 | 
| OnlyIcon | 只有一个appIcon类型 | 
展示广告
要展示开发者原生自渲染广告,开发者需要自定义广告布局,从SANativeAdData中获取广告内容填充的布局中,并将布局元素传入到bindAdToView(ViewGroup adContainer, ImageView icon, ImageView adLogo, List<View> clickViews, List<View> ctaViews, View disLike, ViewGroup mediaContainer, List<ImageView> imgViews, SAUnifiedFeedAdInteractionListener listener)中:
| 参数 | 说明 | 是否必选 | 
|---|---|---|
| ViewGroup adContainer | 广告容器 | 必填,如果传入无效值,广告将不能正常曝光,开发者不会获得收益 | 
| ImageView icon | app icon的imageView | 可选 | 
| ImageView adLogo | 广告网络logo视图 | 可选 | 
List<View> clickViews | 可响应点击的视图数组 | 必填 | 
List<View> ctaViews | cta视图数组 | 可选 | 
| View disLike | 关闭事件视图 | 可选 | 
| ViewGroup mediaContainer | 用于展示视频的容器 | 当广告素材类型为视频时,即getFeedAdMode()为SANativeADMediaMode.Video,必填 | 
List<ImageView> imgViews | 用于展示广告图片素材的视图数组 | 可选 | 
| SAUnifiedFeedAdInteractionListener listener | 广告生命周期事件回调 | 可选 | 
请求模版渲染广告示例
private void loadAd() {
    feedAdManager = new SAFeedAdManager("posid");
    feedAdManager.setActivity(this);
    feedAdManager.setMuted(true);
    feedAdManager.setAdSize(new SAAdSize(width, Height));
    feedAdManager.setLoadListener(new SAFeedAdManager.LoadListener() {
        @Override
        public void onFeedAdLoad(List<SAFeedAd> ads) {
            if (list != null && list.size() > 0) {
                SAFeedAd feedAd = list.get(0);
                if (feedAd.isExpress()) {
                        //模板渲染
                        SAExpressFeedAd expressFeedAd = feedAd.getExpressFeedAd();
                        expressFeedAd.setInteractionListener(new SAExpressFeedAd.InteractionListener() {
                            @Override
                            public void sa_feedAdRenderSuccess() {
                                View adView = expressFeedAd.getAdView();
                                adContainer.removeAllViews();
                            	adContainer.addView(view);
                            }
    
                            @Override
                            public void sa_feedAdRenderFailure(AdError adError) {}
    
                            @Override
                            public void sa_feedAdShowFail(AdError adError) {}
    
                            @Override
                            public void sa_feedAdDidShow() {}
    
                            @Override
                            public void sa_feedAdDidClick() {}
    
                            @Override
                            public void sa_feedAdDidExposure() {}
    
                            @Override
                            public void sa_feedAdDidClose() {
                                // 关闭事件,需要移除广告视图
        						adContainer.removeAllViews();
                            }
                        });
                        feedAd.getExpressFeedAd().isReady();
                        feedAd.getExpressFeedAd().render();
                    }else {
                    //自渲染参考下面示例代码
                }
        	}
        }
        @Override
        public void onError(AdError error) {
            
        }
    });
    feedAdManager.loadFeedAd();
}
请求开发者自渲染广告示例
public class MixFeedActivity extends Activity {
    private SAFeedAdManager feedAdManager;
    private List<SAFeedAd> feedAds = new ArrayList<>();
    private LoadMoreListView listView;
    private FeedListAdapter mFeedListAdapter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mix_feed);
        listView = findViewById(R.id.feed_list);
        listView.setLoadMoreListener(() -> {
            loadAd();
        });
        mFeedListAdapter = new FeedListAdapter(this, feedAds);
        listView.setAdapter(mFeedListAdapter);
        loadAd();
    }
    private void loadAd() {
        feedAdManager = new SAFeedAdManager("id");
        feedAdManager.setActivity(this);
        feedAdManager.setMuted(true);
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        float scale =  metrics.density;
        int width = (int) (metrics.widthPixels / (scale <= 0 ? 1 : scale) + 0.5f);
        feedAdManager.setAdSize(new SAAdSize(width, 0));
        feedAdManager.setLoadListener(new SAFeedAdManager.LoadListener() {
            @Override
            public void onFeedAdLoad(List<SAFeedAd> ads) {
                // 广告请求成功
                if (listView != null) {
                    listView.setLoadingFinish();
                }
                if (ads == null || ads.isEmpty()) {
                    // 广告数据为空
                    return;
                }
                int loadCount = 15;// 模拟每次展示的Item刷新个数
                for (int i = 0; i < loadCount; i++) {
                    feedAds.add(null);
                }
                int totalCount = feedAds.size();
                Set<Integer> set = new HashSet<>();
                for (SAFeedAd feedAd : ads) {
                    if (feedAd == null) {
                        continue;
                    }
                    int random = (int) (Math.random() * loadCount) + totalCount - loadCount;
                    while (set.contains(random)) {
                        random = (int) (Math.random() * loadCount) + totalCount - loadCount;
                    }
                    set.add(random);
                    feedAds.set(random, feedAd);
                    setFeedAdInteractionListener(feedAd);
                }
                mFeedListAdapter.notifyDataSetChanged();
            }
            @Override
            public void onError(AdError error) {
                if (listView != null) {
                    listView.setLoadingError();
                }
                // 广告请求失败
            }
        });
        feedAdManager.loadFeedAd();
    }
    private void setFeedAdInteractionListener(SAFeedAd mixFeedAd) {
        //模板渲染
        if (mixFeedAd.isExpress()) {
            SAExpressFeedAd feedAd = mixFeedAd.getExpressFeedAd();
            feedAd.setInteractionListener(new SAExpressFeedAd.InteractionListener() {
                @Override
                public void sa_feedAdRenderSuccess() {
                // 渲染成功
                }
                @Override
                public void sa_feedAdRenderFailure(AdError error) {
                // 渲染失败
                }
                @Override
                public void sa_feedAdShowFail(AdError error) {
                // 展示失败
                }
                @Override
                public void sa_feedAdDidShow() {
                // 展示
                }
                @Override
                public void sa_feedAdDidClick() {
                // 点击
                }
                @Override
                public void sa_feedAdDidExposure() {
                // 曝光
                }
                @Override
                public void sa_feedAdDidClose() {
                // 点击关闭
                }
            });
            feedAd.render();
        } else {
            //自渲染
        }
    }
    private static class FeedListAdapter extends BaseAdapter {
        private Context mContext;
        private List<SAFeedAd> mFeedList;
        FeedListAdapter(Context context, List<SAFeedAd> feedList) {
            this.mContext = context;
            this.mFeedList = feedList;
        }
        @Override
        public int getCount() {
            return mFeedList.size();
        }
        @Override
        public SAFeedAd getItem(int position) {
            return mFeedList.get(position);
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public int getItemViewType(int position) {
            SAFeedAd feedAd = getItem(position);
            if (feedAd != null) {
                if (feedAd.isExpress()) {
                    return ItemViewType.ITEM_VIEW_TYPE_AD;
                } else {
                    switch (feedAd.getNativeAdData().getFeedAdMode()) {
                        case GroupImage:
                            return ItemViewType.ITEM_VIEW_TYPE_AD_NATIVE_GROUP;
                        case Video:
                            return ItemViewType.ITEM_VIEW_TYPE_AD_NATIVE_VIDEO;
                        default:
                            return ItemViewType.ITEM_VIEW_TYPE_AD_NATIVE_IMAGE;
                    }
                }
            } else {
                return ItemViewType.ITEM_VIEW_TYPE_NORMAL;
            }
        }
        @Override
        public int getViewTypeCount() {
            return 5;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            SAFeedAd feedAd = getItem(position);
            switch (getItemViewType(position)) {
                case ItemViewType.ITEM_VIEW_TYPE_AD:
                    return getExpressAdItemView(convertView, parent, feedAd.getExpressFeedAd());
                case ItemViewType.ITEM_VIEW_TYPE_AD_NATIVE_IMAGE:
                    return getImageItemView(convertView, parent, feedAd.getNativeAdData());
                case ItemViewType.ITEM_VIEW_TYPE_AD_NATIVE_VIDEO:
                    return getVideoItemView(convertView, parent, feedAd.getNativeAdData());
                case ItemViewType.ITEM_VIEW_TYPE_AD_NATIVE_GROUP:
                    return getGroupItemView(convertView, parent, feedAd.getNativeAdData());
                default:
                    return getNormalItemView(convertView, parent, position);
            }
        }
        private View getExpressAdItemView(View convertView, ViewGroup parent, final SAExpressFeedAd feedAd) {
            ExpressFeedAdViewHolder adViewHolder;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.feed_list_item_ad_container,
                        parent, false);
                adViewHolder = new ExpressFeedAdViewHolder(convertView);
                convertView.setTag(adViewHolder);
            } else {
                adViewHolder = (ExpressFeedAdViewHolder) convertView.getTag();
            }
            View adView = feedAd.getAdView();
            if (adView != null && adView.getParent() == null) {
                adViewHolder.mAdContainer.removeAllViews();
                adViewHolder.mAdContainer.addView(adView);
            }
            return convertView;
        }
        private View getImageItemView(View convertView, ViewGroup parent, final SANativeAdData feedAd) {
            NativeFeedAdImageViewHolder adViewHolder;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.self_feed_list_item_ad_container_01, parent, false);
                adViewHolder = new NativeFeedAdImageViewHolder(convertView);
                convertView.setTag(adViewHolder);
            } else {
                adViewHolder = (NativeFeedAdImageViewHolder) convertView.getTag();
            }
            List<View> clicks = new ArrayList<>();
            List<ImageView> imageViews=new ArrayList<>();
            FrameLayout container = adViewHolder.mAdContainer;
            ImageView logo = adViewHolder.iv_logo;
            ImageView bigImage = adViewHolder.imageView;
            clicks.add(bigImage);
            clicks.add(adViewHolder.tvTitle);
            clicks.add(adViewHolder.tvDes);
            clicks.add(logo);
            imageViews.add(bigImage);
            feedAd.bindAdToView(container, null, logo, clicks, null, null, null, imageViews, new SAUnifiedFeedAdInteractionListener() {
                @Override
                public void onAdShow() {
                    // 展示
                }
                @Override
                public void onAdClick() {
                    // 点击
                }
                @Override
                public void onAdError(int errorCode, String msg) {
                    // 展示失败
                }
            });
            if ("".equals(feedAd.getDescription())) adViewHolder.tvDes.setVisibility(View.GONE);
            else adViewHolder.tvDes.setVisibility(View.VISIBLE);
            adViewHolder.tvTitle.setText(feedAd.getTitle());
            adViewHolder.tvDes.setText(feedAd.getDescription());
            return convertView;
        }
        private View getGroupItemView(View convertView, ViewGroup parent, final SANativeAdData feedAd) {
            NativeFeedAdGroupImageViewHolder adViewHolder;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.self_feed_list_item_ad_container_03, parent, false);
                adViewHolder = new NativeFeedAdGroupImageViewHolder(convertView);
                convertView.setTag(adViewHolder);
            } else {
                adViewHolder = (NativeFeedAdGroupImageViewHolder) convertView.getTag();
            }
            adViewHolder.tvTitle.setText(feedAd.getTitle());
            adViewHolder.tvDes.setText(feedAd.getDescription());
            List<View> clicks = new ArrayList<>();
            List<ImageView> imageViews=new ArrayList<>();
            clicks.add(convertView);
            imageViews.add(adViewHolder.iv_1);
            imageViews.add(adViewHolder.iv_2);
            imageViews.add(adViewHolder.iv_3);
            clicks.addAll(imageViews);
            clicks.add(adViewHolder.iv_logo);
            clicks.add(adViewHolder.tvTitle);
            clicks.add(adViewHolder.tvDes);
            feedAd.bindAdToView(adViewHolder.mAdContainer, null, adViewHolder.iv_logo, clicks, null, null, null, imageViews, new SAUnifiedFeedAdInteractionListener() {
                @Override
                public void onAdShow() {
                    // 展示
                }
                @Override
                public void onAdClick() {
                    // 点击
                }
                @Override
                public void onAdError(int errorCode, String msg) {
                    // 展示失败
                }
            });
            return convertView;
        }
        private View getVideoItemView(View convertView, ViewGroup parent, final SANativeAdData feedAd) {
            NativeFeedAdVideoViewHolder adViewHolder;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.self_feed_list_item_ad_container_02, parent, false);
                adViewHolder = new NativeFeedAdVideoViewHolder(convertView);
                convertView.setTag(adViewHolder);
            } else {
                adViewHolder = (NativeFeedAdVideoViewHolder) convertView.getTag();
            }
            List<View> clicks = new ArrayList<>();
            List<ImageView> imageViews=new ArrayList<>();
            FrameLayout container = adViewHolder.mAdContainer;
            ImageView logo = adViewHolder.iv_logo;
            FrameLayout mediaContainer = adViewHolder.mediaContainer;
            clicks.add(container);
            clicks.add(mediaContainer);
            clicks.add(logo);
            feedAd.bindAdToView(container, null, logo, clicks, null, null, mediaContainer, imageViews, new SAUnifiedFeedAdInteractionListener() {
                @Override
                public void onAdShow() {
                    // 展示
                }
                @Override
                public void onAdClick() {
                    // 点击/
                }
                @Override
                public void onAdError(int errorCode, String msg) {
                    // 展示失败
                }
            });
            adViewHolder.tvTitle.setText(feedAd.getTitle());
            adViewHolder.tvDes.setText(feedAd.getDescription());
            return convertView;
        }
        @SuppressLint("DefaultLocale")
        private View getNormalItemView(View convertView, ViewGroup parent, int position) {
            FeedListAdapter.NormalViewHolder normalViewHolder;
            if (convertView == null) {
                normalViewHolder = new FeedListAdapter.NormalViewHolder();
                convertView =
                        LayoutInflater.from(mContext).inflate(R.layout.native_item_normal, parent, false);
                normalViewHolder.textView = convertView.findViewById(R.id.tv);
                convertView.setTag(normalViewHolder);
            } else {
                normalViewHolder = (FeedListAdapter.NormalViewHolder) convertView.getTag();
            }
            normalViewHolder.textView.setText(String.format("ListView item %d", position));
            return convertView;
        }
        @interface ItemViewType {
            int ITEM_VIEW_TYPE_NORMAL = 0;
            int ITEM_VIEW_TYPE_AD = 1;
            int ITEM_VIEW_TYPE_AD_NATIVE_IMAGE = 2;
            int ITEM_VIEW_TYPE_AD_NATIVE_VIDEO = 3;
            int ITEM_VIEW_TYPE_AD_NATIVE_GROUP = 4;
        }
        private static class NormalViewHolder {
            TextView textView;
        }
    }
}
