## 自定義 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

每個Transform其實都是一個gradle task,Android編譯器中的TaskManager將每個Transform串連起來,第一個Transform接收來自javac編譯的結果,以及已經拉取到在本地的第三方依賴(jar. aar),還有resource資源,注意,這里的resource并非android項目中的res資源,而是asset目錄下的資源。這些編譯的中間產物,在Transform組成的鏈條上流動,每個Transform節點可以對class進行處理再傳遞給下一個Transform。我們常見的混淆,Desugar等邏輯,它們的實現如今都是封裝在一個個Transform中,而我們自定義的Transform,會插入到這個Transform鏈條的最前面。

### 實踐
#### 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文件的復制


自定義
## 應用案例
- 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 性能優化
- 數據跨平臺