穿山甲sdk激励视频广告关闭按钮引发的bug排查及解决

项目中接入了穿山甲广告,其中包括图文广告和视频广告,后期发现一个偶现的问题,就是看完激励视频广告后,点击视频页面的关闭按钮,此时回到了app源生页面,UI卡住了,本来看完视频有个加分操作,现在没了。排查了代码,看看是不是自己的代码有漏洞,看了半天也没发现,然后就想,是不是穿山甲sdk的视频有问题,点击关闭按钮没有给客户端回调?为了验证这个问题,我写了个点击事件,请求激励视频并且播放,在穿山甲的回调地方添加log日志,看看是否有打印日志。由于bug是偶现的,那就是说需要大量的尝试,如果向上面的那样操作,点击一下请求广告,视频播放结束显示关闭按钮,再点击一下,比较麻烦,并且把一个人给定死在这里,需要不停的操作,能不能省点力?

第一步,我把请求广告的点击事件,放到了 Activity 的 onResume() 方法中,这样,刚进入该页面,或者视频看完点击关闭按钮后又回到该页面,都会触发激励视频广告,为了体验更好一点,我在 onResume() 方法中延迟了1秒去请求广告,并在请求广告的时候添加请求log日志;

第二步,我想在激励视频结束时也打印一个log日志,由于激励视频页面是SDK内部的Activity,没办法直接监听,我就从 Application 入手,它有个注册方法 registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) 这个方法,可以通过回调 onActivityDestroyed(Activity activity) 方法获取到 Activity 关闭的日志,由于激励视频 Activity 的名字是 "TTRewardVideoActivity" 或 "TTRewardExpressVideoActivity",所以判断 activity 的名字是这两个中的任何一个时,就打印广告关闭日志;

第三步,上一步操作能打印日志,是需要视频播放完了点击关闭按钮,能自动点击吗?激励视频页面是SDK内部的Activity,我们这边拿不到布局和点击事件,所以还是从上面的回调入手,但我们是在 onActivityResumed(Activity activity) 中做操作。知道了 activity 就可以 findViewById(),问题是现在也不知道关闭按钮的id;使用 Android Studio 自带的 Layout Inspector 工具来看激励视频页面的布局,视频播放完成后,点击这个工具,找到关闭按钮的id : tt_video_ad_close,它父容器是 tt_video_ad_close_layout;问题又来了,由于它是sdk内部的,使用 activity 的 findViewById() 的时候,找不到 id 对应的 R ,所以失败了;换个角度,还可以使用反射来找到id对应的view;使用 Resources 的 getIdentifier() 方法来获取对应的id;同时我也看看sdk内部 TTRewardVideoActivity 的源码,虽然被混淆了,但还是找到了具有关闭功能的按钮是 tt_video_ad_close_layout。然后就是在 onActivityResumed(Activity activity) 中做延迟31秒的事件,找到关闭按钮对应的view,调用 view.performClick() ,这样激励视频就自动关闭了;

第四步, 上述三步后,我就可以运行代码,让它自己停的播放视频广告,自动关闭,然后再播放,再关闭,循环执行,我只需要半个小时后看看日志就行了,如果sdk回调的日志比着广告页面关闭的日志少,就可以证明是sdk内部关闭按钮有问题了。但上面一个广告30秒,如果想再快一点,我们把 onActivityResumed(Activity activity) 延迟的时间,由 31 秒 调整到 5 秒,这样就大大提高了效率。

/**
 * 反射获取view的工具类
 */
class Utils{
    
    private static Resources mResources = null;
    private static String mPackageName = null;

    public static int getIdentifier(Context context, String name) {
        return getIdentifier(context, name, "id");
    }

    private static int getIdentifier(Context context, String name, String defType) {
        if (mResources == null) {
            mResources = context.getResources();
        }
        return mResources.getIdentifier(name, defType, getPackageName(context));
    }

    private static String getPackageName(Context context) {
        if (mPackageName == null) {
            mPackageName = context.getPackageName();
        }
        return mPackageName;
    }
}


跑了200次,发现确实是sdk内部关闭按钮的问题。怎么解决呢?我就想既然sdk内部没有给回调,那么我就手动加一个回调。我用了观察者模式,在写回调的类上实现 Observer 这个接口,然后再 Application.ActivityLifecycleCallbacks 的 onActivityDestroyed(Activity activity) 方法中判断是否是激励视频,然后发送一条信息,Observer 接到信息后,调用sdk的回调方法。细节注意:如果sdk内部已经回调了,观察者就不能再调用了,会重复,所以在sdk回调方法中把该观察者模式给移除掉,并且把回调置空,就样就不会重复执行了;还有个小细节,如果看完激励视频A,瞬间又触发了一个激励视频广告B,此时B的Activity已经打开,A的观察者关闭信息刚发过来,这时候会误判执行B广告的回调,所以可以在观察者接收消息的地方,加个时间判断,大于10秒的再执行。
 

class ApplicationListener implements Application.ActivityLifecycleCallbacks{

    Handler mHandler = new Handler();

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }


interface VideoListener{
    void closeListener(int code); // 0 失败,1 成功, 2 错误
}

class VideoListenerImpl implements VideoListener, Observer{

    final static long TIME_MARK = 10 * 1000;
    VideoListener mListener;
    long mStartTime = SystemClock.elapsedRealtime();

    public VideoListenerImpl(VideoListener listener) {
        this.mListener = listener;
    }

    @Override
    public void closeListener(int code) {
        if(mListener != null){
            mListener.closeListener(code);
        }
        NotifyManager.getNotifyManager().deleteObserver(this);
        mListener = null;
        Log.e("RewardVideoCSJ", "onRewardVerify:  " + code);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (isRewardVideo(arg)) {
            Log.e("RewardVideoCSJ", "update:  ");
            boolean valid = SystemClock.elapsedRealtime() - mStartTime > TIME_MARK;
            if(valid){
                closeListener(1);
            }
        }
    }

    private boolean isRewardVideo(Object arg) {
        // 通过msg判断,是否是穿山甲激励视频结束后,Application 的监听回调发送的信息
        return true;
    }
}


    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityStarted(final Activity activity) {
        String name = activity.getClass().getSimpleName();
        if ("TTRewardVideoActivity".equals(name) || "TTRewardExpressVideoActivity".equals(name)) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    View view = activity.findViewById(Utils.getIdentifier(activity, "tt_video_ad_close_layout"));
                    if (view != null) {
                        view.performClick();
                    }
                }
            }, 31 * 1000);
        }
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        String name = activity.getClass().getSimpleName();
        if ("TTRewardVideoActivity".equals(name) || "TTRewardExpressVideoActivity".equals(name)) {
            // 发送关闭消息
            Log.e("RewardVideoCSJ", "onActivityDestroyed:  "  + name +"   " + activity.hashCode());
        }
    }
}


发布了176 篇原创文章 · 获赞 11 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/104035669
今日推荐