广点通sdk广告中的激励视频关闭按钮引发的bug排查及解决

2023-10-25 06:20

本文主要是介绍广点通sdk广告中的激励视频关闭按钮引发的bug排查及解决,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前几篇写过穿山甲sdk激励视频按钮丢失回调的问题,今天说说广点通sdk激励视频的问题。可以先看看前文:
https://blog.csdn.net/Deaht_Huimie/article/details/104035669 ​​​​​​​ ,然后再看本篇文章,这两篇文章给大伙起到抛砖引玉的作用。


由于穿山甲sdk蹦出的关闭按钮问题,那么我很自然的想到了广点通sdk的激励视频是否也存在相同的问题。还按照老思路,打了一堆日志,来看看日志是否有确失。先用 adb 命令来看看激励视频 Activity 的名字,发现是 PortraitADActivity。 接着就可以在通过实现 Application.ActivityLifecycleCallbacks 接口的方法,在 onActivityResumed(Activity activity) 中来实现视频播放4秒就关闭 Activity 的操作了。搜索 PortraitADActivity 这个类,发现了它是个空类,继承了 ADActivity 这个类,

public class PortraitADActivity extends ADActivity {public PortraitADActivity() {}
}

ADActivity 中,看着不禁笑了,这是个壳子,看到这种写法,明显是初代插件化的写法,ACTD 是个接口,真正的逻辑是放在 ACTD 的实现类中,通过 ADActivity 的生命周期回调方法,调用 ACTD 的方法。

public class ADActivity extends Activity {private ACTD a;public ADActivity() {}protected void onCreate(Bundle savedInstanceState) {ADActivity var2 = this;Bundle var3;if ((var3 = this.getIntent().getExtras()) != null) {String var4 = var3.getString("gdt_activity_delegate_name");String var6 = var3.getString("appid");if (!StringUtil.isEmpty(var4) && !StringUtil.isEmpty(var6)) {try {if (GDTADManager.getInstance().initWith(var2.getApplicationContext(), var6)) {var2.a = GDTADManager.getInstance().getPM().getPOFactory().getActivityDelegate(var4, var2);if (var2.a == null) {GDTLogger.e("Init ADActivity Delegate return null,delegateName" + var4);}} else {GDTLogger.e("Init GDTADManager fail in AdActivity");}} catch (Throwable var5) {GDTLogger.e("Init ADActivity Delegate Faile,DelegateName:" + var4, var5);}}}if (this.a != null) {this.a.onBeforeCreate(savedInstanceState);} else {this.finish();}super.onCreate(savedInstanceState);if (this.a != null) {this.a.onAfterCreate(savedInstanceState);}}}    

看到这,我们也不知道 ACTD 的实现类是什么,没办法了,只能想办法打印下 a 的值,它是个私有的属性,怎么办呢?反射这时候闪亮登场了,方法如下

    @Overridepublic void onActivityResumed(Activity activity) {String name = activity.getClass().getSimpleName();if("PortraitADActivity".equals(name)){try {Field field = getDeclaredField(activity, "a");field.setAccessible(true);Object obj = field.get(activity);Log.e("PortraitADActivity", "obj: " +  obj.toString());} catch (Exception e) {e.printStackTrace();}}}public Field getDeclaredField(Object object, String fieldName){Field field = null ;Class<?> clazz = object.getClass() ;for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {try {field = clazz.getDeclaredField(fieldName) ;return field ;} catch (Exception e) {}}return null;}


打印的值为  PortraitADActivity: obj: com.qq.e.comm.plugin.rewardvideo.f@1c668d2 ,我们知道了 ACTD 的实现类是 com.qq.e.comm.plugin.rewardvideo.f,找找这个类,居然没有?原来项目里引用的是 arr 包,那么把它解压,发现有个 assets 的文件夹,继续进去,发现 gdtadv2.jar 这个jar,使用反编译工具,看看它里面的代码。果然,在里面找到了f 这个类,里面都是混淆过后的代码,它实现了 OnClickListener 等接口。

public class f implements OnClickListener, ACTD, com.qq.e.comm.plugin.ac.b.a, com.qq.e.comm.plugin.b.d.a {public void onClick(View v) {if (v == this.f) {this.w.b(System.currentTimeMillis());a(1, null);} else if (v == this.n.a()) {...} else if (v == this.m.b()) {this.v.f();this.a.finish();}...}
}

我们知道,关闭按钮的点击事件不出意外就在这个里面实现。只看代码的话,关闭按钮点击事件大体可以定位到 v == this.m.b() 这个判断里。下一步就是想办法获取到关闭按钮,还是使用 Android studio 自带的工具 Layout Inspector 来看看激励视频的布局,我看了视频播放中和播放结束时的布局,显示如下


从这两张图上可以看出,关闭按钮一直都在,视频播放时处于隐藏状态,视频播放结束时显示出来,并且该 ImageView 没有设置 id,我们想通过 findViewById() 的方法就行不通了;细看布局,发现它外面的布局是 c,那么办法来了,简单点的,我们可以找到 R.id.content 的容器,然后通过获取它的子view,继续,知道获取到 ImageView;如果想给自己找点麻烦,顺便想看看 c 这个容器到底是何方神圣,那么我们就获取子view的时候,到 c 就停止。我们继续看 com.qq.e.comm.plugin.rewardvideo.f 这个类,onAfterCreate(Bundle savedInstanceState) 中,有几点注意

    public void onAfterCreate(Bundle savedInstanceState) {...this.v = d.b(this.a.getIntent().getIntExtra(aa.a, 0));if (this.v == null) {GDTLogger.e("RewardVideo activity fail to create ! ad instance pass failed");this.a.finish();m.a(20152, 0, this.s);} else if (this.c.W()) {...e();g();} else {this.v.a(5001);this.a.finish();m.a(20052, 2, this.s);}}

我们会发现,它里面会做校验,如果不符合的话会直接调用 this.a.finish() 方法,会直接把 Activity 关闭,这种情况下,通过代码分析,不会有回调;看看 e() 这个方法,

    private void e() {this.g = new RelativeLayout(this.a);this.m = new c(this.a);...this.m.setVisibility(View.INVISIBLE);this.m.b().setOnClickListener(this);this.m.a(this.g);...this.a.setContentView(this.g, new LayoutParams(-1, -1));...}

上述是简化代码,可以看出 g 就是上图中 content 的子view, c 被创建后,隐藏了起来,看看 c 的代码

public class c extends RelativeLayout {private final ImageView a;private final a b;public c(Context context) {super(context);this.b = new f(context, null).a();this.b.f();addView(this.b.b(), new LayoutParams(-1, -1));this.a = new ImageView(context);this.a.setScaleType(ScaleType.FIT_CENTER);this.a.setImageBitmap(an.a("iVBOR...mCC\n"));ViewGroup.LayoutParams layoutParams = new LayoutParams(aj.a(context, 30), aj.a(context, 30));layoutParams.addRule(9, -1);layoutParams.addRule(10, -1);layoutParams.topMargin = aj.a(context, 15);layoutParams.leftMargin = aj.a(context, 20);addView(this.a, layoutParams);if (GDTADManager.getInstance().getSM().getInteger("rewardVideoEndcardSoft", 0) == 1) {setLayerType(1, null);} else {this.b.b().setBackgroundColor(0);}}public a a() {return this.b;}public void a(ViewGroup viewGroup) {if (getParent() == null) {viewGroup.addView(this, new ViewGroup.LayoutParams(-1, -1));}}public View b() {return this.a;}
}

可以看出,c 是个相对布局,它的构造方法中创建了 ImageView,并添加到自己里面,a(ViewGroup viewGroup)  方法是把自己添加到了 g 里面, b() 对外暴露的就是关闭按钮 ImageView,这是重新看上面 e() 方法中的代码,就明白了,通过调用 b() 获取关闭按钮,然后设置点击事件,可以再对照下 onClick(View v) 方法,它里面的 v.f() 是发送消息,调用回调方法的。

知道对完提供的方法了,那么老样子,继续使用反射,开始

    private void performCloseView(Activity activity){FrameLayout frameLayout = (FrameLayout) activity.findViewById(Window.ID_ANDROID_CONTENT);RelativeLayout relativeLayout = (RelativeLayout) frameLayout.getChildAt(0);if(relativeLayout != null){try {View viewC = relativeLayout.getChildAt(0);Class clazz = viewC.getClass();Method method = clazz.getMethod("b");View view = (View) method.invoke(viewC);view.performClick();} catch (Exception e) {e.printStackTrace();}}}

这样,就找到关闭按钮,并且调用了它的点击事件。我在大量打log的情况,在 ActivityLifecycleCallbacks 的 onActivityResumed(Activity activity) 方法中,使用 Handler 延迟4秒执行上面的方法,这样,视频播放4秒就关闭了,不用傻傻的几十秒才能进行下一次请求。

本篇是在 https://blog.csdn.net/Deaht_Huimie/article/details/104035669​​​​​​​ 的基础上讲的,如果感觉跳跃性大的话,建议先看看上篇笔记。如何检测关闭按钮的思路和流程都在上一篇中,本篇就是对前面的一个补充。广点通sdk是把广告放到两个部分里,实现了动态加载,如果在 Android Studio 中直接看的话,有一部分会隐藏。这样会更安全些、灵活些?如果我们也封装sdk的话,这是个思路。请知道的大佬给予指正。
 

这篇关于广点通sdk广告中的激励视频关闭按钮引发的bug排查及解决的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/280599

相关文章

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

Python Jupyter Notebook导包报错问题及解决

《PythonJupyterNotebook导包报错问题及解决》在conda环境中安装包后,JupyterNotebook导入时出现ImportError,可能是由于包版本不对应或版本太高,解决方... 目录问题解决方法重新安装Jupyter NoteBook 更改Kernel总结问题在conda上安装了

Goland debug失效详细解决步骤(合集)

《Golanddebug失效详细解决步骤(合集)》今天用Goland开发时,打断点,以debug方式运行,发现程序并没有断住,程序跳过了断点,直接运行结束,网上搜寻了大量文章,最后得以解决,特此在这... 目录Bug:Goland debug失效详细解决步骤【合集】情况一:Go或Goland架构不对情况二:

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

解决jupyterLab打开后出现Config option `template_path`not recognized by `ExporterCollapsibleHeadings`问题

《解决jupyterLab打开后出现Configoption`template_path`notrecognizedby`ExporterCollapsibleHeadings`问题》在Ju... 目录jupyterLab打开后出现“templandroidate_path”相关问题这是 tensorflo

如何解决Pycharm编辑内容时有光标的问题

《如何解决Pycharm编辑内容时有光标的问题》文章介绍了如何在PyCharm中配置VimEmulator插件,包括检查插件是否已安装、下载插件以及安装IdeaVim插件的步骤... 目录Pycharm编辑内容时有光标1.如果Vim Emulator前面有对勾2.www.chinasem.cn如果tools工

Java多线程父线程向子线程传值问题及解决

《Java多线程父线程向子线程传值问题及解决》文章总结了5种解决父子之间数据传递困扰的解决方案,包括ThreadLocal+TaskDecorator、UserUtils、CustomTaskDeco... 目录1 背景2 ThreadLocal+TaskDecorator3 RequestContextH

解决JavaWeb-file.isDirectory()遇到的坑问题

《解决JavaWeb-file.isDirectory()遇到的坑问题》JavaWeb开发中,使用`file.isDirectory()`判断路径是否为文件夹时,需要特别注意:该方法只能判断已存在的文... 目录Jahttp://www.chinasem.cnvaWeb-file.isDirectory()遇

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.