由于项目里接入了广告,包括但不限于穿山甲sdk和广点通sdk,视频广告是收益比较高的广告,项目里面出现的地方比较多。流程一般都是UI上有个按钮,点击按钮,请求到激励视频广告,然后直接打开该广告,也就是跳转到广告视频页面,等广告播放结束后,会有关闭按钮,点击关闭按钮,广告sdk会触发暴露出来的回调;如果请求广告失败,我们自己也会处理;这两种情况我们都是可以掌控的,也就是说这两种情况,我们都会有方法接收到事件,我们可以根据相应的事件,做出下一步的处理,比如给用户发奖励,或者重新请求广告。
打开广告激励视频的方法如下
穿山甲 : nativeAd.showRewardVideoAd(activity); // nativeAd 是 TTRewardVideoAd 类型
广点通 : rewardVideoAD.showAD(); // rewardVideoAD 是 RewardVideoAD 类型
上面方法都是 sdk 提供的方法,我们直接调用即可。但问题也来了,如果广告获取到了,同时也调用了该方法,如果广告页面没打开怎么办?上面两个方法都是 void 类型,如果说广告页面打开失败,但是没有回调事件,那么我们的UI界面由于没有接到下一步的指示,可能会一直固定在当前页面,甚至给人一种卡死的感觉,这种体验无意是很差的。
怎么办?sdk没提供方法,这就需要我们自己额外用些方法来检查了,加些防护。
这种场景比较好复现,比如说我们请求了广告,此时把app切到后台,比如说回到桌面,1秒后广告接口有广告返回,此时触发了 showRewardVideoAd(activity) 或 showAD() 方法,注意这时候app还在后台,不可见的状态,有些手机比如 oppp ,此时就不会跳转到广告页面,即使此时重新计入app,也不行。我们怎么处理呢?最简单的方式,用 Handler 延迟方法,我们在调用广告打开的方法的同时,用 Handler 延迟3秒执行段代码,检测app此时打开的Activity中,是否有当前打开的广告页面,比如 广点通的 Activity : PortraitADActivity,穿山甲的 Activity:TTRewardVideoActivity 或 TTRewardExpressVideoActivity。 老样子,
Application 的 registerActivityLifecycleCallbacks() 方法再次被用到,根据它的回调方法,来储存当前所有的 Activity, 方法如下
public class GlobalActivityLifecycle implements Application.ActivityLifecycleCallbacks {
private static GlobalActivityLifecycle sInstance;
public static void register(Application app) {
if (sInstance == null) {
synchronized (GlobalActivityLifecycle.class) {
if (sInstance == null) {
sInstance = new GlobalActivityLifecycle();
app.registerActivityLifecycleCallbacks(sInstance);
}
}
}
}
private static HashSet<String> mActivityNameSet = new HashSet<>();
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
mActivityNameSet.add(activity.getClass().getSimpleName());
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
mActivityNameSet.remove(activity.getClass().getSimpleName());
}
public static boolean isContainActivity(String activitySimpleName){
return mActivityNameSet.contains(activitySimpleName);
}
public static boolean isContainActivity(String ... names){
for(String name : names){
if(mActivityNameSet.contains(name)){
return true;
}
}
return false;
}
}
上面是工具类,下面是调用方法
Handler mHandler = new Handler(Looper.getMainLooper());
/**
* 穿山甲
*/
public void showRewardVideoAd(Activity activity, TTRewardVideoAd nativeAd) {
nativeAd.showRewardVideoAd(activity);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if(!GlobalActivityLifecycle.isContainActivity("TTRewardVideoActivity", "TTRewardExpressVideoActivity")){
// TODO: 在 这个里面做一些告知UI的操作,意思是界面打开失败了
}
}
}, 3000);
}
/**
* 广点通
*/
public void tryShowRewardVideo(RewardVideoAD rewardVideoAD) {
rewardVideoAD.showAD();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if(!GlobalActivityLifecycle.isContainActivity("PortraitADActivity")){
// TODO: 在 这个里面做一些告知UI的操作,意思是界面打开失败了
}
}
}, 3000);
}
以上是思路,具体的大家可以通过扩展方法的形参,添加回调,或者使用观察者模式发送通知,总之符合自己项目的业务逻辑即可。上面的方法是通过集合来检查 Activity 的名字,可能有人会问,为什么不直接用 App里最 top 的Activity 来检测?假如说我们成功的打开了激励视频页面,这时候我们切入了后台,或者点击了视频广告,又打开了一个新的广告页面,这样最顶端的Activity就不是激励视频广告页面了,这样就误判了。
以上只是提供一个思路,希望起到抛砖引玉的作用。