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

相关文章

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

如何解决线上平台抽佣高 线下门店客流少的痛点!

目前,许多传统零售店铺正遭遇客源下降的难题。尽管广告推广能带来一定的客流,但其费用昂贵。鉴于此,众多零售商纷纷选择加入像美团、饿了么和抖音这样的大型在线平台,但这些平台的高佣金率导致了利润的大幅缩水。在这样的市场环境下,商家之间的合作网络逐渐成为一种有效的解决方案,通过资源和客户基础的共享,实现共同的利益增长。 以最近在上海兴起的一个跨行业合作平台为例,该平台融合了环保消费积分系统,在短

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

速盾高防cdn是怎么解决网站攻击的?

速盾高防CDN是一种基于云计算技术的网络安全解决方案,可以有效地保护网站免受各种网络攻击的威胁。它通过在全球多个节点部署服务器,将网站内容缓存到这些服务器上,并通过智能路由技术将用户的请求引导到最近的服务器上,以提供更快的访问速度和更好的网络性能。 速盾高防CDN主要采用以下几种方式来解决网站攻击: 分布式拒绝服务攻击(DDoS)防护:DDoS攻击是一种常见的网络攻击手段,攻击者通过向目标网

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法   消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法 [转载]原地址:http://blog.csdn.net/x605940745/article/details/17911115 消除SDK更新时的“

Jenkins 插件 地址证书报错问题解决思路

问题提示摘要: SunCertPathBuilderException: unable to find valid certification path to requested target...... 网上很多的解决方式是更新站点的地址,我这里修改了一个日本的地址(清华镜像也好),其实发现是解决不了上述的报错问题的,其实,最终拉去插件的时候,会提示证书的问题,几经周折找到了其中一遍博文

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置