广点通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: 非法字符: ‘\ufeff‘的解决步骤以及说明

《idea报错java:非法字符:‘ufeff‘的解决步骤以及说明》:本文主要介绍idea报错java:非法字符:ufeff的解决步骤以及说明,文章详细解释了为什么在Java中会出现uf... 目录BOM是什么?1. BOM的作用2. 为什么会出现 \ufeff 错误?3. 如何解决 \ufeff 问题?最

Spring三级缓存解决循环依赖的解析过程

《Spring三级缓存解决循环依赖的解析过程》:本文主要介绍Spring三级缓存解决循环依赖的解析过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、循环依赖场景二、三级缓存定义三、解决流程(以ServiceA和ServiceB为例)四、关键机制详解五、设计约

解决tomcat启动时报Junit相关错误java.lang.ClassNotFoundException: org.junit.Test问题

《解决tomcat启动时报Junit相关错误java.lang.ClassNotFoundException:org.junit.Test问题》:本文主要介绍解决tomcat启动时报Junit相... 目录tomcat启动时报Junit相关错误Java.lang.ClassNotFoundException

解决Maven项目报错:failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0的问题

《解决Maven项目报错:failedtoexecutegoalorg.apache.maven.plugins:maven-compiler-plugin:3.13.0的问题》这篇文章主要介... 目录Maven项目报错:failed to execute goal org.apache.maven.pl

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法

《SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法》在SQLyog中执行存储过程时出现的前置缩进问题,实际上反映了SQLyog对SQL语句解析的一个特殊行为,本文给大家介绍了详... 目录问题根源正确写法示例永久解决方案为什么命令行不受影响?最佳实践建议问题根源SQLyog的语句分

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

MyBatis模糊查询报错:ParserException: not supported.pos 问题解决

《MyBatis模糊查询报错:ParserException:notsupported.pos问题解决》本文主要介绍了MyBatis模糊查询报错:ParserException:notsuppo... 目录问题描述问题根源错误SQL解析逻辑深层原因分析三种解决方案方案一:使用CONCAT函数(推荐)方案二: