广点通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

相关文章

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如

解决Entity Framework中自增主键的问题

《解决EntityFramework中自增主键的问题》:本文主要介绍解决EntityFramework中自增主键的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Entity Framework中自增主键问题解决办法1解决办法2解决办法3总结Entity Fram