Skip to content

Commit be5412e

Browse files
author
1170762202@qq.com
committed
自定义Gradle插件 + ASM 实现无埋点监控
1 parent a6c7f00 commit be5412e

26 files changed

+671
-2
lines changed

.idea/compiler.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/jarRepositories.xml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
apply plugin: 'com.android.application'
22
apply plugin: 'android-aspectjx'
33
apply plugin: 'com.jakewharton.butterknife'
4+
apply plugin: 'com.zlx.plugin_lifecycle'
45

56

67
def keystorePropertiesFile = rootProject.file("keystore.properties")

app/src/main/java/com/zlx/sharelive/MyApp.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import com.zlx.module_base.BaseApplication;
44
import com.zlx.module_base.config.ModuleLifecycleConfig;
55

6+
import org.greenrobot.eventbus.EventBus;
7+
import org.greenrobot.eventbus.Subscribe;
8+
import org.greenrobot.eventbus.ThreadMode;
9+
610

711
/**
812
* @date: 2019\3\8 0008
@@ -17,9 +21,15 @@ public class MyApp extends BaseApplication {
1721
public void onCreate() {
1822
super.onCreate();
1923
ModuleLifecycleConfig.getInstance().initModuleAhead(this);
24+
EventBus.getDefault().register(this);
2025
}
2126

2227

28+
@Subscribe(threadMode = ThreadMode.MAIN)
29+
public void onMessageEvent(Integer event) {
30+
System.out.println("收到消息" + event.toString());
31+
}
2332

33+
;
2434

2535
}

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ buildscript {
55
repositories {
66
google()
77
jcenter()
8-
8+
maven {
9+
url uri('./plugin-lifecycle/plugin-lifecycle')
10+
}
911
}
1012
dependencies {
1113
classpath "com.android.tools.build:gradle:4.0.2"
1214
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.2'
1315
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
16+
classpath 'com.zlx.plugin_lifecycle:plugin-lifecycle:0.0.1'
1417
}
1518
}
1619

local.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## This file must *NOT* be checked into Version Control Systems,
2+
# as it contains information specific to your local configuration.
3+
#
4+
# Location of the SDK. This is only used by Gradle.
5+
# For customization when using a Version Control System, please read the
6+
# header note.
7+
#Thu Sep 10 14:41:54 CST 2020
8+
sdk.dir=D\:\\sdk\\Android

plugin-lifecycle/build.gradle

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apply plugin: 'groovy'
2+
apply plugin: 'maven'
3+
dependencies {
4+
//gradle sdk
5+
implementation gradleApi()
6+
//groovy sdk
7+
implementation localGroovy()
8+
implementation 'com.android.tools.build:gradle:4.0.2'
9+
10+
}
11+
12+
13+
repositories {
14+
mavenCentral()
15+
jcenter()
16+
}
17+
//group和version在后面使用自定义插件的时候会用到
18+
group='com.zlx.plugin_lifecycle'
19+
version='0.0.1'
20+
21+
22+
uploadArchives {
23+
repositories {
24+
mavenDeployer {
25+
//提交到远程服务器:
26+
// repository(url: "http://www.xxx.com/repos") {
27+
// authentication(userName: "admin", password: "admin")
28+
// }
29+
//本地的Maven地址:当前工程下
30+
repository(url: uri('./plugin-lifecycle'))
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3311e9e9a64a4bd57ea08f7f5bb2751a
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
674fcd05682dd733cbf06626b6f4983dfed910e0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>com.zlx.plugin_lifecycle</groupId>
6+
<artifactId>plugin-lifecycle</artifactId>
7+
<version>0.0.1</version>
8+
<dependencies>
9+
<dependency>
10+
<groupId>com.android.tools.build</groupId>
11+
<artifactId>gradle</artifactId>
12+
<version>4.0.2</version>
13+
<scope>runtime</scope>
14+
</dependency>
15+
</dependencies>
16+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
51381e0efaf3e0daa4acb0fe9a8ff218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
f3eea6d9164361fef3395c6d3e116019c78a6b38
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<metadata>
3+
<groupId>com.zlx.plugin_lifecycle</groupId>
4+
<artifactId>plugin-lifecycle</artifactId>
5+
<versioning>
6+
<release>0.0.1</release>
7+
<versions>
8+
<version>0.0.1</version>
9+
</versions>
10+
<lastUpdated>20210329090202</lastUpdated>
11+
</versioning>
12+
</metadata>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
e60846202f41c8a105ba9b8c088d4167
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ffbf6b927ad697cba2562508336ce27505708975
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package com.zlx.plugin_lifecycle
2+
3+
import com.android.annotations.NonNull
4+
import com.android.build.api.transform.*
5+
import com.android.build.gradle.AppExtension
6+
import com.android.build.gradle.internal.pipeline.TransformManager
7+
import com.zlx.plugin_lifecycle.LifecycleClassVisitor
8+
import org.apache.commons.codec.digest.DigestUtils
9+
import org.apache.commons.io.FileUtils
10+
import org.apache.commons.io.IOUtils
11+
import org.gradle.api.Plugin
12+
import org.gradle.api.Project
13+
import org.objectweb.asm.ClassReader
14+
import org.objectweb.asm.ClassVisitor
15+
import org.objectweb.asm.ClassWriter
16+
17+
import java.util.jar.JarEntry
18+
import java.util.jar.JarFile
19+
import java.util.jar.JarOutputStream
20+
import java.util.zip.ZipEntry
21+
22+
import static org.objectweb.asm.ClassReader.EXPAND_FRAMES
23+
24+
class LifecyclePlugin extends Transform implements Plugin<Project> {
25+
26+
@Override
27+
void apply(Project project) {
28+
//registerTransform
29+
def android = project.extensions.getByType(AppExtension)
30+
android.registerTransform(this)
31+
}
32+
33+
@Override
34+
String getName() {
35+
return "LifecyclePlugin"
36+
}
37+
38+
@Override
39+
Set<QualifiedContent.ContentType> getInputTypes() {
40+
return TransformManager.CONTENT_CLASS
41+
}
42+
43+
@Override
44+
Set<? super QualifiedContent.Scope> getScopes() {
45+
return TransformManager.SCOPE_FULL_PROJECT
46+
}
47+
48+
@Override
49+
boolean isIncremental() {
50+
return false
51+
}
52+
53+
@Override
54+
void transform(@NonNull TransformInvocation transformInvocation) {
55+
println '--------------- LifecyclePlugin visit start --------------- '
56+
def startTime = System.currentTimeMillis()
57+
Collection<TransformInput> inputs = transformInvocation.inputs
58+
TransformOutputProvider outputProvider = transformInvocation.outputProvider
59+
//删除之前的输出
60+
if (outputProvider != null)
61+
outputProvider.deleteAll()
62+
//遍历inputs
63+
inputs.each { TransformInput input ->
64+
//遍历directoryInputs
65+
input.directoryInputs.each { DirectoryInput directoryInput ->
66+
//处理directoryInputs
67+
handleDirectoryInput(directoryInput, outputProvider)
68+
}
69+
70+
//遍历jarInputs
71+
input.jarInputs.each { JarInput jarInput ->
72+
//处理jarInputs
73+
handleJarInputs(jarInput, outputProvider)
74+
}
75+
}
76+
def cost = (System.currentTimeMillis() - startTime) / 1000
77+
println '--------------- LifecyclePlugin visit end --------------- '
78+
println "LifecyclePlugin cost : $cost s"
79+
}
80+
81+
/**
82+
* 处理文件目录下的class文件
83+
*/
84+
static void handleDirectoryInput(DirectoryInput directoryInput, TransformOutputProvider outputProvider) {
85+
//是否是目录
86+
if (directoryInput.file.isDirectory()) {
87+
//列出目录所有文件(包含子文件夹,子文件夹内文件)
88+
directoryInput.file.eachFileRecurse { File file ->
89+
def name = file.name
90+
if (name.endsWith(".class") && !name.startsWith("R\$")
91+
&& !"R.class".equals(name) && !"BuildConfig.class".equals(name)
92+
&& "androidx/fragment/app/FragmentActivity.class".equals(name)) {
93+
println '----------- deal with "class" file <' + name + '> -----------'
94+
ClassReader classReader = new ClassReader(file.bytes)
95+
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
96+
ClassVisitor cv = new LifecycleClassVisitor(classWriter)
97+
classReader.accept(cv, EXPAND_FRAMES)
98+
byte[] code = classWriter.toByteArray()
99+
FileOutputStream fos = new FileOutputStream(
100+
file.parentFile.absolutePath + File.separator + name)
101+
fos.write(code)
102+
fos.close()
103+
}
104+
}
105+
}
106+
//处理完输入文件之后,要把输出给下一个任务
107+
def dest = outputProvider.getContentLocation(directoryInput.name,
108+
directoryInput.contentTypes, directoryInput.scopes,
109+
Format.DIRECTORY)
110+
FileUtils.copyDirectory(directoryInput.file, dest)
111+
}
112+
113+
/**
114+
* 处理Jar中的class文件
115+
*/
116+
static void handleJarInputs(JarInput jarInput, TransformOutputProvider outputProvider) {
117+
if (jarInput.file.getAbsolutePath().endsWith(".jar")) {
118+
//重名名输出文件,因为可能同名,会覆盖
119+
def jarName = jarInput.name
120+
def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
121+
if (jarName.endsWith(".jar")) {
122+
jarName = jarName.substring(0, jarName.length() - 4)
123+
}
124+
JarFile jarFile = new JarFile(jarInput.file)
125+
Enumeration enumeration = jarFile.entries()
126+
File tmpFile = new File(jarInput.file.getParent() + File.separator + "classes_temp.jar")
127+
//避免上次的缓存被重复插入
128+
if (tmpFile.exists()) {
129+
tmpFile.delete()
130+
}
131+
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(tmpFile))
132+
//用于保存
133+
while (enumeration.hasMoreElements()) {
134+
JarEntry jarEntry = (JarEntry) enumeration.nextElement()
135+
String entryName = jarEntry.getName()
136+
ZipEntry zipEntry = new ZipEntry(entryName)
137+
InputStream inputStream = jarFile.getInputStream(jarEntry)
138+
139+
//插桩class
140+
if (entryName.endsWith(".class") && !entryName.startsWith("R\$")
141+
&& !"R.class".equals(entryName) && !"BuildConfig.class".equals(entryName)
142+
&& ("androidx/fragment/app/FragmentActivity.class".equals(entryName) ||
143+
"androidx/fragment/app/Fragment.class".equals(entryName) ||
144+
"com/zlx/module_home/fragment/HomeFg.class".equals(entryName))) {
145+
//class文件处理
146+
println '----------- deal with "jar" class file <' + entryName + '> -----------'
147+
jarOutputStream.putNextEntry(zipEntry)
148+
ClassReader classReader = new ClassReader(IOUtils.toByteArray(inputStream))
149+
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
150+
ClassVisitor cv = new LifecycleClassVisitor(classWriter)
151+
classReader.accept(cv, EXPAND_FRAMES)
152+
byte[] code = classWriter.toByteArray()
153+
jarOutputStream.write(code)
154+
} else {
155+
jarOutputStream.putNextEntry(zipEntry)
156+
jarOutputStream.write(IOUtils.toByteArray(inputStream))
157+
}
158+
jarOutputStream.closeEntry()
159+
}
160+
//结束
161+
jarOutputStream.close()
162+
jarFile.close()
163+
def dest = outputProvider.getContentLocation(jarName + md5Name,
164+
jarInput.contentTypes, jarInput.scopes, Format.JAR)
165+
FileUtils.copyFile(tmpFile, dest)
166+
tmpFile.delete()
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)