add UncaughtExceptionHandler

master
Omooo 6 years ago
parent 0b78dc28ae
commit c2bf7f960d
  1. 152
      blogs/Android/APT.md
  2. 32
      blogs/Android/Hook.md
  3. 20
      blogs/Android/Socket 编程.md
  4. 101
      blogs/Java/UncaughtExceptionHandler.md

@ -1,18 +1,164 @@
--- ---
APT APT、JavaPoet 实现 ButterKnife
--- ---
#### 目录 #### 目录
1. 思维导图 1. 思维导图
2. 概述 2. 概述
3. ButterKnife 的实现 3. APT
4. 参考 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 等。 APT(Annotation Processing Tool)即注解处理器,是一种注解处理工具,用来在编译器扫描和处理注解,通过注解来生成 Java 文件。即以注解作为桥梁,通过预先规定好的代码生成规则来自动生成 Java 文件。此类注解框架的代表有 ButterKnife、Dagger2、EventBus 等。
Java API 已经提供了扫描源码并解析注解的框架,开发者可以通过继承 AbstractProcessor 类来实现自己的注解处理逻辑。APT 的原理是在注解了某些代码元素(如字段、函数、类等)后,在编译时编译器会检查 AbstractProcessor 的子类,并且自动调用其 process() 方法,然后将添加了指定注解的所有代码元素作为参数传递给该方法,开发者在根据注解元素在编译期输出对应的 Java 代码。 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<String> getSupportedAnnotationTypes() {
Set<String> hashSet = new HashSet<>();
hashSet.add(BindView.class.getCanonicalName());
return hashSet;
}
/**
* 指定使用的 Java 版本
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 处理注解
*/
@Override
public boolean process(Set<? extends TypeElement> 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)

@ -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 的实现:
#### 参考

@ -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 传输。

@ -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)
Loading…
Cancel
Save