Android Lancet Aop 字节编码修复7.1系统Toast问题(WindowManager$BadTokenException)

近期在Bugly上出现7.1以下设备上出现大量BadTokenException:

android.view.WindowManager$BadTokenException

Unable to add window -- token android.os.BinderProxy@6c0415d is not valid; is your activity running?

报错堆栈,如下所示:
在这里插入图片描述

1.定位分析

查看Toast的源码可知,在android 7.1版本及其以下,没有对wm.addview()进行异常捕捉:
在这里插入图片描述
官方在android8.0 以上修复该问题,源码如下:
在这里插入图片描述

2.解决方案

2.1 先hook Toast 进行代理捕捉异常

通过查看源码可知,TN#Handler是一个hook点,可以对其进行hook 替代,捕捉异常,核心代码如下:

public class SafetToast {
   private static final String TAG="SafetToast";

   /**
    * 处理7.x 的toast 异常 ,代理TN#Handler
    * <p>
    * toast 源码地址:
    * https://cs.android.com/android/platform/superproject/+/android-7.1.0_r1:frameworks/base/core/java/android/widget/Toast.java;bpv=1;bpt=1
    *
    * @param toast
    */
   public static Toast fixToastWithAndroid7(Toast toast) {
      if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
         try {
            Class<?> toastClass = Toast.class;
            Field mTNField = toastClass.getDeclaredField("mTN");
            mTNField.setAccessible(true);
            Object mTN = mTNField.get(toast);
            Field handleField = mTN.getClass().getDeclaredField("mHandler");
            handleField.setAccessible(true);
            final Handler handler = (Handler) handleField.get(mTN);
            Handler proxyHandler = new Handler(handler.getLooper()) {
               @Override
               public void dispatchMessage(Message msg) {
                  try {
                     Log.w(TAG," proxy toast handle");
                     handler.dispatchMessage(msg);
                  } catch (Exception e) {
                     e.printStackTrace();
                  }
               }
            };
            handleField.set(mTN, proxyHandler);
            Log.w(TAG,"fixToastWithAndroid7");
         } catch (Exception e) {
            e.printStackTrace();
         }
      }else{
         Log.w(TAG," current device not need fix toast");
      }
      return toast;
   }
}

2.2 Lancet aop字节编码进行全局替换

在实际上开发中,存在各种第三方的sdk, 存在Toast 处理点不同的问题,需要通过Aop方式进行替换。

编写Lancet 核心代码

public class LancetTools {
    private static final  String TAG="LancetTools";
    @Proxy(value = "show")
    @TargetClass(value = "android.widget.Toast")
    public  void show() {
       Toast toast= (Toast) This.get();
       SafetToast.fixToastWithAndroid7(toast);
       Origin.callVoid();
    }

}

以上代码比较简单,在编译过程中,生成dex文件之前,对class文件中每个Toast.show()进行编码操作,替换成以上代码。进行hook,接着继续调用原有逻辑;

在实际上开发中,存在多个渠道包问题和Lancet 代码修改后会全量编译问题。
最佳的做法是:根据渠道动态加载Lancet 插件和抽象出一个Library模块管理有关Lancet api 代码

若是不存在多渠道包,或者变种包,则进行正常的配置便可

因项目中在vivo渠道包中使用该功能,进行验证修复效果。
进行以下操作:
在Root目录下的build.gradle中:

buildscript {
    //定义一个开关变量, 判断渠道包任务
    ext.lancet_open = gradle.startParameter.taskNames.any {
         it.contains('vivo')|| it.contains('Vivo')
    }
    dependencies {
     
        //classpath 'me.ele:lancet-plugin:1.0.6'
        // 用于解决asm6问题
        classpath 'com.bytedance.tools.lancet:lancet-plugin-asm6:1.0.2'
    }
}

在App module中:

apply plugin: 'com.android.application'
if (lancet_open) {
    //动态依赖该plugin插件
    apply plugin: 'me.ele.lancet'
}

dependencies {
    //vivo 渠道中依赖
     vivoImplementation project(':lancetLib')
     // lancetLib中已经依赖该库,因此不需要再次依赖
    //compileOnly 'me.ele:lancet-base:1.0.6'  
}

最后创建一个LancetLib的moudle, 编写lancet api 相关的代码:
在这里插入图片描述

3.测试验证

3.1 查看apk中字节编码后代码

项目中原本的代码:
在这里插入图片描述

经过lancet aop 字节编码后的代码,查看apk中代码:
在这里插入图片描述

3.2 运行Logcat 日志:

在Android 7.1及其以下设备运行:
在这里插入图片描述
成功打印日志,进入到hook toast的dispatchMessage()中,一次Toast 会有一次show 一次hide,因此会打印两遍proxy toast handle

4.进一步学习Lancet 字节编码

Lancet 常用的两种纺织方式

1.@Insert 指令
顾名思义,是在原本函数执行前或者执行后插入一段逻辑,在中转函数中接着调用原本的旧逻辑函数。通常用于项目或者sdk中创建的类。

2.@Proxy指令
顾名思义,是代理原本的方法逻辑,进行替换,执行新的逻辑操作(在中转函数中可摒弃旧的函数,也可以继续调用旧的函数)。通用对Android系统类 Api 调用。

匹配目标类
1.@TargetClass 通过类名来匹配
Scope.SELF 代表仅匹配 value 指定的目标类.
Scope.DIRECT 代表匹配 value 指定类的直接子类.
Scope.All 代表匹配 value 指定类的所有子类.
Scope.LEAF 代表匹配 value 指定类的最终子类.众所周知java是单继承,所以继承关系是树形结构,所以这里代表了指定类为顶点的继承树的所有叶子节点

2.@ImplementedInterface 通过接口来匹配
Scope.SELF : 代表直接实现所有指定接口的类.
Scope.DIRECT : 代表直接实现所有指定接口,以及指定接口的子接口的类.
Scope.ALL: 代表 Scope.DIRECT 指定的所有类及他们的所有子类.
Scope.LEAF: 代表 Scope.ALL 指定的森林结构中的所有叶节点.

申明方法注意点
保持 Hook 方法的 public/protected/private static 信息与目标方法一致,参数类型,返回类型与目标方法一致。返回类型可以用 Object 代替。方法名不限.。异常声明也不限。

通过一个案例进一步了解Lancet ,在AppCompatActivity的子类中onStop()执行前插入一段
System.out.println("hello world");

    @TargetClass(value = "androidx.appcompat.app.AppCompatActivity", scope = Scope.LEAF)
    @Insert(value = "onStop",mayCreateSuper = true)
    protected void onStop(){ // 修复符 和static 信息与目标方法一致,参数类型,返回类型与目标方法一致
        System.out.println("hello world");
        Origin.callVoid();
    }

Scope.LEAF :该类中所有的子类节点上
mayCreateSuper true: 当该方法没有重写时,会自动重写。

接着构建apk ,查看字节编码后的效果:
在这里插入图片描述

更多详细,请阅读Lancet 开源地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2174.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据分析师CDA认证 Level Ⅰ笔记

**黑色字体部分为考纲&#xff0c;蓝色字体部分为笔记&#xff0c;仅供参考 PART 1 数据分析概念与职业操守 1、数据分析概念、方法论、角色 【领会】 数据分析基本概念&#xff08;数据分析、数据挖掘、大数据&#xff09; 数据分析目的及其意义 数据分析方法与流程 数据分析的…

【网络安全工程师】从零基础到进阶,看这一篇就够了

学前感言 1.这是一条需要坚持的道路&#xff0c;如果你只有三分钟的热情那么可以放弃往下看了。 2.多练多想&#xff0c;不要离开了教程什么都不会&#xff0c;最好看完教程自己独立完成技术方面的开发。 3.有问题多google,baidu…我们往往都遇不到好心的大神&#xff0c;谁…

【Leetcode】队列实现栈和栈实现队列

目录 一.【Leetcode225】队列实现栈 1.链接 2.题目再现 3.解法 二.【Leetcode232】栈实现队列 1.链接 2.题目再现 3.解法 一.【Leetcode225】队列实现栈 1.链接 队列实现栈 2.题目再现 3.解法 这道题给了我们两个队列&#xff0c;要求去实现栈&#xff1b; 首先&…

8大核心语句,带你深入python

人生苦短 我用python 又来给大家整点好东西啦~ 咱就直接开练噜&#xff01;内含大量代码配合讲解 python 安装包资料:点击此处跳转文末名片获取 1. for - else 什么&#xff1f;不是 if 和 else 才是原配吗&#xff1f; No&#xff0c;你可能不知道&#xff0c; else 是个…

Cache的地址结构,tag到底与Cache什么关系,Cache容量与总容量,Cache行长,Cache字地址?

目录.Cache映射的问题一.Cache的三种映射重点&#xff1a;那么我说1.直接映射2.全相联映射3.组相联映射4.总结三种映射二.Cache的三个字眼(例题)1.Cache字地址多少位&#xff08;字地址即按字编址&#xff09;2.Cache容量与总容量3.Cache行长一.Cache的三种映射 重点&#xff…

C++ 类与对象

结构体与类&#xff1a;在C语言中结构体可以存储一些不同类型的数据&#xff0c;这个功能就很强大了&#xff0c;但是这些数据都是不安全的我们可以在主函数中随意修改它&#xff0c;在C中的类可以很好的解决这个问题。类就相当于C语言中的结构体一样&#xff0c;C结构体&#…

GC 垃圾回收机制

文章目录JVM 的内存模型对象存活&#xff1f;引用计数算法可达性分析算法垃圾收集标记-清除算法标记-复制算法标记-整理算法垃圾收集器垃圾收集器发展Serial / Serial OldParallel Scavenge / Parallel OldParNew / CMSG1ZGC扩展JVM 的内存模型 Java 虚拟机&#xff08;Java V…

转速/线速度/角速度计算FC

工业应用中很多设备控制离不开转速、线速度的计算,这篇博客给大家汇总整理。张力控制的开环闭环方法中也离不开转速和线速度的计算,详细内容请参看下面的文章链接: PLC张力控制(开环闭环算法分析)_plc的收卷张力控制系统_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不…

按键修改阈值功能、报警功能、空气质量功能实现(STM32)

按键修改阈值功能 要使用按键&#xff0c;首先要定义按键。通过查阅资料&#xff0c;可知按键的引脚如图所示&#xff1a;按键1&#xff08;S1&#xff09;通过KEY0与PA0连接&#xff0c;按键2&#xff08;S2&#xff09;通过KEY1与PE2连接&#xff0c;按键3&#xff08;S3&…

收到6家大厂offer,我把问烂了的《Java八股文》打造成3个文档。共1700页!!

前言大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了&#xff0c;考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些程序员了。这不&#…

【LINUX】初识文件系统

文章目录一、前言二、回顾C语言文件操作三、初识系统调用openreadwriteclose四、文件系统初识五、结语一、前言 二、回顾C语言文件操作 int main() {FILE* fp fopen("log.txt", "w");if (fp NULL){perror("fopen");}int cnt 0;fputs("…

spring2

1.Spring配置数据源1.1 数据源&#xff08;连接池&#xff09;的作用 数据源(连接池)是提高程序性能如出现的事先实例化数据源&#xff0c;初始化部分连接资源使用连接资源时从数据源中获取使用完毕后将连接资源归还给数据源常见的数据源(连接池)&#xff1a;DBCP、C3P0、BoneC…

js实现一个简单的扫雷

目录先看下最终的效果&#xff1a;首先来分析一个扫雷游戏具有哪些功能分析完成后我们就开始一步步的实现1. 相关html和css2. 我们使用类来完成相应功能3. 之后我们则是要定义一个地图4. 对地图进行渲染5. 对开始按钮添加点击事件6. 现在我们可以实现鼠标左击扫雷的功能7. 给单…

网络知识汇总

文章目录一、 [网络拓扑结构](https://blog.csdn.net/weixin_52140964/article/details/127786157)二、[空间组网](https://blog.csdn.net/weixin_43509834/article/details/123225995)三、卫星网络发展历程四、SDN的由来五、ECMP、WCMP一、 网络拓扑结构 又称分布式结构。 任…

面试热点题:回溯算法 电话号码的字母组合与组合总和

前言&#xff1a; 如果你一点也不了解什么叫做回溯算法&#xff0c;那么推荐你看看这一篇回溯入门&#xff0c;让你快速了解回溯算法的基本原理及框架 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。…

2023/3/21总结

题解&#xff1a; E - 2xN Grid (atcoder.jp) 1.这一题&#xff0c;就是求出第一行与第二行相对应的相同数字的个数&#xff0c;看数据有多大&#xff0c;我们就知道&#xff0c;这道题目&#xff0c;是不可能穷举的。 2.因此&#xff0c;我们需要 找到规律去写它。此题需要…

最新消息!信息系统项目管理师教程改版!

《信息系统项目管理师教程&#xff08;第4版&#xff09;》于2023年3月出版 新版《信息系统项目管理师考试大纲》于2023年3月出版 信息系统项目管理师教程改版说明 一、整体说明 1、教材整体说明&#xff1a;第四版教材由25章组成&#xff0c;相比第三版28章内容减少了3章主要…

硬件速攻-AT24CXX存储器

AT24C02是什么&#xff1f; AT24CXX是存储芯片&#xff0c;驱动方式为IIC协议 实物图&#xff1f; 引脚介绍&#xff1f; A0 地址设置角 可连接高电平或低电平 A1 地址设置角 可连接高电平或低电平 A2 地址设置角 可连接高电平或低电平 1010是设备前四位固定地址 &#xf…

QT | 编写一个简单的上位机

QT | 编写一个简单的上位机 时间&#xff1a;2023-03-19 参考&#xff1a; 1.易懂 | 手把手教你编写你的第一个上位机 2.QT中修改窗口的标题和图标 3.图标下载 1.打开QT Creator 2.新建工程 Qt Creator 可以创建多种项目&#xff0c;在最左侧的列表框中单击“Application”&am…

学校教的Python,找工作没企业要,太崩溃了【大四真实求职经历】

如果只靠学校学的东西去找工作&#xff0c;能找到工作吗&#xff1f; 今天给大家看一个粉丝的真实求职案例&#xff0c;想做Python方面的工作&#xff0c;投了二十几个简历却没人要&#xff0c;心态崩了。为什么没人要&#xff1f;我来告诉你答案。 然后我还会结合我的这些年的…