SDK版本说明
- 接入 JAd 所需要的基础库 JCore SDK 最低版本要求为 JCore3.2.0
- 若用户为 JAd 与 JPush 两个极光产品的使用者,在接入 JAd SDK 时,建议将 JPush SDK 同步升级到 JPush4.6.6 及以上版本
SDK初始化配置
说明:
极光联盟SDK需要在主进程中初始化
涉及多进程WebView的使用,用户想要使用自己的数据路径,可以在SDK初始化之前调用WebView.setDataDirectorySuffix()
开发者需要在Application#onCreate()方法中调用初始化接口来初始化极光联盟SDK。SDK是支持多进程,为提升应用稳定性,建议开启SDK多进程。
<!-- AndroidManifest.xml配置SDK支持多进程,可以通过继承UserSerice用户自定义一个Service组件,其中action需要保持不变 -->
<service
android:name="cn.jiguang.ads.app.service.UserService"
android:exported="false"
android:process=":remote">
<intent-filter>
<action android:name="cn.jiguang.user.service.action" />
</intent-filter>
</service>
初始化SDK
public class MainApplication extends Application {
private static final String TAG = "MainApplication";
@Override
public void onCreate() {
super.onCreate();
//配置
JAdApi.config(this, new JAdConfig.Builder()
// 是否开启日志,默认false
.setLoggerEnable(true)
.build());
// 应用内广告初始化
JNativeAdApi.init(this);
// 通知栏广告初始化
JNotifyAdApi.init(this);
}
}
DEV拉取,SDK渲染
//第一步:构建拉取广告所需Slot
JNativeAdSlot nativeAdSlot = new JNativeAdSlot.Builder()
.setLoader(JNativeAdSlot.LOADER_DEV)
.setRender(JNativeAdSlot.RENDER_SDK)
.setAdPosition("广告位")
.setAdStyle(adStyle)//adStyle类型,见类 JAdSlot
.build();
//第二步:拉取
JNativeAdApi.loadNativeAd(context, nativeAdSlot, new OnNativeAdLoadListener() {
@Override
public void onError(JAdError error) {
Toast.makeText(context, "广告拉取失败" + error.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onLoaded(List<JNativeAd> ads) {
Toast.makeText(context, "广告拉取成功", Toast.LENGTH_SHORT).show();
//第三步:加载广告后进行广告有效性校验,提升广告曝光效率
for (JNativeAd nativeAd : ads){
if(!ad.isValid()){
Logger.w(TAG, "sdk渲染广告校验不通过");
continue;
}
// sdk渲染
renderBySdk(nativeAd);
break;
}
}
});
//第四步:SDK渲染广告
private void renderBySdk(JNativeAd nativeAd) {
nativeAd.render();
nativeAd.setOnNativeAdEventListener(new OnNativeAdEventListener() {
@Override
public void onError(JAdError error) {
Toast.makeText(NativeAdActivity.this, "sdk渲染失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onAdExposed(JNativeAd nativeAd1) {
Toast.makeText(NativeAdActivity.this, "sdk渲染成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onAdClicked(View view) {
Toast.makeText(NativeAdActivity.this, "sdk渲染被点击", Toast.LENGTH_SHORT).show();
}
});
}
DEV拉取,DEV渲染
开发者渲染方式,即开发者对拉取到广告素材进行自行渲染展示,自渲染支持广告样式有:横幅广告、插屏广告、侧悬浮广告以及信息流广告。
//第一步:构建拉取广告所需
JNativeAdSlot nativeAdSlot = new JNativeAdSlot.Builder()
.setLoader(JNativeAdSlot.LOADER_DEV)
.setRender(JNativeAdSlot.RENDER_DEV)
.setAdPosition("广告位")
.setAdStyle(adStyle)//adStyle类型,见类 JAdSlot
.setAdCount(1)//目前DEV渲染仅支持一条
.build();
//第二步:拉取
JNativeAdApi.loadNativeAd(context, nativeAdSlot, new OnNativeAdLoadListener() {
@Override
public void onError(JAdError error) {
Toast.makeText(context, "广告拉取失败" + error.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onLoaded(List<JNativeAd> ads) {
Toast.makeText(context, "广告拉取成功", Toast.LENGTH_SHORT).show();
// 取出广告
JNativeAd nativeAd = ads.get(0);
// dev渲染
renderByDev(nativeAd);
}
});
//第三步:dev渲染
private void renderByDev(JNativeAd nativeAd) {
String title = nativeAd.getTitle();
String content = nativeAd.getContent();
String imageUrl = "";
List<JAdImage> imageList = nativeAd.getImageList();
if (imageList != null && !imageList.isEmpty()) {
imageUrl = imageList.get(0).getUrl();
}
String source = nativeAd.getSource();
String interactionText = nativeAd.getButtonText();
View adView = null;
TextView adTitle = null;
TextView adContent = null;
TextView adSource = null;
TextView adInteraction = null;
ImageView adImage = null;
Button adConfirm = null;
int adStyle = getAdStyle();
switch (adStyle) {
case JNativeAdSlot.STYLE_BANNER:
adView = LayoutInflater.from(NativeAdActivity.this).inflate(R.layout.view_banner, adContainer, false);
adTitle = adView.findViewById(R.id.tv_banner_title);
adContent = adView.findViewById(R.id.tv_banner_content);
adImage = adView.findViewById(R.id.iv_banner_image);
adView.findViewById(R.id.iv_banner_cancel).setOnClickListener(this);
break;
case JNativeAdSlot.STYLE_FLOAT:
adView = LayoutInflater.from(NativeAdActivity.this).inflate(R.layout.view_float, adContainer, false);
adImage = adView.findViewById(R.id.iv_float_image);
adView.findViewById(R.id.iv_float_cancel).setOnClickListener(this);
break;
case JNativeAdSlot.STYLE_MODAL:
adView = LayoutInflater.from(NativeAdActivity.this).inflate(R.layout.view_modal, adContainer, false);
adTitle = adView.findViewById(R.id.tv_modal_title);
adContent = adView.findViewById(R.id.tv_modal_content);
adImage = adView.findViewById(R.id.iv_modal_image);
adView.findViewById(R.id.iv_modal_cancel).setOnClickListener(this);
adConfirm = adView.findViewById(R.id.btn_modal);
break;
case JNativeAdSlot.STYLE_FEED:
adView = LayoutInflater.from(NativeAdActivity.this).inflate(R.layout.view_feed, adContainer, false);
adTitle = adView.findViewById(R.id.tv_feed_ad_title);
adContent = adView.findViewById(R.id.tv_feed_ad_content);
adImage = adView.findViewById(R.id.iv_feed_ad_image);
adSource = adView.findViewById(R.id.tv_feed_ad_source);
adInteraction = adView.findViewById(R.id.tv_feed_ad_interaction);
adView.findViewById(R.id.iv_feed_ad_close).setOnClickListener(this);
break;
default:
return;
}
if (adTitle != null) {
adTitle.setText(title);
}
if (adContent != null) {
adContent.setText(content);
}
if (adImage != null) {
Glide.with(NativeAdActivity.this).load(imageUrl).into(adImage);
}
if(adSource != null){
adSource.setText(source);
}
if(adInteraction != null){
adInteraction.setText(interactionText);
}
adContainer.removeAllViews();
adContainer.addView(adView);
LinkedList<View> clickViews = new LinkedList<>();
clickViews.add(adContainer);
clickViews.add(adConfirm);
//第四步:设置回调监听
nativeAd.setOnNativeAdEventListener(adContainer, clickViews, new OnNativeAdEventListener() {
@Override
public void onError(JAdError error) {
Toast.makeText(NativeAdActivity.this, "dev渲染失败" + error.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onAdExposed(JNativeAd nativeAd1) {
Toast.makeText(NativeAdActivity.this, "dev渲染成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onAdClicked(View view) {
Toast.makeText(NativeAdActivity.this, "dev渲染被点击", Toast.LENGTH_SHORT).show();
}
});
}
信息流广告
极光联盟JAd5.0.3版本开始支持信息流广告,信息流广告有两种类型,分别为模板渲染与开发者自渲染,开发者自渲染信息流详见【DEV拉取,DEV渲染】部分,以下为模板渲染信息流接入示例。
//第一步:构建拉取广告所需
JNativeAdSlot adSlot = new JNativeAdSlot.Builder()
.setLoader(JNativeAdSlot.LOADER_DEV)
.setRender(JNativeAdSlot.RENDER_SDK)
.setAdStyle(JNativeAdSlot.STYLE_FEED)
.setAdPosition("Feed广告位")
.setExpressViewAcceptedSize(getAdSize()) //期望模板广告view的size,单位dp
.build();
//第二步:拉取
JNativeAdApi.loadNativeExpressAd(this, adSlot, new OnNativeExpressAdLoadListener() {
@Override
public void onError(JAdError error) {
JToast.showToast(MainApplication.gContext, "信息流广告拉取失败,errCode: " + error.getCode());
}
@Override
public void onLoaded(List<JNativeExpressAdView> ads) {
JToast.showToast(MainApplication.gContext, "信息流广告拉取成功,Size:" + ads.size());
JNativeExpressAdView expressAdView = null;
for (JNativeExpressAdView adView : ads) {
//加载广告后建议进行广告有效性校验,提升广告曝光效率
if (!adView.isValid()) {
Log.w(TAG, "native express ad invalid");
continue;
}
expressAdView = adView;
break;
}
if (expressAdView == null) {
JToast.showToast(MainApplication.gContext, "信息流广告拉取成功,无可用广告");
Log.w(TAG, "native express ad start render failed, because there is no valid ad");
return;
}
// 释放前一个展示的NativeExpressADView的资源
if (nativeExpressAdView != null) {
nativeExpressAdView.destroy();
}
if (containerView.getVisibility() != View.VISIBLE) {
containerView.setVisibility(View.VISIBLE);
}
if (containerView.getChildCount() > 0) {
containerView.removeAllViews();
}
nativeExpressAdView = expressAdView;
Log.i(TAG, "onADLoaded, ad info: " + getAdInfo(nativeExpressAdView));
//绑定广告渲染监听事件
bindNativeExpressListener();
//绑定广告组件状态事件监听
bindViewStatusListener();
// 广告可见才会产生曝光,否则将无法产生收益。注:必须先调用 addView 再 render,否则将可能影响广告曝光效率
containerView.addView(nativeExpressAdView);
nativeExpressAdView.render();
}
});
//绑定渲染回调监听
private void bindNativeExpressListener() {
nativeExpressAdView.setNativeExpressAdListener(new OnNativeExpressAdListener() {
@Override
public void onError(JAdError error, JNativeExpressAdView adView) {
JToast.showToast(MainApplication.gContext, "信息流广告渲染失败 - errCode: " + error.getCode());
}
@Override
public void onAdExposed(JNativeExpressAdView adView, int width, int height) {
//返回view的宽高 单位 dp
JToast.showToast(MainApplication.gContext, "信息流广告曝光成功,尺寸[" + width + "," + height + "]");
}
@Override
public void onAdClicked(JNativeExpressAdView adView) {
JToast.showToast(MainApplication.gContext, "信息流广告点击");
}
@Override
public void onAdClose(JNativeExpressAdView adView) {
JToast.showToast(MainApplication.gContext, "信息流广告关闭");
if (containerView != null && containerView.getChildCount() > 0) {
containerView.removeAllViews();
containerView.setVisibility(View.GONE);
}
}
});
}
//绑定广告视图可见性状态监听回调
private void bindViewStatusListener() {
nativeExpressAdView.setViewBindStatusListener(new JNativeExpressAd.ViewBindStatusListener() {
@Override
public void onAttachedToWindow() {
Log.d(TAG, "onAttachedToWindow");
}
@Override
public void onDetachedFromWindow() {
Log.d(TAG, "onDetachedFromWindow");
}
@Override
public void onStartTemporaryDetach() {
Log.d(TAG, "onStartTemporaryDetach");
}
@Override
public void onFinishTemporaryDetach() {
Log.d(TAG, "onFinishTemporaryDetach");
}
});
}
黑白名单或页面定向配置
对于有需要在指定页面展示应用内广告需求的用户,如果指定页面为Fragment,则需要用户调用接口类 JAdApi 以下两个接口进行动态控制。
/**
* 进入Fragment页面,调用该接口
* @param context ApplicationContext
* @param fragmentName Fragment页面完整类名(this.getClass().getCanonicalName())
*/
public static void onFragmentResumed(Context context, String fragmentName)
/**
* 退出Fragment页面,调用该接口
* @param context
* @param fragmentName
*/
public static void onFragmentPaused(Context context, String fragmentName)
如果仅只需对Activity页面进行动态控制,则不需要开发者调用接口控制,SDK会根据对Activity的生命周期监听,进行动态控制处理。
注:为了保持更好控制效果,以及更好兼容后续的动态配置调整(黑名单替换为白名单, 或白名单换为设置黑名单),建议对每个Fragment页面完成动态配置接口的调用。
因为,Android添加Fragment方式共有四种,而每种不同的添加方式,需要对Fragment的处理方式也有不一样。下面就针对Fragment的四种方式,指定在指定Fragment展示in app控制方式,分别进行讨论。
Fragment使用方式 | 是否可以监听到Fragment生命周期 | 极光动态指定展示页面接口调用方式 |
---|---|---|
方式一:add/show/hide | 无法监听生命周期 | Fragment内的onFragmentResume与onHiddenChaned回调调用 |
方式二:ViewPager | 无法监听生命周期 | Fragment内的setUserVisibleHint回调调用 |
方式三:replace | 可以监听生命周期 | Fragment内的生命周期回调onResume与onPause调用 |
方式四:attach/detach | 可以监听生命周期 | 同上 |
具体控制接口调用方式如下:
1、add/show/hide方式
在需要动态控制的Fragment页面(配置为黑名单页面),
1)在Fragment的onCreateView回调函数中调用动态控制接口,并要求传入参数。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
String classname = this.getClass().getCanonicalName();
JAdApi.onFragmentResumed(MyApplication.gContext, classname);
return inflater.inflate(R.layout.black_fragment, container, false);
}
2)在Fragment的onHiddenChaned回调接口中,调用动态控制接口,并按要求传入参数。
@Override
public void onHiddenChanged(boolean hidden) {
String classname = this.getClass().getCanonicalName();
if(!hidden){//context传application context
JAdApi.onFragmentResumed(MyApplication.gContext, classname);
}else{//hide
JAdApi.onFragmentPaused(MyApplication.gContext, classname);
}
super.onHiddenChanged(hidden);
}
2、ViewPager方式
1)在Fragment的setUserVisibleHint回调接口中,调用动态控制接口,并按要求传入参数。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
String classname = this.getClass().getCanonicalName();
if(isVisibleToUser){
//传application context
JAdApi.onFragmentResumed(MyApplication.gContext, classname);
}else{//hide
JPushInterface.onFragmentPause(MyApplication.gContext, classname);
}
super.setUserVisibleHint(isVisibleToUser);
}
3、replace方式或attach/detach方式
在添加Fragment内的生命周期回调接口中,调用动态页面配置接口。
@Override
public void onResume() {//传application context
String classname = this.getClass().getCanonicalName();
JAdApi.onFragmentResumed(MyApplication.gContext, classname);
super.onResume();
}
@Override
public void onPause() {
String classname = this.getClass().getCanonicalName();
JPushInterface.onFragmentPause(MyApplication.gContext, classname);
super.onPause();
}
DEV拉取,错误码对照表
errorCode | code含义 | 解决方案 |
---|---|---|
0 | 有广告返回 | 正常拉取广告 |
7001 | 未知异常 | 多尝试几次,如若仍出现该错误,请联系技术支持 |
7002 | 程序内部错误 | 多尝试几次,如若仍出现该错误,请联系技术支持 |
7003 | 接口请求超频 | 不要过于频繁请求接口,请稍后再试 |
7004 | 黑名单页面不请求 | 不要在配置为黑名单的页面进行广告请求 |
7005 | SDK TCP建立连接还未完成或出现异常 | 检查当前网络状态或由于网络状态,有可能建连需要一些时间,可多等待一会 |
7006 | 请求超时 | 检查当前网络状态,请多尝试几次 |
7007 | 后台不允许请求 | 应用当前处于后台,不允许进行广告请求 |
7008 | 广告业务关闭 | 该应用当前广告业务关闭,如需开通,请联系商务 |
7009 | 极光广告下发方式关闭 | 该应用极光广告下发方式关闭,如需开通,请联系商务 |
7010 | 透明页面不允许请求 | 应用当前透明页面,不允许进行广告请求 |
7011 | 曝光次数频控 | 广告曝光超过当天最大次数限制,无法返回广告 |
7012 | 曝光间隔频控 | 当前下发广告与上次曝光广告时间小于最小的曝光间隔时间,无法返回广告 |
7013 | 信息流广告设置视图高度小于160dp | 设置信息流广告视图高度大小不小于160dp,最小高度是为保证广告展示效果。 |
8001 | 无in app data | 多拉几次,若依旧没填充,登录联盟后台确认广告位是否设置了过于严格的过滤规则 |
8002 | 后台返回请求超频率 | 不要过于频繁请求接口,请稍后再试 |
8003 | 请求样式不支持 | 广告样式已通过广告位进行控制,请求时无需指定样式 |
8004 | 服务器内部错误 | 基本不会发生,如出现请重试 |
8005 | 请求模式不匹配,未开通拉取权限 | 广告位只支持极光主动下发或者开发者拉取这两种模式二选一,需确认广告下发方式与广告位配置的下发方式是否一致 |
8006 | 业务未开通或者加载运营配置异常 | 请确认appkey和代码位的业务状态为开启 |
8007 | 运营无可用广告位 | 请通过联盟管理后台创建可用广告位 |
8008 | 广告位曝光频控内部错误 | 基本不会发生,如出现请重试 |
8009 | 广告位曝光间隔频控 | 请求过于频繁,请稍后再试 |
8010 | 广告位曝光次数频控 | 该用户已达到当日曝光上限,请更换设备再试 |
8011 | uid下发频控内部错误 | 该用户已达到下发次数上限,请更换设备再试 |
8012 | uid下发间隔频控 | 请求过于频繁,请稍后再试 |
8013 | uid下发次数频控 | 该用户下发次数已达上限,请更换设备再试 |
8014 | 请求超时 | 请检查网络状况 |
8015 | extra请求参数不合法 | 请求参数不合法,请查看集成文档 |
8016 | 样式不匹配 | 广告位只支持极光主动下发或者开发者拉取这两种模式二选一,需确认广告下发方式与广告位配置的下发方式是否一致 |
8017 | 无可用推广目的 | 广告位推广目的配置异常,请更换广告位再试 |
8018 | 测试广告位非测试uid | 请将广告位切换为正式状态 |
8019 | 查询不到Imei信息 | 设备信息获取失败,请稍后再试 |
8020 | 设备信息不完整 | 设备信息获取失败,请稍后再试 |
8021 | 无可用模板 | 广告位推广目的配置异常,请更换广告位再试 |
8022 | 无sequence id | 请求异常,请重试 |
8023 | 非自渲染请求 | 请用自渲染广告位请求自渲染接口 |
8024 | 广告素材不合法 | 请求异常,请重试 |
8025 | adType有指定轻推送或者InApp但与实际场景不符 | 请求异常,请重试 |
8026 | 不属于通道设定的灰度比例 | 该用户不在灰度范围内,暂无法请求广告 |
8027 | 流量定向过滤 | 该用户不在投放范围内,无法请求广告 |
8028 | uid曝光间隔频控filtered by uid exposure time、duplicate req | 请求过于频繁,请稍后再试 |
8029 | 通道vip | 该用户为媒体VIP用户,无法请求广告 |
8030 | 重复请求 | 重复请求 |
8031 | sdk版本太低,无支持的模板 | SDK版本过低,请更新SDK版本 |
8032 | 敏感词过滤 | 敏感词过于严格,请登录联盟管理后台修改敏感词 |
8033 | 自渲染业务未开通 | 请联系商务开启自渲染权限 |