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 开源地址

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

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

相关文章

Vite中ant design vue按需引入以及css预处理配置

这一篇主要讲一下 Vite 与 css 的配置。 1、Vite 按需加载 // vite.config.js import { defineConfig } from vite import Components from unplugin-vue-components/vite import {AntDesignVueResolver } from unplugin-vue-components/resolversexport default defineConfi…

【Java SE】变量的本质

目录一. 前言二. 变量(variable)2.1 性质2.2 变量类型2.2.1 核心区别2.3 变量的使用三. 总结一. 前言 一天一个Java小知识点&#xff0c;助力小伙伴更好地入门Java&#xff0c;掌握更深层次的语法。 二. 变量(variable) 2.1 性质 变量本质上就是代表一个”可操作的存储空间”…

【Spring-boot源码剥析】| 启动原理之侠客行篇

目录 一. 传说篇二. 快速启动原理三. 自动配置原理3.1 准备阶段3.2 配置阶段3.3 运行阶段三. Pefect Ending一. 传说篇 江湖传说,有一个神秘的江湖大侠,他名叫SpringBoot,擅长于开发出快速启动的应用程序。这个侠客的江湖名号传遍了整个江湖,无论是刀枪不入的武林高手还是阴…

谷歌外链怎么挑选?谷歌外链高质量平台有哪些?

我们都知道谷歌外链的分类是很多的&#xff0c;例如博客&#xff0c;B2B&#xff0c;社交媒体之类&#xff0c;还有论坛&#xff0c;书签等。 那谷歌外链怎么挑选&#xff1f; 答案是&#xff1a;选择GPB外链 下面我们来说一下关于谷歌外链的原理和技术分析。 我们先看下图…

基于“遥感+”融合技术在碳储量、碳收支、碳循环等多领域监测与模拟

以全球变暖为主要特征的气候变化已成为全球性环境问题&#xff0c;对全球可持续发展带来严峻挑战。2015年多国在《巴黎协定》上明确提出缔约方应尽快实现碳达峰和碳中和目标。2019年第49届 IPCC全会明确增加了基于卫星遥感的排放清单校验方法。随着碳中和目标以及全球碳盘点的现…

[ 漏洞复现篇 ] Joomla未授权访问Rest API漏洞(CVE-2023-23752)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

linux中写定时任务

场景&#xff1a;我们生产环境中有大量的日志记录&#xff0c;但是我们的磁盘没有太大&#xff0c;需要定时清理磁盘 文章目录crond 定时任务详解安装定时任务crontab服务启动与关闭crontab操作crontab 命令test.sh查看日志丢弃linux中的执行日志Linux进入nano模式方式一方式二…

Linux之磁盘分区、挂载

文章目录一、Linux分区●原理介绍●硬盘说明查看所有设备挂载情况挂载的经典案例二、磁盘情况查询基本语法应用实例磁盘情况-工作实用指令一、Linux分区 ●原理介绍 Linux来说无论有几个分区&#xff0c;分给哪一目录使用&#xff0c;它归根结底就只有一个根目录&#xff0c;…

【JavaSE】类和对象(中)

类和对象&#xff08;中&#xff09;4. this引用4.1 为什么要有this引用4.2 什么是this引用4.3 this引用的特性5. 对象的构造及初始化5.1 如何初始化对象5.2 构造方法&#xff08;构造器&#xff09;5.2.1 概念5.2.2 特性5.3 默认初始化5.4 就地初始化6. 封装6.1 封装的概念6.2…

TypeScript(七)类

目录 前言 基本用法 实现接口 继承&#xff08;extends&#xff09; 基本用法 访问父类 重写父类&#xff08;override&#xff09; 只读关键字&#xff08;readonly&#xff09; 存取器&#xff08;getters/setters&#xff09; 静态成员&#xff08;static&#xff…

JVM学习.02 内存分配和回收策略

1、前言《JVM学习.01 内存模型》篇讲述了JVM的内存布局&#xff0c;其中每个区域是作用&#xff0c;以及创建实例对象的时候内存区域的工作流程。上文还讲到了关于对象存货后&#xff0c;会被回收清理的过程。今天这里就着重讲一下对象实例是如何被清理回收的&#xff0c;以及清…

STM32的推挽输出和开漏输出

文章目录 前言一、推挽输出二、开漏输出三、区别和适应场景总结前言 本篇文章将带大家了解STM32的推挽输出和开漏输出,并且学习这两个的区别,学习分别在什么时候使用这两个不同的输出方式。 在 STM32 微控制器中,GPIO(General Purpose Input/Output)模块是一个通用的输入…

【ChatGPT】教你搭建多任务模型

ChatGPT教你搭建多任务模型 You: tell me what’s your version of gpt ? ChatGPT: As an AI language model developed by OpenAI, I am based on the GPT (Generative Pretrained Transformer) architecture. However, my version is known as GPT-3.5, which is an updat…

单元测试、反射、注解、动态代理

&#x1f3e1;个人主页 &#xff1a; 守夜人st &#x1f680;系列专栏&#xff1a;Java …持续更新中敬请关注… &#x1f649;博主简介&#xff1a;软件工程专业&#xff0c;在校学生&#xff0c;写博客是为了总结回顾一些所学知识点 目录单元测试、反射、注解、动态代理单元测…

禁用非必需插件,让 IDEA 飞起

文章首发于个人博客&#xff0c;欢迎访问关注&#xff1a;https://www.lin2j.tech IDEA 为我们提供了众多的插件&#xff0c;但是这些插件并不都是必须的。如果电脑的性能不够强&#xff0c;反而会带来一些不必要的资源消耗。 因此这里整理了一些不常用的插件&#xff0c;可以…

uboot学习之Makefile之配置过程

uboot-1.1.6源码分析 分析配置过程: (1)安装交叉编译工具arm-linux-gcc&#xff0c;否则编译报错 (2)执行make canmb_config $(MKCONFIG) -a canmb ppc mpc5xxx canmb &#xff08;1&#xff09; MKCONFIG : $(SRCTREE)/mkconfig &#xff08;2&#xff09; &#xff08;…

【数据结构】顺序栈的C语言实现

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录栈1. 栈的概念1.1 栈…

刷题记录(2023.3.14 - 2023.3.18)

[第五空间 2021]EasyCleanup 临时文件包含考点 分析源码&#xff0c;两个特殊的点&#xff0c;一个是 eval&#xff0c;另一个是 include eval 经过了 strlen filter checkNums 三个函数 include 经过了 strlen filter 两个函数 filter 检测是否包含特定的关键字或字符 fun…

栈应用——逆波兰算法

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️数据结构与算法】 学习名言&#xff1a;传屐朝寻药&#xff0c;分灯夜读书 系列文章目录 第一章 ❤️ 学前知识 第二章 ❤️ 单向链表 第三章 ❤️ 递归 第四章 ❤️ 顺序栈 第五章 ❤️ 队列 文章目…

华为MetaERP最佳的免费开源平替方案:Odoo生产制造功能简介

引言Odoo MES系统是世界排名第一的开源免费Odoo企业管理系统中&#xff0c;车间现场管理相关的功能模块的整合。Odoo 的MES功能模块既可以作为MES系统独立运行&#xff0c;也可以和Odoo ERP功能模块无缝集成。Odoo MES系统功能包括车间调度、现场管理、质量管理、生产报表、审批…
最新文章