parent
							
								
									0b78dc28ae
								
							
						
					
					
						commit
						c2bf7f960d
					
				| @ -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 代码。 | ||||
| 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…
					
					
				
		Reference in new issue