## 自定义 gradle 插件
### 推荐阅读
[Gradle自定义插件以及发布方法](https://www.jianshu.com/p/d1d7fd48ff0b)
### 简介
自定义插件基于源码放置可以分为3种:
1. Build script:源码放置在模块内的 build.gradle 中
2. buildSrc project:源码放置在 rootProjectDir/buildSrc/src/main/groovy目录内(也就是工程根目录下创建 buildSrc 目录)
3. Standalone project:使用单独的一个工程/模块创建我们的 [Gradle](https://gradle.org/guides/) 插件,这种方法会构建和发表一个JAR文件,可以提供给多工程构建和其他开发者共同使用
### 实践
~~~
public class SimpleGradlePlugin implements Plugin<Project> {
void apply(Project project) {
note()
//create an extension object:Whyn,so others can config via Whyn
project.extensions.create("simpleGradle", SimpleGradleExtension)
project.task('simpleGradle'){
group = "test"
description = "gradle Standalone project demo,shares everywhere"
doLast{
println '**************************************'
println "$project.simpleGradle.description"
println '**************************************'
}
}
}
private void note(){
println '------------------------'
println 'apply StandAlonePlugin'
println '------------------------'
}
}
class SimpleGradleExtension {
String description = 'default description'
}
~~~
## Transform
### 推荐阅读
[官网](http://tools.android.com/tech-docs/new-build-system/transform-api)
[Gradle自定义插件以及发布方法](https://www.jianshu.com/p/d1d7fd48ff0b)
### 简介
Gradle Transform是Android官方提供给开发者在项目构建阶段即由class到dex转换期间修改class文件的一套api
![](https://img.kancloud.cn/15/b4/15b4aace81af312b66ce4b3ac5028f44_900x1090.png)
每个Transform其实都是一个gradle task,Android编译器中的TaskManager将每个Transform串连起来,第一个Transform接收来自javac编译的结果,以及已经拉取到在本地的第三方依赖(jar. aar),还有resource资源,注意,这里的resource并非android项目中的res资源,而是asset目录下的资源。这些编译的中间产物,在Transform组成的链条上流动,每个Transform节点可以对class进行处理再传递给下一个Transform。我们常见的混淆,Desugar等逻辑,它们的实现如今都是封装在一个个Transform中,而我们自定义的Transform,会插入到这个Transform链条的最前面。
![](https://img.kancloud.cn/39/e3/39e3a5eb43adacef93fcd888687fd457_1020x634.png)
### 实践
#### 1 引入下面这个依赖
~~~
compile 'com.android.tools.build:transform-api:1.5.0'
~~~
#### 2 自定义的Transform
~~~
//Transform 就是把输入的 .class 文件转变成目标字节码文件。
public class AutoTransform extends Transform {
Project project
public AutoTransform(Project project) {
this.project = project
}
@Override
String getName() {
return "AutoTrack"
}
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
// 要处理的数据类型
// TransformManager.CONTENT_CLASS 编译后的字节码,有可能是jar也有可能是目录
// TransformManager.CONTENT_RESOURCES 标准java资源
return TransformManager.CONTENT_CLASS
}
@Override
Set<? super QualifiedContent.Scope> getScopes() {
// 作用域
// Scope.PROJECT 只处理当前项目
// Scope.SUB_PROJECTS 只处理子项目
// Scope.PROJECT_LOCAL_DEPS 只处理当前项目本地依赖 jar aar
// Scope.SUB_PROJECTS_LOCAL_DEPS 只处理子项目本地依赖 jar aar
// Scope.EXTERNAL_LIBRARIES 只处理外部的依赖
return TransformManager.SCOPE_FULL_PROJECT
}
@Override
boolean isIncremental() {
// 是否增量构建?
return true
}
// TransformInput 是指这些输入文件的抽象。它包括两部分:
// 1)DirectoryInput 集合
// 是指以源码方式参与项目编译的所有目录结构及其目录下 的源码文件。
// 2)JarInput 集合
// 是指以 jar 包方式参与项目编译的所有本地 jar 包和远程 jar 包。
//
// TransformOutputProvider是指 Transform 的输出,通过它可以获取输出路径。
@Override
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
Collection<TransformInput> inputs = transformInvocation.inputs
TransformOutputProvider outputProvider = transformInvocation.outputProvider
printlnJarAndDir(inputs)
inputs.each { TransformInput input ->
input.directoryInputs.each { DirectoryInput directoryInput ->
handleDirectoryInput(directoryInput, outputProvider)
}
input.jarInputs.each { JarInput jarInput ->
//处理jarInputs
handleJarInputs(jarInput, outputProvider)
}
}
}
}
~~~
#### 3 自定义的Transform
~~~
class SimpleTransformPlugin implements Plugin<Project> {
void apply(Project project) {
def android = project.extensions.getByType(AppExtension)
android.registerTransform(new AutoTransform(project))
}
}
~~~
## ASM
### 推荐阅读
[官网](https://asm.ow2.io/)
[一起玩转Android项目中的字节码](http://quinnchen.cn/2018/09/13/2018-09-13-asm-transform/)
[Android】函数插桩(Gradle + ASM)](https://www.jianshu.com/p/16ed4d233fd1)
### 原理和简介
JVM平台上,处理字节码的框架最常见的就三个,ASM,Javasist,AspectJ。最推荐选择ASM,因为使用它可以更底层地处理字节码的每条命令,处理速度、内存占用,也优于其他两个框架。
ASM处理涉及三个类 ClassReader、ClassWriter、ClassVisitor
~~~
static void handleDirectoryInput(DirectoryInput directoryInput, TransformOutputProvider outputProvider, MethodIdFactory factory) {
//是否是目录
if (directoryInput.file.isDirectory()) {
//列出目录所有文件(包含子文件夹,子文件夹内文件)
directoryInput.file.eachFileRecurse { File file ->
def name = file.name
if (name.endsWith(".class") && !name.startsWith("R\$")
&& !"R.class".equals(name) && !"BuildConfig.class".equals(name) && !name.contains("Manifest") ) {
// ClassReader负责读取class字节码
ClassReader classReader = new ClassReader(file.bytes)
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
//代理自己的ClassVisitor
ClassVisitor cv = new MethodStatClassVisitor(classWriter, factory)
//真正触发这个逻辑就是通过ClassWriter的accept方式。
classReader.accept(cv, ClassReader.EXPAND_FRAMES)
//我们通过ClassWriter的toByteArray(),将从ClassReader传递到ClassWriter的字节码导出,写入新的文件即可
byte[] code = classWriter.toByteArray()
FileOutputStream fos = new FileOutputStream(
file.parentFile.absolutePath + File.separator + name)
fos.write(code)
fos.close()
}
}
}
//处理完输入文件之后,要把输出给下一个任务
def dest = outputProvider.getContentLocation(directoryInput.name,
directoryInput.contentTypes, directoryInput.scopes,
Format.DIRECTORY)
FileUtils.copyDirectory(directoryInput.file, dest)
}
~~~
ClassReader负责读取class字节码,将字节码的每个细节按顺序通过接口的方式,传递给然后ClassReader通过一个ClassVisitor接口,将字节码的每个细节按顺序通过接口的方式,传递给ClassVisitor(你会发现ClassVisitor中有多个visitXXXX接口),这个过程就像ClassReader带着ClassVisitor游览了class字节码的每一个指令。
ClassWriter我们可以看它源码,其实就是一个ClassVisitor的实现。
ClassWriter的toByteArray(),将从ClassReader传递到ClassWriter的字节码导出,写入新的文件即可。这就完成了class文件的复制
![](https://img.kancloud.cn/3b/a0/3ba0ea6fcd0b0a0eb9cd5291549a29b3_900x524.png)
![](https://img.kancloud.cn/f0/21/f02130f5864a1a117a276d451058973d_900x524.png)
自定义
## 应用案例
- Android
- 四大组件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介绍
- MessageQueue详细
- 启动流程
- 系统启动流程
- 应用启动流程
- Activity启动流程
- View
- view绘制
- view事件传递
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大数据
- Binder小结
- Android组件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 迁移与修复
- Sqlite内核
- Sqlite优化v2
- sqlite索引
- sqlite之wal
- sqlite之锁机制
- 网络
- 基础
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP进化图
- HTTP小结
- 实践
- 网络优化
- Json
- ProtoBuffer
- 断点续传
- 性能
- 卡顿
- 卡顿监控
- ANR
- ANR监控
- 内存
- 内存问题与优化
- 图片内存优化
- 线下内存监控
- 线上内存监控
- 启动优化
- 死锁监控
- 崩溃监控
- 包体积优化
- UI渲染优化
- UI常规优化
- I/O监控
- 电量监控
- 第三方框架
- 网络框架
- Volley
- Okhttp
- 网络框架n问
- OkHttp原理N问
- 设计模式
- EventBus
- Rxjava
- 图片
- ImageWoker
- Gilde的优化
- APT
- 依赖注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 协程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 运行期Java-hook技术
- 编译期hook
- ASM
- Transform增量编译
- 运行期Native-hook技术
- 热修复
- 插件化
- AAB
- Shadow
- 虚拟机
- 其他
- UI自动化
- JavaParser
- Android Line
- 编译
- 疑难杂症
- Android11滑动异常
- 方案
- 工业化
- 模块化
- 隐私合规
- 动态化
- 项目管理
- 业务启动优化
- 业务架构设计
- 性能优化case
- 性能优化-排查思路
- 性能优化-现有方案
- 登录
- 搜索
- C++
- NDK入门
- 跨平台
- H5
- Flutter
- Flutter 性能优化
- 数据跨平台