From c2bf7f960d4ae7d85aa8af6c7bb4ebb5b458acdf Mon Sep 17 00:00:00 2001 From: Omooo <869759698@qq.com> Date: Wed, 23 Jan 2019 23:59:13 +0800 Subject: [PATCH] add UncaughtExceptionHandler --- blogs/Android/APT.md | 154 ++++++++++++++++++++++++- blogs/Android/Hook.md | 32 +++++ blogs/Android/Socket 编程.md | 20 ++++ blogs/Java/UncaughtExceptionHandler.md | 101 ++++++++++++++++ 4 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 blogs/Android/Hook.md create mode 100644 blogs/Android/Socket 编程.md create mode 100644 blogs/Java/UncaughtExceptionHandler.md diff --git a/blogs/Android/APT.md b/blogs/Android/APT.md index a9e4673..c2af177 100644 --- a/blogs/Android/APT.md +++ b/blogs/Android/APT.md @@ -1,18 +1,164 @@ --- -APT +APT、JavaPoet 实现 ButterKnife --- #### 目录 1. 思维导图 2. 概述 -3. ButterKnife 的实现 -4. 参考 +3. APT +4. AutoService +5. JavaPoet +6. ButterKnife 的实现 +7. 参考 #### 思维导图 #### 概述 +用 APT、JavaPoet、AutoService 实现简单的 ButterKnife,APT 负责处理编译时注解,JavaPoet 用于生成 Java 代码,AutoService 负责注册注解处理器。 + +#### APT 注解处理器 + APT(Annotation Processing Tool)即注解处理器,是一种注解处理工具,用来在编译器扫描和处理注解,通过注解来生成 Java 文件。即以注解作为桥梁,通过预先规定好的代码生成规则来自动生成 Java 文件。此类注解框架的代表有 ButterKnife、Dagger2、EventBus 等。 -Java API 已经提供了扫描源码并解析注解的框架,开发者可以通过继承 AbstractProcessor 类来实现自己的注解处理逻辑。APT 的原理是在注解了某些代码元素(如字段、函数、类等)后,在编译时编译器会检查 AbstractProcessor 的子类,并且自动调用其 process() 方法,然后将添加了指定注解的所有代码元素作为参数传递给该方法,开发者在根据注解元素在编译期输出对应的 Java 代码。 \ No newline at end of file +Java API 已经提供了扫描源码并解析注解的框架,开发者可以通过继承 AbstractProcessor 类来实现自己的注解处理逻辑。APT 的原理是在注解了某些代码元素(如字段、函数、类等)后,在编译时编译器会检查 AbstractProcessor 的子类,并且自动调用其 process() 方法,然后将添加了指定注解的所有代码元素作为参数传递给该方法,开发者在根据注解元素在编译期输出对应的 Java 代码。 + +##### AbstractProcessor + +实现一个注解处理器,需要继承 AbstractProcessor ,如下: + +```java +public class BindViewProcessor extends AbstractProcessor { + + private Elements mElementsUtils; + private Types mTypesUtils; + private Filter mFilter; + private Messager mMessager; + + /** + * 初始化方法 + * 可以初始化一些给注解处理器使用的工具类 + */ + @Override + public synchronized void init(ProcessingEnvironment processingEnvironment) { + super.init(processingEnvironment); + mElementsUtils = processingEnvironment.getElementUtils(); + } + + /** + * 指定目标注解对象 + */ + @Override + public Set getSupportedAnnotationTypes() { + Set hashSet = new HashSet<>(); + hashSet.add(BindView.class.getCanonicalName()); + return hashSet; + } + + /** + * 指定使用的 Java 版本 + */ + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + /** + * 处理注解 + */ + @Override + public boolean process(Set set, RoundEnvironment roundEnvironment) { + //... + return true; + } +} +``` + +对于 APT,其实主要是有很多 API 不熟悉。 + +Elements:用于处理程序元素的工具类; + +Types:用于处理类型数据的工具类; + +Filter:用于给注解处理器创建文件; + +Messager:用于给注解处理器报告错误、警告、提示等信息。 + +##### Element 元素相关 + +注解处理器工具扫描 Java 源文件,源文件中的每一部分都是程序中的 Element 元素,如包、类、方法、字段等。例如源代码中的类声明信息代表 TypeElement 类型元素,方法声明信息代表 ExecutableElement 类型元素,有了这些结构,就能完整的表示整个源代码信息了。 + +Element 元素分为以下类型: + +1. ExcecutableElement + + 可执行元素,包括类或接口的方法、构造方法或初始化程序。 + +2. PackageElement + + 包元素,提供对有关包及其成员的信息的访问。 + +3. TypeElement + + 类或接口元素,提供对有关类型及其成员的信息的访问。 + +4. TypeParameterElement + + 表示一般类、接口、方法或构造方法元素的形式类型参数,类型参数声明一个 TypeVariable + +5. VariableElement + + 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。 + +#### AutoServcie 注册注解处理器 + +以前要注册注解处理器要在 module 的 META_INFO 目录新建 services 目录,并创建一个名为 Java.annotation.processing.Processor 的文件,然后在文件中写入要注册的注解处理器的全民。 + +后来 Google 推出了 AutoService 注解库来实现注册注解处理器的注册,通过在注解处理器上加上 @AutoService(Processor.class) 注解,即可在编译时生成 META_INFO 信息。 + +```java +@AutoService(Processor.class) +public class BindViewProcessor extends AbstractProcessor { +} +``` + +#### JavaPoet 生成 Java 代码 + +JavaPoet 中有几个常用的类: + +MethodSpec:代表一个构造方法或方法声明; + +TypeSpec:代表一个类、接口、或者枚举声明; + +FieldSpec:代表一个成员变量、字段声明; + +JavaFile:包含一个顶级类的 Java 文件; + +关于它的使用,直接看官方文档即可: + +[https://github.com/square/javapoet](https://github.com/square/javapoet) + +#### ButterKnife 的实现 + +分为四步: + +1. 定义注解 +2. 注解处理器处理注解 +3. 生成 Java 文件 +4. 引入 + +##### 定义注解 + + + +#### 参考 + +[教你实现一个轻量级的注解处理器 APT](https://mp.weixin.qq.com/s/3zrAzOUGpovRRbuYnce3uw) + +[拆 JakeWharton 系列之 ButterKnife](https://juejin.im/post/58f388d1da2f60005d369a09) + +[ButterKnife原理分析(二)注解的处理](https://www.jianshu.com/p/bcddc376c0ef) + + + diff --git a/blogs/Android/Hook.md b/blogs/Android/Hook.md new file mode 100644 index 0000000..f342afb --- /dev/null +++ b/blogs/Android/Hook.md @@ -0,0 +1,32 @@ +--- +Hook +--- + +#### 目录 + +1. 思维导图 +2. 概述 +3. 实现方式 +4. 参考 + +#### 思维导图 + +#### 概述 + +Hook 就是勾子的意思,之前我以为 Hook 技术是很难理解的呢,没想到真正去了解的时候是那么简单。 + +一般 Hook 的实现方式是通过反射和代理实现,只能 Hook 当前的应用程序进程。通过 Hook 框架来实现,比如 Xposed,可以实现全局 Hook,但是需要 root。 + +#### 实现方式 + +##### 代理实现 + +代理分为静态代理和动态代理,这里就不过多阐述了,详见: + +[工厂模式](https://github.com/Omooo/Android-Notes/blob/master/blogs/DesignMode/%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F.md) + +Hook View 的 onClick 事件我记得在写埋点的时候写过,这里就不再多说了,现在来 Hook startActivity 的实现: + + + +#### 参考 \ No newline at end of file diff --git a/blogs/Android/Socket 编程.md b/blogs/Android/Socket 编程.md new file mode 100644 index 0000000..890eae1 --- /dev/null +++ b/blogs/Android/Socket 编程.md @@ -0,0 +1,20 @@ +--- +Socket 编程基础 +--- + +#### 目录 + +1. 思维导图 +2. 概述 +3. UDP Socket +4. TCP Socket +5. 参考 + +#### 概述 + +Sccket 中文意思是插座的意思,专业术语称之为套接字,它把 TCP/IP 封装成了调用接口供开发者调用,也就是说通过调用 Socket 相关 API 来实现网络通讯。在 Java 中也存在 Socket 相关 API,主要分为两个,分别是基于 UDP 传输协议的 Socket 和基于 TCP 传输协议的 Socket。 + +#### UDP Socket + +UDP 是无连接的,只要提供对方的 IP 地址和端口号就能进行数据的传输,其中 IP 负责定位主机端口负责定位应用。知道了目标 IP 和目标端口号通过 Java 中的 UDP Socket 就能进行 I/O 传输。 + diff --git a/blogs/Java/UncaughtExceptionHandler.md b/blogs/Java/UncaughtExceptionHandler.md new file mode 100644 index 0000000..19949c9 --- /dev/null +++ b/blogs/Java/UncaughtExceptionHandler.md @@ -0,0 +1,101 @@ +--- +UncaughtExceptionHandler +--- + +#### 目录 + +1. 思维导图 +2. 概述 +3. 源码分析 + - Thread#UncaughtExceptionHandler + - ThreadGroup + - 总结 +4. 参考 + +#### 思维导图 + +#### 概述 + +在虚拟机中,当一个线程如果没有显式处理(即 try catch)异常而抛出时会将该异常事件报告给该线程对象的 UncaughtExceptionHandler 进行处理,如果线程没有设置 UncaughtExceptionHandler,则默认会把异常栈信息输出到终端而使程序直接崩溃。所以如果我们想在线程意外崩溃时做一些处理就可以通过实现 UncaughtExceptionHandler 来满足需求。 + +#### 源码分析 + +##### Thread#UncaughtExceptionHandler + +```java +public class Thread{ + public interface UncaughtExceptionHandler { + void uncaughtException(Thread t, Throwable e); + } + + private volatile UncaughtExceptionHandler uncaughtExceptionHandler; + + private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; + + /** + * 静态方法,设置一个全局的异常处理器 + */ + public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + defaultUncaughtExceptionHandler = eh; + } + + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){ + return defaultUncaughtExceptionHandler; + } + + /** + * 注意,如果 uncaughtExceptionHandler 为空就返回 group + * 这里的 group 其实是 ThreadGroup + */ + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler != null ? + uncaughtExceptionHandler : group; + } + + /** + * 对指定的线程设置异常处理器 + */ + public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + checkAccess(); + uncaughtExceptionHandler = eh; + } +} +``` + +看到这就能明白了 setDefaultUncaughtExceptionHandler 和 setUncaughtExceptionHandler 的区别的。线程崩溃时异常抛出的顺序是先调用 Thread 的 getUncaughtExceptionHandler 查看自己是否有自己对象特有的 handler,如果有就直接处理,没有就调用 group(即 ThreadGroup)的逻辑处理。 + +##### ThreadGroup + +```java +public class ThreadGroup implements Thread.UncaughtExceptionHandler { + //... + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + //通常情况下,parent 为 null + parent.uncaughtException(t, e); + } else { + Thread.UncaughtExceptionHandler ueh = + Thread.getDefaultUncaughtExceptionHandler(); + if (ueh != null) { + ueh.uncaughtException(t, e); + } else if (!(e instanceof ThreadDeath)) { + System.err.print("Exception in thread \"" + + t.getName() + "\" "); + e.printStackTrace(System.err); + } + } + } +} +``` + +这里就清楚了,上面返回的 group 其实就是 ThreadGroup,所以它肯定也是实现 UncaughtExceptionHandler 接口的,它 uncaughtException 的处理逻辑就是先拿全局的 UncaughtExceptionHandler,如果为空就按正常流程抛出异常堆栈信息。 + +##### 总结 + +1. UncaughtExceptionHandler 是一个接口,我们可以通过实现它来处理程序中未被 try-cache 的异常,它有全局异常处理和针对某个线程的异常处理之分 +2. 异常在抛出时,会先检查当前线程是否设置了 UncaughtExceptionHandler,如果有就直接处理,如果没有就跳到 ThreadGroup 中处理,ThreadGroup 也实现了 UncaughtExceptionHandler 接口,它的 uncaughtException 的实现逻辑是判断是否有全局异常处理器,如果有就处理,没有就抛出异常堆栈信息,我们平时的异常信息就是从这里抛出的。 + +#### 参考 + +[UncaughtExceptionHandler 相关问题解析](https://mp.weixin.qq.com/s/ghnNQnpou6-NemhFjpl4Jg) +