├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── src
│ ├── huawei
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── strings.xml
│ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── androidplugin
│ │ │ ├── ASMByte.java
│ │ │ ├── MainActivity.java
│ │ │ └── TestActivity.java
│ │ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_test.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
└── version.xml
├── build.gradle
├── buildSrc
├── .gitignore
├── build.gradle
├── copyTask.txt
└── src
│ └── main
│ ├── copyTaskOk.txt
│ ├── groovy
│ └── com
│ │ └── wj
│ │ └── global
│ │ ├── gradle
│ │ ├── GlobalGradleProject.groovy
│ │ ├── PrintKotlin.kt
│ │ └── TemplateTask.java
│ │ ├── log
│ │ └── SystemOutPrint.java
│ │ └── task
│ │ └── TemplateOutTask.java
│ ├── resources
│ └── META-INF
│ │ └── gradle-plugins
│ │ └── global.gradle.properties
│ └── synctest
│ ├── copyTask.txt
│ └── syncexist.txt
├── firstplugin
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── groovy
│ └── com
│ │ └── wj
│ │ └── plugin
│ │ ├── FirstPluginProject.groovy
│ │ ├── SystemOutPrint.java
│ │ ├── extension
│ │ ├── AndroidExtension.java
│ │ ├── TemplateSettingExtension.java
│ │ └── TemplateSettingExtensionInProject.java
│ │ ├── task
│ │ ├── HandleTemplateTask.java
│ │ └── HandleTemplateTaskInProject.java
│ │ └── transform
│ │ └── HotTransform.java
│ └── resources
│ └── META-INF
│ └── gradle-plugins
│ └── com.wj.firstplugin.properties
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
├── manifestplugin
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── groovy
│ └── com
│ │ └── wj
│ │ └── manifest
│ │ ├── ManifestExtension.groovy
│ │ ├── ManifestProject.groovy
│ │ ├── task
│ │ ├── AddExportForPackageManifestTask.groovy
│ │ └── SetLatestVersionForMergedManifestTask.groovy
│ │ └── utils
│ │ └── SystemPrint.java
│ ├── kotlin
│ └── com
│ │ └── wj
│ │ └── manifest
│ │ ├── ManifestExtensionByKotlin.kt
│ │ ├── ManifestProjectByKotlin.kt
│ │ └── SystemPrintByKotlin.kt
│ └── resources
│ └── META-INF
│ └── gradle-plugins
│ └── com.wj.plugin.manifest.properties
├── plugins
└── com
│ ├── android
│ └── wj
│ │ └── plugins
│ │ └── DebugPlugin
│ │ ├── 1.0.0
│ │ ├── DebugPlugin-1.0.0.jar
│ │ ├── DebugPlugin-1.0.0.jar.md5
│ │ ├── DebugPlugin-1.0.0.jar.sha1
│ │ ├── DebugPlugin-1.0.0.pom
│ │ ├── DebugPlugin-1.0.0.pom.md5
│ │ └── DebugPlugin-1.0.0.pom.sha1
│ │ ├── maven-metadata.xml
│ │ ├── maven-metadata.xml.md5
│ │ └── maven-metadata.xml.sha1
│ └── wj
│ └── plugin
│ ├── firstplugin
│ ├── 1.0.0
│ │ ├── firstplugin-1.0.0-classifiter.jar
│ │ ├── firstplugin-1.0.0-classifiter.jar.md5
│ │ ├── firstplugin-1.0.0-classifiter.jar.sha1
│ │ ├── firstplugin-1.0.0.jar
│ │ ├── firstplugin-1.0.0.jar.md5
│ │ ├── firstplugin-1.0.0.jar.sha1
│ │ ├── firstplugin-1.0.0.pom
│ │ ├── firstplugin-1.0.0.pom.md5
│ │ └── firstplugin-1.0.0.pom.sha1
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ └── maven-metadata.xml.sha1
│ └── manifestplugin
│ ├── 1.0.0
│ ├── manifestplugin-1.0.0.jar
│ ├── manifestplugin-1.0.0.jar.md5
│ ├── manifestplugin-1.0.0.jar.sha1
│ ├── manifestplugin-1.0.0.pom
│ ├── manifestplugin-1.0.0.pom.md5
│ └── manifestplugin-1.0.0.pom.sha1
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ └── maven-metadata.xml.sha1
├── settings.gradle
├── testcopy.txt
└── wjplugin
├── .gitignore
├── build.gradle
└── src
└── main
├── groovy
└── com
│ └── android
│ └── wj
│ └── debug
│ ├── WjDebugPluginProject.groovy
│ ├── autologvisitor
│ ├── AutoLogAdviceAdapter.java
│ ├── AutoLogClassVisitor.java
│ └── AutoLogFieldVistor.java
│ ├── autotransform
│ └── AutoLogTransform.java
│ └── utils
│ └── SystemOutPrintln.java
└── resources
└── META-INF
└── gradle-plugins
└── com.android.wj.debug.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.idea/*
3 | /.gradle/*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidPlugin
2 | 用来学习Android Gradle Plugin
3 |
4 | https://github.com/wenjing-bonnie/GradlePlugin这个工程里面相对更好些。
5 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | //Mobpush sdk验证 'com.wj.plugin.manifest'可以为AndroidManifest添加android:export属性
4 | //TODO id 'com.mob.sdk'
5 | //TODO 暂时隐藏学习的时候创建的第一个插件,使用wjplugin添加一个有具体功能的插件
6 | //id 'com.wj.firstplugin'
7 | //id 'global.gradle'
8 | //TODO 暂时隐藏字节码插件 id 'com.android.wj.debug'
9 | id 'com.wj.plugin.manifest'
10 |
11 | }
12 | ///**'com.wj.plugin.manifest'*/
13 | ManifestPlugin {
14 | versionFile = file("version.xml")
15 | }
16 |
17 | android {
18 | compileSdkVersion 31
19 | buildToolsVersion "30.0.3"
20 |
21 | defaultConfig {
22 | applicationId "com.android.androidplugin"
23 | minSdkVersion 23
24 | targetSdkVersion 31
25 | versionCode 1
26 | versionName "1.0"
27 |
28 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29 |
30 | multiDexEnabled true
31 | }
32 |
33 | buildTypes {
34 | release {
35 | minifyEnabled false
36 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
37 | }
38 | }
39 | //特色产品:同一套代码生产出不同特色的产品
40 | flavorDimensions "channel"
41 | productFlavors {
42 |
43 | huawei {
44 | manifestPlaceholders = [CHANNEL: "huawei"]
45 | dimension = "channel"
46 | buildConfigField("String", "CHANNEL", "\"huawei\"")
47 | }
48 |
49 | oppo {
50 | manifestPlaceholders = [CHANNEL: "oppo"]
51 | dimension = "channel"
52 | buildConfigField("String", "CHANNEL", "\"oppo\"")
53 | }
54 |
55 | xiaomi {
56 | manifestPlaceholders = [CHANNEL: "xiaomi"]
57 | dimension = "channel"
58 | buildConfigField("String", "CHANNEL", "\"xiaomi\"")
59 | }
60 | }
61 | }
62 |
63 | def prefix = "@@@@@@@@@@@@@@ app @@@@@@@@@@@@@ "
64 |
65 |
66 | /*this.afterEvaluate {
67 | //当前variant的类型为application类型
68 | this.android.applicationVariants.all { ApplicationVariant variant ->
69 | println(prefix + "name = " + variant.name)
70 | //得到每个variant中的task,然后进行对Task进行hook
71 | variant.getAssembleProvider().get().doFirst {
72 | println(prefix + " assemble task do first ")
73 | }
74 | //修改每个variant的输出的apk的名字
75 | variant.outputs.all { // output ->
76 | println(prefix + "name = " + outputFileName)
77 | String newApk = "app-${variant.baseName}-${variant.versionName}.apk"
78 | println(prefix + "newApkName = " + newApk)
79 | //outputFileName = newApk
80 | }
81 |
82 | }
83 | }*/
84 |
85 | //自定义Task
86 | task firstTask {
87 | //创建一个Action,添加到Action列表的头部
88 | doFirst {
89 | println(prefix + "firstTask do first")
90 | }
91 | // println(prefix + "firstTask ")
92 | //创建一个Action,添加到Action列表到尾部
93 | // doLast {
94 | // println(prefix + "firstTask do last")
95 | // }
96 | }
97 |
98 | task("secondTask") {
99 | // println(prefix + "secondTask by (string) ")
100 | }
101 |
102 | task(thirdTask) {
103 | // println(prefix + "thirdTask by () ")
104 | }
105 |
106 | //可以设置thirdTask是否执行
107 | //thirdTask.enabled true
108 |
109 | //方法一:在该project配置完成之后,在preBuild之前添加这些自定义的Task
110 | //this.afterEvaluate {
111 | // getTasks().matching {
112 | // it.name.equals("preBuild")
113 | // }.each {
114 | // it.dependsOn(firstTask, secondTask, thirdTask)
115 | // }
116 | //}
117 | //方法二:直接从tasks的找到preBuild这个task即可
118 | tasks.preBuild.dependsOn(firstTask, secondTask, thirdTask)
119 |
120 | //TODO 暂时隐藏学习的时候创建的第一个插件,使用wjplugin添加一个有具体功能的插件
121 | //自定义firstplugin插件中的输入属性的设置
122 | /*templateSettingExtension {
123 | compileSdk = "1.0.0"
124 | interfaceSourceDir = file("src/main/java/mvp")
125 | }
126 | templateSettingExtensionInProject {
127 | compileSdk = "1.0.0"
128 | interfaceSourceDir = file("src/main/java/mvp")
129 | }
130 |
131 | //自定义firstplugin插件多层闭包的属性扩展
132 | androidExtension {
133 | compileSdkVersion = "1.0.0"
134 | buildToolsVersion = "29.0.0"
135 |
136 | defaultConfig {
137 | applicationId = "1.0.0"
138 | //minSdkVersion = "3.0.0"
139 | }
140 |
141 | buildTypes {
142 | dev {
143 | //A problem occurred evaluating project ':app'.
144 | //> No signature of method: build_dwrb2icq269nomqcc24dytwa1.androidExtension() is applicable for argument types: (build_dwrb2icq269nomqcc24dytwa1$_run_closure7) values: [build_dwrb2icq269nomqcc24dytwa1$_run_closure7@4ddf3c7]
145 | //signingConfig false
146 | signingConfig = true
147 | }
148 |
149 | }
150 | }*/
151 |
152 | configurations {
153 | firstConfiguration {
154 | description("This is the first configuration")
155 |
156 | }
157 | }
158 | dependencies {
159 |
160 | implementation 'androidx.appcompat:appcompat:1.2.0'
161 | implementation 'com.google.android.material:material:1.2.1'
162 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
163 | testImplementation 'junit:junit:4.+'
164 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
165 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
166 |
167 | firstConfiguration 'androidx.appcompat:appcompat:1.2.0'
168 | }
169 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/huawei/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/huawei/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/huawei/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/huawei/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/huawei/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/huawei/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/huawei/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/huawei/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/huawei/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/huawei/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/huawei/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/huawei/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/huawei/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HuaweiPlugin
3 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/androidplugin/ASMByte.java:
--------------------------------------------------------------------------------
1 | package com.android.androidplugin;
2 |
3 | import android.util.Log;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by wenjing.liu on 2021/8/6 in J1.
8 | *
9 | * 用来学习字节码的测试文件
10 | *
11 | * @author wenjing.liu
12 | */
13 | public class ASMByte implements View.OnClickListener {
14 | private int a = 10;
15 | private static int c = 20;
16 | private String name;
17 |
18 | private int sum(int aa, int bb) {
19 | long beginTime = System.currentTimeMillis();
20 | System.out.println("Other running code");
21 | long callTime = System.currentTimeMillis() - beginTime;
22 | Log.d("AUTO", String.format("cost time is [%d]ms", callTime));
23 | return aa + bb;
24 | }
25 |
26 | @Override
27 | public void onClick(View v) {
28 |
29 | // Log.d(getClass().getSimpleName(), String.format("%s cost time is [%l]ms", name, callTime));
30 |
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/androidplugin/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.android.androidplugin;
2 |
3 | import android.app.Activity;
4 | import android.content.pm.ApplicationInfo;
5 | import android.content.pm.PackageManager;
6 | import android.graphics.Color;
7 | import android.os.Bundle;
8 | import android.util.Log;
9 | import android.view.View;
10 | import android.widget.TextView;
11 |
12 | import java.util.HashMap;
13 |
14 | public class MainActivity extends Activity {
15 | private TextView tvInfo;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_main);
21 | tvInfo = findViewById(R.id.tv_info);
22 | setInfoText("The channel is " + getChannelFromAndroidManifest() + " , " + BuildConfig.CHANNEL, null);
23 | }
24 |
25 | private void setInfoText(String info, int[] nums) {
26 | tvInfo.setText(info);
27 | }
28 |
29 | public void btnOnClick(View view) {
30 | tvInfo.setTextColor(Color.RED);
31 | }
32 |
33 | /**
34 | * 获取meta-data中的channel
35 | *
36 | * @return
37 | */
38 | private String getChannelFromAndroidManifest() {
39 | String metaChannel = "";
40 | try {
41 | ApplicationInfo info = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
42 | if (info == null || info.metaData == null) {
43 | return metaChannel;
44 | }
45 | metaChannel = info.metaData.getString("channel");
46 | } catch (PackageManager.NameNotFoundException e) {
47 | }
48 | return metaChannel;
49 | }
50 |
51 |
52 | private int sum(int a, int b) {
53 | Log.d(this.getClass().getSimpleName(),""+a);
54 | return a + b;
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/android/androidplugin/TestActivity.java:
--------------------------------------------------------------------------------
1 | package com.android.androidplugin;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class TestActivity extends Activity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_test);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidPlugin
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/version.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 新增购物车
5 | 12
6 | 2.0.0
7 | 2021/09/16
8 |
9 |
10 | APP第一版本上线
11 | 12
12 | 1.0.0
13 | 2021/09/15
14 |
15 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlinVersion = '1.4.10'
4 | ext.gradleVersion = "4.2.2"
5 | repositories {
6 | google()
7 | mavenCentral()
8 | maven {
9 | //maven本地仓库
10 | url uri('plugins')
11 | }
12 | maven {
13 | //验证适配Android12 mobpush
14 | url uri("https://mvn.mob.com/android")
15 | }
16 | }
17 | dependencies {
18 | classpath "com.android.tools.build:gradle:$gradleVersion"
19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
20 | // NOTE: Do not place your application dependencies here; they belong
21 | // // in the individual module build.gradle files
22 | classpath "com.wj.plugin:firstplugin:1.0.0"
23 | classpath "com.android.wj.plugins:debugplugin:1.0.0"
24 | classpath "com.wj.plugin:manifestplugin:1.0.0"
25 | // 注册MobSDK
26 | classpath 'com.mob.sdk:MobSDK:+'
27 | }
28 | }
29 |
30 | allprojects {
31 | repositories {
32 | google()
33 | mavenCentral()
34 | //jcenter() // Warning: this repository is going to shut down soon
35 | }
36 | }
37 | def prefix = "**********root build************* "
38 | //android Studio自动添加的就可以执行
39 | task clean(type: Delete) {
40 | delete rootProject.buildDir
41 | doLast {
42 | println(prefix + "Android Studio auto add clean task do last")
43 | }
44 | doFirst {
45 | println(prefix + "Android Studio auto add clean task do first")
46 | }
47 | }
48 | //添加的测试Task
49 | // 为什么而按照相同的方式复制的Task却不能执行呢?并且也不在该taskGraph中
50 | task copy(type: Copy) {
51 | from 'testcopy.txt'
52 | into 'app/'
53 | doLast {
54 | println(prefix + "test copy task do last")
55 | }
56 | }
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.gradle/*
--------------------------------------------------------------------------------
/buildSrc/build.gradle:
--------------------------------------------------------------------------------
1 | //apply plugin: 'groovy'
2 | plugins {
3 | id 'groovy'
4 | // id 'org.jetbrains.kotlin.jvm' version '1.3.72'
5 | }
6 | def prefix = "~~~~~~GlobalGradleProject~~~~~~~~"
7 |
8 | dependencies {
9 | //gradle sdk
10 | implementation gradleApi()
11 | //groovy sdk
12 | implementation localGroovy()
13 | //kotlin
14 | //implementation "org.jetbrains.kotlin:kotlin-stdlib"
15 | }
16 |
17 | repositories {
18 | mavenCentral()
19 | }
20 | //TODO 这种方式不能引用这个自定义的TemplateTask,import的时候都不能导入包,暂时不知道什么原因
21 | //TODO add on 2021-06-17 by wenjing.liu
22 | //task templateTaskInBuild(type: com.wj.global.gradle.TemplateTask) {
23 | // doFirst {
24 | // println(prefix + " template do first ========")
25 | // }
26 | //}
27 |
28 | task syncTask(type: Sync) {
29 | from 'copyTask.txt'
30 | into 'src/main/synctest'
31 | preserve {
32 | //保持该文件不被覆盖
33 | exclude 'src/main/synctest/syncexist.txt'
34 | }
35 | doLast {
36 | println(prefix + " syncTask do last")
37 | }
38 | }
39 |
40 | task copyTask(type: Copy) {
41 | from 'copyTask.txt'
42 | into 'src/main'
43 | doLast {
44 | println(prefix + " Task of copy do last")
45 | }
46 | rename {
47 | //重命名
48 | String fileName ->
49 | fileName = "copyTaskOk.txt"
50 | }
51 | }
52 |
53 | this.afterEvaluate {
54 | tasks.compileJava.dependsOn(syncTask,copyTask)
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/buildSrc/copyTask.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/buildSrc/copyTask.txt
--------------------------------------------------------------------------------
/buildSrc/src/main/copyTaskOk.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/buildSrc/src/main/copyTaskOk.txt
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/com/wj/global/gradle/GlobalGradleProject.groovy:
--------------------------------------------------------------------------------
1 | package com.wj.global.gradle
2 |
3 | import com.wj.global.log.SystemOutPrint
4 | import org.gradle.api.Plugin
5 | import org.gradle.api.Project
6 |
7 | /**
8 | * Project相当于build.gradle
9 | * @author wenjing.liu
10 | */
11 | class GlobalGradleProject implements Plugin {
12 |
13 | @Override
14 | void apply(Project project) {
15 | SystemOutPrint.print("Apply the Global Gradle Project " + project.name)
16 | addTemplateTask(project)
17 | }
18 |
19 | void addTemplateTask(Project project) {
20 | TemplateTask templateTask = project.getTasks().create("templateTask", TemplateTask)
21 |
22 | project.afterEvaluate {
23 | it.name.equals("preBuild")
24 | }.each {
25 | it.dependsOn(templateTask)
26 | }
27 | }
28 |
29 |
30 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/com/wj/global/gradle/PrintKotlin.kt:
--------------------------------------------------------------------------------
1 | package com.wj.global.gradle
2 |
3 | /**
4 | * Created by wenjing.liu on 2021/6/9 in J1.
5 | *
6 | * 输出日志
7 | *
8 | * @author wenjing.liu
9 | */
10 | class PrintKotlin {
11 | companion object {
12 | @JvmStatic
13 | fun print(info: String) {
14 | print("~~~~~~~~ " + info)
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/com/wj/global/gradle/TemplateTask.java:
--------------------------------------------------------------------------------
1 | package com.wj.global.gradle;
2 |
3 | import com.wj.global.log.SystemOutPrint;
4 |
5 | import org.gradle.api.DefaultTask;
6 | import org.gradle.api.tasks.Input;
7 | import org.gradle.api.tasks.InputFile;
8 | import org.gradle.api.tasks.Optional;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import java.io.File;
12 |
13 | /**
14 | * Created by wenjing.liu on 2021/6/9 in J1.
15 | *
16 | * 一个Task
17 | * TODO 1.暂时发现自定义的Task只能放到该目录下,其他的module中才可以引用该Task:
18 | * TODO 即 com.wj.global.task.TemplateOutTask就不能被app这个module下build.gradle通过"task taskActionTask(type: TemplateOutTask)"来使用该Task
19 | * TODO 2.该TemplateTask并且不能放到当前build.gradle文件下通过"task taskActionTask(type: TemplateTask)"来对该Task进行增强
20 | * TODO 暂时不知道原因add on 2021-06-17 by wenjing.liu
21 | *
22 | * TODO 然而放到一个非buildSrc下发现:
23 | * TODO 上述1和2的问题都不存在
24 | *
25 | * @author wenjing.liu
26 | */
27 | public class TemplateTask extends DefaultTask {
28 | private String compileSdk;
29 | private File inputFile;
30 | //private TemplateEngine
31 |
32 | @Input
33 | @Optional
34 | public String getCompileSdk() {
35 | return compileSdk;
36 | }
37 |
38 | @InputFile
39 | @Optional
40 | public File getInputFile() {
41 | return inputFile;
42 | }
43 |
44 | @TaskAction
45 | public void taskAction() {
46 | SystemOutPrint.print("task action in Default Task");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/com/wj/global/log/SystemOutPrint.java:
--------------------------------------------------------------------------------
1 | package com.wj.global.log;
2 |
3 | /**
4 | * Created by wenjing.liu on 2021/6/9 in J1.
5 | *
6 | * 日志输出
7 | *
8 | * @author wenjing.liu
9 | */
10 | public class SystemOutPrint {
11 | private final static boolean DEBUG = true;
12 |
13 | public static void print(String info) {
14 | if (DEBUG) {
15 | System.out.println("~~~~~~GlobalGradleProject~~~~~~~~ " + info);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/com/wj/global/task/TemplateOutTask.java:
--------------------------------------------------------------------------------
1 | package com.wj.global.task;
2 |
3 | import com.wj.global.log.SystemOutPrint;
4 |
5 | import org.gradle.api.DefaultTask;
6 | import org.gradle.api.tasks.Input;
7 | import org.gradle.api.tasks.InputFile;
8 | import org.gradle.api.tasks.TaskAction;
9 |
10 | import java.io.File;
11 |
12 | /**
13 | * Created by wenjing.liu on 2021/6/9 in J1.
14 | *
15 | * 一个Task
16 | * TODO 不能被app这个module下build.gradle通过"task taskActionTask(type: TemplateOutTask)"来使用该Task
17 | * TODO 必须放到gradle下才可以在app这个module下build.gradle使用
18 | *
19 | * @author wenjing.liu
20 | */
21 | public class TemplateOutTask extends DefaultTask {
22 | private String compileSdk;
23 | private File inputFile;
24 | //private TemplateEngine
25 |
26 | @Input
27 | public String getCompileSdk() {
28 | SystemOutPrint.print(" compileSdk = " + compileSdk);
29 | return compileSdk;
30 | }
31 |
32 | @InputFile
33 | public File getInputFile() {
34 | SystemOutPrint.print(" inputFile = " + inputFile.getPath());
35 | return inputFile;
36 | }
37 |
38 |
39 | @InputFile
40 | public void taskInput(File file) {
41 | this.getInputs().file(file);
42 | }
43 |
44 | @TaskAction
45 | public void taskAction() {
46 | SystemOutPrint.print("task action in Default Task");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/buildSrc/src/main/resources/META-INF/gradle-plugins/global.gradle.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.wj.global.gradle.GlobalGradleProject
--------------------------------------------------------------------------------
/buildSrc/src/main/synctest/copyTask.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/buildSrc/src/main/synctest/copyTask.txt
--------------------------------------------------------------------------------
/buildSrc/src/main/synctest/syncexist.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/buildSrc/src/main/synctest/syncexist.txt
--------------------------------------------------------------------------------
/firstplugin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/firstplugin/build.gradle:
--------------------------------------------------------------------------------
1 | import com.wj.plugin.task.HandleTemplateTaskInProject
2 | import com.wj.plugin.extension.TemplateSettingExtensionInProject
3 | import com.wj.plugin.SystemOutPrint
4 |
5 | apply plugin: 'groovy'
6 | apply plugin: 'maven'
7 | /** 第一部分:项目依赖 */
8 | dependencies {
9 | //gradle sdk
10 | implementation gradleApi()
11 | //groovy sdk
12 | implementation localGroovy()
13 |
14 | implementation 'com.android.tools.build:gradle:4.2.1'
15 | }
16 |
17 | repositories {
18 | mavenCentral()
19 | }
20 |
21 | /**第二部分:项目自定义实现具体功能的Task*/
22 |
23 | /**(1)添加 TemplateSettingExtension
24 | * 如果这里单纯的通过getExtensions().create来进行属性扩展,那么只能在该build.gradle中使用
25 | * 解决方案:
26 | * 方案一:如何找到哪个module apply了该插件:通过循环找到使用了该插件的module见createTemplateSettingExtensionInProjectForAllProject()来获取该扩展属性
27 | * 但是这种方案的缺陷在于:
28 | * 在该build.gradle才为被依赖module增加属性扩展,而被依赖module已经afterEvaluate。
29 | * 或者换种说法:在构建被依赖module的build.gradle的时候,会找不到该属性扩展,抛出 Could not find method templateSettingExtensionInProject() for arguments
30 | * 被依赖module先afterEvaluate,而在该build.gradle才增加
31 | * 解决方案:
32 | * 目前看下来只能在FirstPluginProject中增加属性扩展,但是可通过getTemplateSettingExtensionInProject()
33 | * 方案二:将root project下所有的project都添加该属性扩展
34 | * 缺陷同方案一
35 | * */
36 |
37 | /**(2)将功能的Task添加到app这个project的任务队列中*/
38 | //若增加HandleTemplateTaskInProject出现编译不通过的时候,需要删除plugins/里面的内容之后,重新编译该项目然后uploadArchives,最后在将该依赖添加到root project中
39 | task handleTemplateTaskInProject(type: HandleTemplateTaskInProject) {
40 | }
41 |
42 |
43 | /**将自定义的的Task添加到依赖关系图中,必须保证该Task的input和output已经设置并且还设置类型一致,
44 | * 否则会抛出"No value has been specified for property"
45 | * 遇到这种情况就是将root project中的com.wj.firstplugin的依赖去掉之后,重新编译项目,成功之后重新打包上传,
46 | * 然后在root project在引入该插件
47 | * */
48 | this.afterEvaluate {
49 |
50 | //必须设置input,否则会抛出"No value has been specified for property"
51 | String path = "/Users/j1/Documents/android/code/AndroidPlugin/firstplugin/src/main/groovy/com/wj/plugin/extension"
52 | TemplateSettingExtensionInProject extension = getTemplateSettingExtensionInProject()
53 | if (extension != null) {
54 | handleTemplateTaskInProject.setFileSourceDir(extension.interfaceSourceDir)
55 | }
56 | handleTemplateTaskInProject.setFileFormat(".java")
57 | tasks.compileJava.dependsOn(handleTemplateTaskInProject)
58 | }
59 | /**
60 | * 通过循环找到使用了该插件的module
61 | */
62 | void createTemplateSettingExtensionInProjectForAllProject() {
63 | Set allProjectsInRoot = getRootProject().getAllprojects()
64 | for (Project project : allProjectsInRoot) {
65 | List plugins = project.getPlugins().toList()
66 | for (Plugin plugin : plugins) {
67 | //找到含有该插件的被依赖的project,添加该属性扩展
68 | if (project.getPlugins().findPlugin(com.wj.plugin.FirstPluginProject)) {
69 | project.getExtensions().create(TemplateSettingExtensionInProject.TAG, TemplateSettingExtensionInProject)
70 | return
71 | }
72 | }
73 | }
74 | }
75 | /**
76 | * 找到设置了属性扩展的module中的属性扩展
77 | * @return
78 | */
79 | TemplateSettingExtensionInProject getTemplateSettingExtensionInProject() {
80 | Set allProjectsInRoot = getRootProject().getAllprojects()
81 | for (Project project : allProjectsInRoot) {
82 | TemplateSettingExtensionInProject extension = project.getExtensions().findByName(TemplateSettingExtensionInProject.TAG)
83 | if (extension != null) {
84 | return extension
85 | }
86 | }
87 | return null
88 | }
89 |
90 |
91 | /**第三部分:下面是将项目打包给到其他module使用*/
92 |
93 | /**(方法一):打包成Jar包的方式 */
94 | def marvenjar = 'mavenjar'
95 |
96 | /**默认名字是 [archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]*/
97 | task makeJar(type: org.gradle.jvm.tasks.Jar) {
98 | //删除之前创建的文件夹
99 | delete marvenjar
100 | archiveBaseName = "firstplugin"
101 | archiveAppendix = "appendix"
102 | //为什么不起作用呢??
103 | archiveVersion = '1.0.0'
104 | archiveClassifier = 'classifiter'
105 | //扩展名,默认的为jar
106 | //archiveExtension = "extension"
107 | doLast {
108 | SystemOutPrint.println(" The task of make jar is finished !!!")
109 | }
110 | }
111 |
112 | artifacts {
113 | //将firstPluginJar看做artifact,交给archives管理
114 | archives makeJar
115 | }
116 |
117 | uploadArchives {
118 | repositories {
119 | maven {
120 | //在build.gradle的同级目录下生成mavenjar文件夹
121 | url "file:" + marvenjar
122 | }
123 | }
124 | }
125 |
126 | /**=== (方法二): 生成maven依赖库 */
127 | uploadArchives {
128 | repositories {
129 | //delete '../plugins'
130 | mavenDeployer {
131 | //配置自定义插件的classpath
132 | pom.groupId = 'com.wj.plugin'
133 | pom.artifactId = 'firstplugin'
134 | pom.version = '1.0.0'
135 |
136 | //提交到远程服务器
137 | // repository(url:"服务器地址"){
138 | // authentication(userName:'admin',password:'admin')
139 | // }
140 |
141 | //本地maven
142 | repository(url: uri('../plugins'))
143 | }
144 | }
145 | }
146 |
147 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/FirstPluginProject.groovy:
--------------------------------------------------------------------------------
1 | package com.wj.plugin
2 |
3 | import com.android.build.gradle.AppExtension
4 | import com.android.build.gradle.BaseExtension
5 | import com.wj.plugin.extension.AndroidExtension
6 | import com.wj.plugin.extension.TemplateSettingExtension
7 | import com.wj.plugin.extension.TemplateSettingExtensionInProject
8 | import com.wj.plugin.task.HandleTemplateTask
9 | import com.wj.plugin.transform.HotTransform
10 | import org.gradle.api.Plugin
11 | import org.gradle.api.Project
12 | import org.gradle.api.Task
13 |
14 | /**
15 | *
16 | * Project 相当于一个build.gradle文件
17 | * 当把该插件通过plugins{}放入到项目中的build.gradle中的时候:
18 | * 1)在Configure project :app 会首先执行该文件里面的内容,直到把plugins{}里面的插件的Plugin都执行完毕
19 | * 2)然后在Configure project :firstplugin 每一个插件的build.gradle,当该插件的build.gradle配置结束回调自身的afterEvaluate{},最后回调到整个项目工程的settings.gradle
20 | *
21 | * 所以在apply(Project project) 中的project返回的是app(通过apply或plugins{}添加该插件的工程),
22 | * 所以通过下面的方式添加的属性扩展和自定义Task都是添加到app这个module上面
23 | * @author wenjing.liu
24 | */
25 |
26 | class FirstPluginProject implements Plugin {
27 |
28 | @Override
29 | void apply(Project project) {
30 | SystemOutPrint.println("Apply the First Plugin Project")
31 | /**(1)添加 TemplateSettingExtension
32 | * 因为添加到被依赖module的project中,所以该属性扩展只能在被依赖module中引用
33 | * */
34 | createExtensions(project)
35 | /**(2)将功能的Task添加到app这个project的任务队列中*/
36 | addHandleTemplateTask(project)
37 | /**为添加到build.gradle来增加扩展属性*/
38 | createExtensionsForInProject(project)
39 | //测试两层闭包
40 | createAndroidExtensions(project)
41 | //隐藏测试输出
42 | // testAndroidExtension(project)
43 |
44 | /**添加Transform*/
45 | SystemOutPrint.println("project.extensions all = "+project.extensions.findAll())
46 | //TODO 暂时隐藏该功能
47 | //project.extensions.findByType(BaseExtension.class).registerTransform(new HotTransform())
48 | }
49 |
50 | void createExtensionsForInProject(project) {
51 | project.getExtensions().create(TemplateSettingExtensionInProject.TAG, TemplateSettingExtensionInProject)
52 | }
53 |
54 |
55 | void createAndroidExtensions(Project project) {
56 | project.getExtensions().create(AndroidExtension.TAG, AndroidExtension, project)
57 | }
58 |
59 | /**
60 | * 创建Extension
61 | * @param project
62 | */
63 | TemplateSettingExtension createExtensions(Project project) {
64 | //在这里是无法取到 extension的值,因为此时还没有构建到app中的build.gradle
65 | project.getExtensions().create(TemplateSettingExtension.TAG, TemplateSettingExtension)
66 | }
67 | /**
68 | * 将HandleTemplateTask加入到任务队列中
69 | * @param project
70 | */
71 | void addHandleTemplateTask(Project project) {
72 | Task task = project.getTasks().create("handleTemplateTask", HandleTemplateTask)
73 |
74 | //这里是返回的app的这个module,然后在app的project的所有tasks中添加该handleTemplateTask
75 | project.afterEvaluate {
76 | project.getTasks().matching {
77 | //如果将该插件放到'com.android.application',则在"preBuild"之前添加该Task
78 | it.name.equals("preBuild")
79 | }.each {
80 | it.dependsOn(task)
81 | setHandleTemplateTaskInputFromExtension(project, task)
82 | }
83 | }
84 | }
85 | /**
86 | * 设置HandleTemplateTask的input
87 | * @param project
88 | * @param task
89 | */
90 | void setHandleTemplateTaskInputFromExtension(Project project, HandleTemplateTask task) {
91 | //项目配置完成之后,就可以获得设置的Extension中的内容
92 | TemplateSettingExtension extension = project.getExtensions().findByName(TemplateSettingExtension.TAG)
93 | task.setFileFormat(".java")
94 | String path = project.getProjectDir().getAbsolutePath() + "/src/main/java/mvp"
95 | task.setFileSourceDir(extension.interfaceSourceDir)
96 | }
97 |
98 | void testAndroidExtension(Project project) {
99 | project.afterEvaluate {
100 | AndroidExtension extension = project.getExtensions().findByName(AndroidExtension.TAG)
101 | SystemOutPrint.println("compileSdkVersion = " + extension.compileSdkVersion)
102 | SystemOutPrint.println("applicationId = " + extension.defaultConfig.getApplicationId())
103 | SystemOutPrint.println("minSdkVersion = " + extension.defaultConfig.minSdkVersion)
104 | extension.buildTypes.each {
105 | SystemOutPrint.println("buildTypes = " + it.name + " , signingConfig = " + it.signingConfig)
106 |
107 | }
108 | }
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/SystemOutPrint.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin;
2 |
3 | /**
4 | * Created by wenjing.liu on 2021/6/9 in J1.
5 | *
6 | * 日志输出
7 | *
8 | * @author wenjing.liu
9 | */
10 | public class SystemOutPrint {
11 |
12 | private static final boolean DEBUG = true;
13 |
14 | public static void println(String info) {
15 | if (DEBUG) {
16 | System.out.println("%%%%%%%%% FirstPluginProject %%%%%%%%% " + info);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/extension/AndroidExtension.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin.extension;
2 |
3 |
4 | import org.gradle.api.Action;
5 | import org.gradle.api.NamedDomainObjectContainer;
6 | import org.gradle.api.Project;
7 |
8 | /**
9 | * Created by wenjing.liu on 2021/6/25 in J1.
10 | *
11 | * 仿android{}中的多层闭包的Extension
12 | *
13 | * @author wenjing.liu
14 | */
15 | public class AndroidExtension {
16 | public static final String TAG = "androidExtension";
17 | private String compileSdkVersion;
18 | private String buildToolsVersion;
19 | private DefaultConfig defaultConfig = new DefaultConfig();
20 | private NamedDomainObjectContainer buildTypes;
21 |
22 | public AndroidExtension(Project project) {
23 | buildTypes = project.container(BuildTypes.class);
24 | }
25 |
26 | public void setCompileSdkVersion(String version) {
27 | this.compileSdkVersion = version;
28 | }
29 |
30 | public String getCompileSdkVersion() {
31 | return this.compileSdkVersion;
32 | }
33 |
34 | public void setBuildToolsVersion(String version) {
35 | this.buildToolsVersion = version;
36 | }
37 |
38 | public String getBuildToolsVersion() {
39 | return this.buildToolsVersion;
40 | }
41 |
42 | /**
43 | * @param action 在build.gradle文件
44 | * defaultConfig{
45 | * it.applicationId = "1.0.0"
46 | * }
47 | */
48 | public void defaultConfig(Action action) {
49 | action.execute(defaultConfig);
50 | }
51 |
52 | // /**
53 | // * @param config 在build.gradle文件
54 | // * defaultConfig {
55 | // * applicationId = "1.0.0"
56 | // * minSdkVersion = "3.0.0"
57 | // * }
58 | // */
59 | // public void setDefaultConfig(Closure config) {
60 | // ConfigureUtil.configure(config, defaultConfig);
61 | // }
62 | public DefaultConfig getDefaultConfig() {
63 | return defaultConfig;
64 | }
65 |
66 | public void buildTypes(Action> action) {
67 | action.execute(buildTypes);
68 | }
69 |
70 | public NamedDomainObjectContainer getBuildTypes() {
71 | return buildTypes;
72 | }
73 |
74 | class DefaultConfig {
75 | private String applicationId;
76 | private String minSdkVersion;
77 |
78 | public void setApplicationId(String id) {
79 | this.applicationId = id;
80 | }
81 |
82 | public String getApplicationId() {
83 | return this.applicationId;
84 | }
85 |
86 | public void setMinSdkVersion(String sdk) {
87 | this.minSdkVersion = sdk;
88 | }
89 |
90 | public String getMinSdkVersion() {
91 | return this.minSdkVersion;
92 | }
93 | }
94 |
95 | /**
96 | * 必须为static类,否则会抛出"Class AndroidExtension.BuildTypes is a non-static inner class."
97 | */
98 | static class BuildTypes {
99 | private boolean signingConfig;
100 | //必须含有name属性,否则会抛出"'com.wj.plugin.extension.AndroidExtension$BuildTypes@130b3f7d' because it does not have a 'name' property"
101 | private String name;
102 |
103 | BuildTypes(String name) {
104 | this.name = name;
105 | }
106 |
107 | public String getName() {
108 | return name;
109 | }
110 |
111 | public void setSigningConfig(boolean config) {
112 | this.signingConfig = config;
113 | }
114 |
115 | public boolean getSigningConfig() {
116 | return this.signingConfig;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/extension/TemplateSettingExtension.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin.extension;
2 |
3 | import java.io.File;
4 | import java.util.Map;
5 |
6 | /**
7 | * Created by wenjing.liu on 2021/6/21 in J1.
8 | *
9 | * 用来配置项目的一些配置项:必须有对应的setter和getter方法,否则会提示read-only
10 | * 该类是对被依赖的module进行属性扩展
11 | *
12 | * @author wenjing.liu
13 | */
14 | public class TemplateSettingExtension {
15 | public static final String TAG = "templateSettingExtension";
16 |
17 | private String compileSdk;
18 | private File interfaceSourceDir;
19 |
20 | public String getCompileSdk() {
21 | return compileSdk;
22 | }
23 |
24 | public void setCompileSdk(String compileSdk) {
25 | this.compileSdk = compileSdk;
26 | }
27 |
28 | public File getInterfaceSourceDir() {
29 | return interfaceSourceDir;
30 | }
31 |
32 | public void setInterfaceSourceDir(File interfaceSourceDir) {
33 | this.interfaceSourceDir = interfaceSourceDir;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/extension/TemplateSettingExtensionInProject.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin.extension;
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * Created by wenjing.liu on 2021/6/24 in J1.
7 | *
8 | * 该类用来对本插件自身进行属性扩展,将该扩展添加到本插件的project中
9 | *
10 | * @author wenjing.liu
11 | */
12 | public class TemplateSettingExtensionInProject {
13 |
14 | public static final String TAG = "templateSettingExtensionInProject";
15 |
16 | private String compileSdk;
17 | private File interfaceSourceDir;
18 |
19 | public String getCompileSdk() {
20 | return compileSdk;
21 | }
22 |
23 | public void setCompileSdk(String compileSdk) {
24 | this.compileSdk = compileSdk;
25 | }
26 |
27 | public File getInterfaceSourceDir() {
28 | return interfaceSourceDir;
29 | }
30 |
31 | public void setInterfaceSourceDir(File interfaceSourceDir) {
32 | this.interfaceSourceDir = interfaceSourceDir;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/task/HandleTemplateTask.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin.task;
2 |
3 | import com.wj.plugin.SystemOutPrint;
4 |
5 | import org.apache.tools.ant.Task;
6 | import org.gradle.api.DefaultTask;
7 | import org.gradle.api.file.FileCollection;
8 | import org.gradle.api.tasks.Input;
9 | import org.gradle.api.tasks.InputDirectory;
10 | import org.gradle.api.tasks.InputFile;
11 | import org.gradle.api.tasks.InputFiles;
12 | import org.gradle.api.tasks.Optional;
13 | import org.gradle.api.tasks.TaskAction;
14 |
15 | import java.io.File;
16 |
17 | /**
18 | * Created by wenjing.liu on 2021/6/9 in J1.
19 | *
20 | * 一个Task:实现一个定义的接口类来实现对应的实现类的功能
21 | * 分析:
22 | * input:一个接口文件
23 | * output:一个实现类
24 | * action:实现具体的实现功能
25 | *
26 | * @author wenjing.liu
27 | */
28 | public class HandleTemplateTask extends DefaultTask {
29 | /**
30 | * 文件格式
31 | */
32 | private String fileFormat;
33 | /**
34 | * 文件的路径
35 | */
36 | private File fileSourceDir;
37 |
38 |
39 | @Input
40 | public String getFileFormat() {
41 | return fileFormat;
42 | }
43 |
44 | public void setFileFormat(String fileFormat) {
45 | this.fileFormat = fileFormat;
46 | }
47 |
48 | @InputDirectory
49 | //@Optional 可添加表示参数可选
50 | public File getFileSourceDir() {
51 | return fileSourceDir;
52 | }
53 |
54 | public void setFileSourceDir(File fileSourceDir) {
55 | this.fileSourceDir = fileSourceDir;
56 | }
57 |
58 | @TaskAction
59 | public void run() {
60 | SystemOutPrint.println(" HandleTemplateTask is running ");
61 | SystemOutPrint.println(" Set the file format is \" " + getFileFormat());
62 | SystemOutPrint.println(" Set the file source dir is \" " + getFileSourceDir());
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/task/HandleTemplateTaskInProject.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin.task;
2 |
3 | import com.wj.plugin.SystemOutPrint;
4 |
5 | import org.gradle.api.DefaultTask;
6 | import org.gradle.api.tasks.Input;
7 | import org.gradle.api.tasks.InputDirectory;
8 | import org.gradle.api.tasks.Optional;
9 | import org.gradle.api.tasks.TaskAction;
10 |
11 | import java.io.File;
12 |
13 | /**
14 | * Created by wenjing.liu on 2021/6/3 in J1.
15 | * 可在本插件module的build.gradle添加该Task
16 | *
17 | * @author wenjing.liu
18 | */
19 | public class HandleTemplateTaskInProject extends DefaultTask {
20 | /**
21 | * 文件格式
22 | */
23 | private String fileFormat;
24 | /**
25 | * 文件的路径
26 | */
27 | private File fileSourceDir;
28 |
29 |
30 | @Input
31 | public String getFileFormat() {
32 | return fileFormat;
33 | }
34 |
35 | public void setFileFormat(String fileFormat) {
36 | this.fileFormat = fileFormat;
37 | }
38 |
39 | @InputDirectory
40 | @Optional
41 | public File getFileSourceDir() {
42 | return fileSourceDir;
43 | }
44 |
45 | public void setFileSourceDir(File fileSourceDir) {
46 | this.fileSourceDir = fileSourceDir;
47 | }
48 |
49 | @TaskAction
50 | public void run() {
51 | SystemOutPrint.println(" HandleTemplateTaskInProject is running ");
52 | SystemOutPrint.println(" Set the file format is \" " + getFileFormat());
53 | SystemOutPrint.println(" Set the file source dir is \" " + getFileSourceDir());
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/firstplugin/src/main/groovy/com/wj/plugin/transform/HotTransform.java:
--------------------------------------------------------------------------------
1 | package com.wj.plugin.transform;
2 |
3 | import com.android.build.api.transform.DirectoryInput;
4 | import com.android.build.api.transform.Format;
5 | import com.android.build.api.transform.JarInput;
6 | import com.android.build.api.transform.QualifiedContent;
7 | import com.android.build.api.transform.Transform;
8 | import com.android.build.api.transform.TransformException;
9 | import com.android.build.api.transform.TransformInput;
10 | import com.android.build.api.transform.TransformInvocation;
11 | import com.android.build.api.transform.TransformOutputProvider;
12 | import com.android.build.gradle.internal.pipeline.TransformManager;
13 | import com.android.utils.FileUtils;
14 | import com.wj.plugin.SystemOutPrint;
15 |
16 | import java.io.File;
17 | import java.io.IOException;
18 | import java.util.Collection;
19 | import java.util.Set;
20 |
21 | /**
22 | * Created by wenjing.liu on 2021/7/6 in J1.
23 | * 学习有class到dex的Transform过程
24 | *
25 | * 每个Transform都是一个gradle task,将class、本地jar、aar和resource资源统一处理
26 | * 每个Transform在处理之后交给下一个Transform。如果是自定义的Transform会插在队列最前面
27 | *
28 | * @author wenjing.liu
29 | */
30 | public class HotTransform extends Transform {
31 |
32 | @Override
33 | public String getName() {
34 | return "HotTransformTask";
35 | }
36 |
37 | /**
38 | * 指定需要处理的数据.
39 | * CONTENT_CLASS表示处理的是java class文件
40 | * CONTENT_RESOURCES:处理的是java资源文件
41 | *
42 | * @return
43 | */
44 | @Override
45 | public Set getInputTypes() {
46 | return TransformManager.CONTENT_CLASS;
47 | }
48 |
49 | /**
50 | * 需要操作的内容范围
51 | *
52 | * @return
53 | */
54 | @Override
55 | public Set super QualifiedContent.Scope> getScopes() {
56 | //仅仅用来查看input文件
57 | //return TransformManager.EMPTY_SCOPES;
58 | return TransformManager.SCOPE_FULL_PROJECT;
59 | }
60 |
61 | /**
62 | * 仅仅用来设置查看input文件的作用域
63 | */
64 | // @Override
65 | // public Set super QualifiedContent.Scope> getReferencedScopes() {
66 | // return TransformManager.SCOPE_FULL_PROJECT;
67 | // }
68 |
69 | /**
70 | * 是否增量编译
71 | *
72 | * @return
73 | */
74 | @Override
75 | public boolean isIncremental() {
76 | return false;
77 | }
78 |
79 | /**
80 | * 输出内容
81 | *
82 | * @param transformInvocation
83 | * @throws TransformException
84 | * @throws InterruptedException
85 | * @throws IOException
86 | */
87 | @Override
88 | public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
89 | //如果不带super,就不会生成dex文件
90 | super.transform(transformInvocation);
91 |
92 | SystemOutPrint.println("context project name = " + transformInvocation.getContext().getProjectName()
93 | + "context project name = " + transformInvocation.getContext().getPath()
94 | + " , isIncremental = " + transformInvocation.isIncremental());
95 | //现在进行处理.class文件:消费型输入,需要输出给下一个任务
96 | Collection inputs = transformInvocation.getInputs();
97 | //仅仅用来查看input文件:引用型输入,无需输出,此时outputProvider为null
98 | //Collection inputs = transformInvocation.getReferencedInputs();
99 | TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
100 | for (TransformInput input : inputs) {
101 | //返回的是ImmutableJarInput。
102 | for (JarInput jar : input.getJarInputs()) {
103 | //SystemOutPrint.println("jar file = " + jar.getFile());
104 | //TODO 在这里增加处理.jar文件的代码
105 |
106 | //获取Transforms的输出目录
107 | File dest = outputProvider.getContentLocation(jar.getFile().getAbsolutePath(), jar.getContentTypes(), jar.getScopes(), Format.JAR);
108 | //将修改之后的文件拷贝到对应outputProvider的目录中
109 | FileUtils.copyFile(jar.getFile(), dest);
110 | }
111 | //返回的是ImmutableDirectoryInput
112 | for (DirectoryInput directory : input.getDirectoryInputs()) {
113 | // SystemOutPrint.println("directory file = " + directory.getFile());
114 | //TODO 在这里增加处理.class文件的代码
115 |
116 | //获取Transforms的输出目录
117 | File dest = outputProvider.getContentLocation(directory.getName(), directory.getContentTypes(), directory.getScopes(), Format.DIRECTORY);
118 | //将修改之后的文件拷贝到对应outputProvider的目录中
119 | FileUtils.copyDirectory(directory.getFile(), dest);
120 | }
121 | }
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/firstplugin/src/main/resources/META-INF/gradle-plugins/com.wj.firstplugin.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.wj.plugin.FirstPluginProject
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | #开启gradle缓存
19 | org.gradle.caching=true
20 | android.enableBuildCache=true
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 02 17:21:44 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
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 | #Tue Aug 03 11:16:03 CST 2021
8 | sdk.dir=/Users/j1/Documents/android/sdk-macosx
9 |
--------------------------------------------------------------------------------
/manifestplugin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/manifestplugin/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'groovy'
2 | apply plugin: 'maven'
3 | //<如果使用kotlin> Gradle 2.1版本之后,仅需要引入即可。
4 | apply plugin: 'org.jetbrains.kotlin.jvm'
5 | //<如果使用kotlin> Gradle 2.1版本之前需要两步:步骤一:引入插件
6 | //apply plugin: 'kotlin'
7 |
8 | dependencies {
9 | implementation gradleApi()
10 | implementation localGroovy()
11 | implementation "com.android.tools.build:gradle:4.2.2"
12 | //<如果使用kotlin> Gradle 2.1版本之前需要两步:步骤二:添加插件依赖
13 | // implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31"
14 | // implementation "org.jetbrains.kotlin:kotlin-stdlib"
15 | }
16 |
17 | repositories {
18 | mavenCentral()
19 | }
20 |
21 | uploadArchives {
22 | repositories {
23 | mavenDeployer {
24 | pom.groupId = "com.wj.plugin"
25 | pom.artifactId = "manifestplugin"
26 | pom.version = "1.0.0"
27 | repository(url: uri('../plugins'))
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/manifestplugin/src/main/groovy/com/wj/manifest/ManifestExtension.groovy:
--------------------------------------------------------------------------------
1 | package com.wj.manifest
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * Created by wenjing.liu on 2021/9/18 in J1.
7 | * 扩展属性
8 | * 然后再用来学习下kotlin语法
9 | * @author wenjing.liu
10 | */
11 | class ManifestExtension {
12 | protected static final String TAG = "ManifestPlugin"
13 | private File versionFile
14 |
15 | protected void setVersionFile(File file) {
16 | this.versionFile = file
17 | }
18 |
19 | protected File getVersionFile() {
20 | return versionFile
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/manifestplugin/src/main/groovy/com/wj/manifest/ManifestProject.groovy:
--------------------------------------------------------------------------------
1 | package com.wj.manifest
2 |
3 | import com.android.build.gradle.AppExtension
4 | import com.android.build.gradle.tasks.ProcessApplicationManifest
5 | import com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest
6 | import com.wj.manifest.task.AddExportForPackageManifestTask
7 | import com.wj.manifest.task.SetLatestVersionForMergedManifestTask
8 | import com.wj.manifest.utils.SystemPrint
9 | import org.gradle.api.Plugin
10 | import org.gradle.api.Project
11 |
12 | import java.util.regex.Matcher
13 | import java.util.regex.Pattern
14 |
15 | /**
16 | * 插件入口
17 | */
18 | class ManifestProject implements Plugin {
19 | String variantName
20 |
21 | @Override
22 | void apply(Project project) {
23 | //创建ManifestExtension
24 | createManifestExtension(project)
25 | //在sync中无法获取到variantName
26 | getVariantNameInBuild(project)
27 | SystemPrint.outPrintln(String.format("Welcome %s ManifestProject", variantName))
28 | //如果不是一个有效的variant,则直接返回
29 | if (!isValidVariantName()) {
30 | return
31 | }
32 | addTaskForVariantAfterEvaluate(project)
33 | }
34 |
35 | /**
36 | * 配置扩展属性
37 | * @param project
38 | */
39 | void createManifestExtension(Project project) {
40 | project.getExtensions().create(ManifestExtension.TAG, ManifestExtension)
41 | }
42 |
43 | /**
44 | * 在项目配置完成之后添加task
45 | * @param project
46 | */
47 | void addTaskForVariantAfterEvaluate(Project project) {
48 | //初始化 AddExportForPackageManifestTask
49 | AddExportForPackageManifestTask addExportTask = project.getTasks().create(AddExportForPackageManifestTask.TAG,
50 | AddExportForPackageManifestTask)
51 | //初始化 SetLastVersionInfoTask
52 | SetLatestVersionForMergedManifestTask versionTask = project.getTasks().create(SetLatestVersionForMergedManifestTask.TAG, SetLatestVersionForMergedManifestTask)
53 | //在项目配置完成后,添加自定义Task
54 | project.afterEvaluate {
55 | //为当前变体的task都加入到这个任务队列中。
56 | //所以通过project.getTasks().each {}去匹配每个task的startsWith&&endsWith的逻辑是一致的
57 | //并且这种性能会更高
58 | //直接通过task的名字找到ProcessApplicationManifest这个task
59 | addExportTaskForPackageManifest(project, addExportTask)
60 | addVersionTaskForMergedManifest(project, versionTask)
61 | }
62 | }
63 |
64 | /**
65 | * 为所有依赖的包的AndroidManifest添加android:exported
66 | * processHuaweiDebugMainManifest:合并所有依赖包以及主module中的AndroidManifest文件
67 | * processDebugManifest:为所有变体生成最终AndroidManifest文件
68 | * 不能使用ProcessDebugManifest.因为processHuaweiDebugMainManifest执行的时候就报错,还未执行到ProcessDebugManifest
69 | * @param project
70 | */
71 | void addExportTaskForPackageManifest(Project project, AddExportForPackageManifestTask beforeAddTask) {
72 | //找到processHuaweiDebugMainManifest,在这个之前添加export
73 | ProcessApplicationManifest processManifestTask = project.getTasks().getByName(String.format("process%sMainManifest", variantName))
74 | beforeAddTask.setManifestsFileCollection(processManifestTask.getManifests())
75 | beforeAddTask.setMainManifestFile(processManifestTask.getMainManifest().get())
76 | processManifestTask.dependsOn(beforeAddTask)
77 | }
78 |
79 | /**
80 | * 添加处理版本信息的Task
81 | * @param project
82 | */
83 | void addVersionTaskForMergedManifest(Project project, SetLatestVersionForMergedManifestTask versionTask) {
84 | //在项目配置完成后,添加自定义Task
85 | //方案一:直接通过task的名字找到ProcessMultiApkApplicationManifest这个task
86 | //直接找到ProcessDebugManifest,然后在执行后之后执行该Task
87 | ProcessMultiApkApplicationManifest processManifestTask = project.getTasks().getByName(String.format("process%sManifest", variantName))
88 | versionTask.setManifestFile(processManifestTask.getMainMergedManifest().asFile.get())
89 | processManifestTask.finalizedBy(versionTask)
90 | }
91 |
92 |
93 | /**
94 | * 获取当前变体名
95 | * (1)在执行build任务的时候,
96 | * project.gradle.getStartParameter().getTaskRequests()返回的内容:[DefaultTaskExecutionRequest{args=[:wjplugin:assemble, :wjplugin:testClasses, :manifestplugin:assemble, :manifestplugin:testClasses, :firstplugin:assemble, :firstplugin:testClasses, :app:assembleHuaweiDebug],projectPath='null'}]
97 | * 可从该字符串中截取当前的variant,然后在该变体基础上创建各个task.
98 | * (2)在执行sync任务的时候,
99 | * project.gradle.getStartParameter().getTaskRequests()返回的内容:[DefaultTaskExecutionRequest{args=[],projectPath='null'}]
100 | * 解决方案:通过project.extensions.findByType(AppExtension.class)找到一个可用的变体(因为会将所有的变体task都加入到任务队列中),将该变体作为变体名来执行完sync任务(仅仅为了完成sync任务,没有任何意义,在执行build任务的时候还会通过{@link #getVariantNameInBuild}替换掉逻辑)
101 | * 但是最理想的解决方案是该在sync的时候,可以不执行该插件(判断逻辑就是获取的variantName为null的时候,{@link #apply()}直接返回即可)
102 | *
103 | * TODO 需要验证在debug release多个变体打包过程
104 | * @param project
105 | * @return "HuaweiDebug"\"Debug"...
106 | */
107 | void getVariantNameInBuild(Project project) {
108 | String parameter = project.gradle.getStartParameter().getTaskRequests().toString()
109 | //assemble(\w+)(Release|Debug)仅提取Huawei
110 | String regex = parameter.contains("assemble") ? "assemble(\\w+)" : "generate(\\w+)"
111 | Pattern pattern = Pattern.compile(regex)
112 | Matcher matcher = pattern.matcher(parameter)
113 | if (matcher.find()) {
114 | //group(0)就是指的整个串,group(1) 指的是第一个括号里的东西,group(2)指的第二个括号里的东西
115 | variantName = matcher.group(1)
116 | }
117 | //但是sync时返回的内容:[DefaultTaskExecutionRequest{args=[],projectPath='null'}].
118 | //所以此时走注释中的(2),实现"则直接但是最理想的解决方案是该在sync的时候,可以不执行该插件"这种方案,则直接隐藏下面的代码
119 | if (!isValidVariantName()) {
120 | //从AppExtension中获取所有变体,作为获取当前变体的备用方案
121 | getValidVariantNameFromAllVariant(project)
122 | }
123 | }
124 |
125 | /**
126 | * 获取所有的变体中的一个可用的变体名,仅仅用来保证sync任务可执行而已
127 | * project.extensions.findByType()有执行时机,所以会出现在getVariantNameInBuild()中直接调用getVariantNameFromAllVariant()将无法更新variantName
128 | *
129 | * @param project
130 | */
131 | void getValidVariantNameFromAllVariant(Project project) {
132 | if (isValidVariantName()) {
133 | return
134 | }
135 | //但是sync时返回的内容:[DefaultTaskExecutionRequest{args=[],projectPath='null'}],其实该过程可以不执行该插件也可以
136 | //直接从所有的变体中取一个可用的变体名,返回
137 | //
138 | project.extensions.findByType(AppExtension.class).variantFilter {
139 | variantName = it.name.capitalize()
140 | SystemPrint.outPrintln(String.format("Fake variant name from all variant is \" %s \"", variantName))
141 | if (isValidVariantName()) {
142 | return true
143 | }
144 | }
145 | }
146 |
147 | boolean isValidVariantName() {
148 | variantName != null && variantName.length() > 0
149 | }
150 |
151 | }
--------------------------------------------------------------------------------
/manifestplugin/src/main/groovy/com/wj/manifest/task/AddExportForPackageManifestTask.groovy:
--------------------------------------------------------------------------------
1 | package com.wj.manifest.task
2 |
3 | import com.wj.manifest.utils.SystemPrint
4 | import groovy.xml.XmlUtil
5 | import org.gradle.api.DefaultTask
6 | import org.gradle.api.file.FileCollection
7 | import org.gradle.api.tasks.TaskAction
8 | import org.xml.sax.SAXException
9 |
10 | import javax.xml.parsers.ParserConfigurationException
11 |
12 | /**
13 | * Created by wenjing.liu on 2021/9/16 in J1.
14 | *
15 | * 适配Android12,为每个带有添加android:exported="true"属性
16 | * 在合并所有的Manifest之前为所有的AndroidManifest文件添加
17 | * @author wenjing.liu
18 | */
19 | class AddExportForPackageManifestTask extends DefaultTask {
20 | protected static String TAG = "AddExportForPackageManifestFromManifestProject"
21 | String ATTRIBUTE_EXPORT = "{http://schemas.android.com/apk/res/android}exported"
22 | private FileCollection manifestCollection
23 | private File mainManifestFile
24 | private boolean isMainManifestFile
25 |
26 | /**
27 | * 设置所有的 需要合并的Manifest文件
28 | * @param collection
29 | */
30 | void setManifestsFileCollection(FileCollection collection) {
31 | manifestCollection = collection
32 | }
33 |
34 | /**
35 | *
36 | * @param file
37 | */
38 | void setMainManifestFile(File file) {
39 | mainManifestFile = file
40 | }
41 |
42 | @TaskAction
43 | void doTaskAction() {
44 | //处理所有包下的AndroidManifest文件添加android:exported
45 | SystemPrint.outPrintln(TAG, "Running .....")
46 | isMainManifestFile = false
47 | manifestCollection.each {
48 | handlerVariantManifestFile(it)
49 | }
50 | //自己APP中的manifest文件只提示增加,不主动添加
51 | isMainManifestFile = true
52 | handlerVariantManifestFile(mainManifestFile)
53 | }
54 |
55 | /**
56 | * 处理单个变体的Manifest文件
57 | */
58 | void handlerVariantManifestFile(File manifestFile) {
59 | if (!manifestFile.exists()) {
60 | return
61 | }
62 | def node = readManifestFromPackageManifest(manifestFile)
63 | writeManifestForPackageManifest(manifestFile, node)
64 | }
65 |
66 | /**
67 | * 读manifest内容
68 | * @param manifestFile
69 | * @return
70 | */
71 | Node readManifestFromPackageManifest(File manifestFile) {
72 | try {
73 | XmlParser xmlParser = new XmlParser()
74 | def node = xmlParser.parse(manifestFile)
75 |
76 | //node.attributes();获取的一级内容 里设置的内容如:key为package、encoding,value为对应的值
77 | //node.children();获取的二级内容
78 | //node.application直接可获取到这级标签
79 | //第一步:处理
80 | node.application.activity.each {
81 | //如果已经有android:exported,则直接循环下一个:return true 相当于continue
82 | if (handlerEveryNodeWithoutExported(it)) {
83 | return true
84 | }
85 | }
86 | //第二步:处理
87 | node.application.service.each {
88 | //如果已经有android:exported,则直接循环下一个:return true 相当于continue
89 | if (handlerEveryNodeWithoutExported(it)) {
90 | return true
91 | }
92 | }
93 | //第三步:处理
94 | node.application.receiver.each {
95 | //如果已经有android:exported,则直接循环下一个:return true 相当于continue
96 | if (handlerEveryNodeWithoutExported(it)) {
97 | return true
98 | }
99 | }
100 | return node
101 |
102 | } catch (ParserConfigurationException e) {
103 | e.printStackTrace()
104 | } catch (SAXException e) {
105 | e.printStackTrace()
106 | } catch (IOException e) {
107 | e.printStackTrace()
108 | }
109 | }
110 |
111 | /**
112 | * 第四步:保存到原AndroidManifest文件中
113 | * @param manifestFile
114 | * @param node
115 | */
116 | void writeManifestForPackageManifest(File manifestFile, Node node) {
117 | if (isMainManifestFile) {
118 | //如果是主module的manifest,自行添加
119 | return
120 | }
121 | String result = XmlUtil.serialize(node)
122 | manifestFile.write(result, "utf-8")
123 | }
124 |
125 | /**
126 | * 为每个需要添加android:exported的node
127 | * 在each{}中该方法不能定义为private,否则会提示找到该方法
128 | * @param it
129 | * @return
130 | */
131 | boolean handlerEveryNodeWithoutExported(Node it) {
132 | //attributes()取得是在里面配置的属性值,而里面嵌套的<>>可直接通过.xxx的形式取得
133 | def attrs = it.attributes()
134 | //如果含有了android:exported,则直接处理下一个.
135 | if (hasAttributeExported(attrs)) {
136 | //SystemPrint.errorPrintln(TAG, String.format("The \" %s \" already has \" android:exported \" , to next one .", it.name()))
137 | //结束本次循环,相当于continue find return true相当于break
138 | return true
139 | }
140 | //得到配置的里面的如
141 | def children = it.children()
142 | if (hasIntentFilter(children)) {
143 | handlerAddExportForNode(it)
144 | }
145 | return false
146 | }
147 | /**
148 | * 添加android:export
149 | */
150 | private void handlerAddExportForNode(Node node) {
151 | if (isMainManifestFile) {
152 | //仅做提示
153 | String errorFormat = "To solve the build error \n \"Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined\"\n" +
154 | "you must set \"android:exported\" based on actual demand for manifest in main module of \n \" %s \""
155 | SystemPrint.errorPrintln(TAG, String.format(errorFormat, node.attributes().toString()))
156 | return
157 | }
158 | SystemPrint.outPrintln(TAG, String.format("Handler third sdk of \"%s\" , so add \"android:exported=true\" .", node.name()))
159 | SystemPrint.outPrintln(TAG, String.format("In Handler: \n %s", node.attributes()))
160 | //注意这里使用的是"android:exported"而不是ATTRIBUTE_EXPORT!!!!!!
161 | node.attributes().put("android:exported", true)
162 | //node.attributes().put(ATTRIBUTE_EXPORT,"true")
163 | //TODO 该种方式就可以替换,但是之前已有的不管采用{http://schemas.android.com/apk/res/android}name还是android:name都无法赋值成功
164 | /**这个原因跟在hasAttributeExported()使用attrs.containsKey(ATTRIBUTE_EXPORT)是一个原因,
165 | * 只能在attrs.each中取出里面key在进行判断才可以返回true,然后在调用下面的方法才可以替换成功
166 | * node.attributes().replace(new String("android:name"), "add")
167 | * node.attributes().replace(new String("{http://schemas.android.com/apk/res/android}name"), "ddd")
168 | * */
169 | }
170 |
171 |
172 | /**
173 | * 是否含有android:exported属性
174 | * TODO attrs.containsKey(ATTRIBUTE_EXPORT) 不起作用
175 | * @return
176 | */
177 | private boolean hasAttributeExported(Map attrs) {
178 | boolean isExported = false
179 | attrs.find {
180 | if (ATTRIBUTE_EXPORT.equals(it.key.toString())) {
181 | isExported = true
182 | //find return true相当于break
183 | return true
184 | }
185 | }
186 | return isExported
187 | }
188 | /**
189 | * 是否含有
190 | * @param children
191 | * @return
192 | */
193 | private boolean hasIntentFilter(List children) {
194 | boolean isIntent = false
195 | children.find {
196 | if ("intent-filter".equals(it.name())) {
197 | isIntent = true
198 | //find return true相当于break
199 | return true
200 | }
201 | }
202 | return isIntent
203 | }
204 |
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/manifestplugin/src/main/groovy/com/wj/manifest/task/SetLatestVersionForMergedManifestTask.groovy:
--------------------------------------------------------------------------------
1 | package com.wj.manifest.task
2 |
3 | import com.wj.manifest.ManifestExtension
4 | import com.wj.manifest.utils.SystemPrint
5 | import org.gradle.api.DefaultTask
6 | import org.gradle.api.tasks.TaskAction
7 |
8 | /**
9 | * Created by wenjing.liu on 2021/9/17 in J1.
10 | * 读取配置文件(含有对每个版本的信息描述)中的versionCode与versionName
11 | * 然后在加上360的自动打包流程
12 | * 用来复写下Extension的定义 以及@InputFile
13 | *
14 | * 调用 clean 以后第一次编译的过程,这个就是全量编译,
15 | * 之后修改了代码或者资源文件,再次编译,就是增量编译。
16 | * @author wenjing.liu
17 | */
18 | class SetLatestVersionForMergedManifestTask extends DefaultTask {
19 | protected static final String TAG = "SetLatestVersionForMergedManifestFromManifestProject"
20 | File manifestFile
21 | String versionName
22 | String versionCode
23 | /**
24 | * 设置Manifest文件的路径如Users/j1/Documents/android/code/studio/AndroidPlugin/app/build/intermediates/merged_manifest/xiaomiRelease/AndroidManifest.xml
25 | * @param file
26 | */
27 | void setManifestFile(File file) {
28 | this.manifestFile = file
29 | }
30 |
31 | @TaskAction
32 | void doTaskAction() {
33 | SystemPrint.outPrintln(TAG, String.format("Running handler the manifest is \n %s ", manifestFile.getAbsolutePath()))
34 | ManifestExtension extension = project.getExtensions().findByType(ManifestExtension)
35 | File versionFile = extension.versionFile
36 | if (versionFile == null || !versionFile.exists()) {
37 | SystemPrint.errorPrintln(TAG, "NO VERSION XML SOURCE")
38 | return
39 | }
40 | handlerVersionNameAndCodeForAndroidManifest(versionFile)
41 | }
42 |
43 | /**
44 | * 为Manifest文件添加新的版本号
45 | * @param versionFile
46 | */
47 | void handlerVersionNameAndCodeForAndroidManifest(File versionFile) {
48 | SystemPrint.outPrintln(TAG, String.format("Handler the versionFile is\n") + versionFile)
49 | readVersionCodeAndVersionName(versionFile)
50 | if (versionName == null || versionName.length() == 0 || versionCode == null || versionCode.length() == 0) {
51 | SystemPrint.errorPrintln(TAG, "The config version file must set the and !")
52 | return
53 | }
54 | writeVersionCodeAndVersionNameForManifest()
55 | }
56 |
57 | /**
58 | * 从versionFile中读取versionCode和versionName
59 | * @param versionFile
60 | */
61 | void readVersionCodeAndVersionName(File versionFile) {
62 |
63 | XmlParser xmlParser = new XmlParser()
64 | def node = xmlParser.parse(versionFile)
65 |
66 | node.children().find {
67 | if (hasNewVersionTagChildNode(it)) {
68 | //获取的xml中配置的versionCode和versionName
69 | versionCode = it.versionCode.text()
70 | versionName = it.versionName.text()
71 | return true
72 | }
73 | }
74 | }
75 |
76 | /**
77 | * 为Manifest文件写入配置的versionCode and versionName
78 | */
79 | void writeVersionCodeAndVersionNameForManifest() {
80 | SystemPrint.outPrintln(TAG, String.format("Set the new versionCode %s , versionName %s", versionCode, versionName))
81 | XmlParser xmlParser = new XmlParser()
82 | def node = xmlParser.parse(manifestFile)
83 | int count = 0
84 | node.attributes().each {
85 | if (it.key.toString().endsWith("versionCode")) {
86 | node.attributes().replace(it.key, versionCode)
87 | count++
88 | }
89 | if (it.key.toString().endsWith("versionName")) {
90 | node.attributes().replace(it.key, versionName)
91 | count++
92 | }
93 | //仅仅为了减少循环次数而已
94 | if (count == 2) {
95 | SystemPrint.outPrintln(TAG, String.format("Write \" %s \" is ok !", manifestFile))
96 | return true
97 | }
98 | }
99 | }
100 |
101 | /**
102 | * 含有latest="true"的节点
103 | * @param it
104 | * @return
105 | */
106 | boolean hasNewVersionTagChildNode(Node it) {
107 | boolean isFindNewVersion = false
108 | it.attributes().find {
109 | if (("latest").equals(it.key.toString())) {
110 | isFindNewVersion = it.value
111 | //找到标记" latest="true" "为最新版本的标记
112 | if (isFindNewVersion) {
113 | return true
114 | }
115 | }
116 | }
117 | return isFindNewVersion
118 | }
119 |
120 |
121 | /** IncrementalTask //全量的时候执行操作
122 | @Override
123 | protected void doFullTaskAction() throws Exception {}//增量的时候执行操作
124 | @Override
125 | protected void doIncrementalTaskAction(@NotNull Map changedInputs) throws Exception {super.doIncrementalTaskAction(changedInputs)}
126 |
127 | @Override
128 | Property getAnalyticsService() {return null}
129 |
130 | @Override
131 | WorkerExecutor getWorkerExecutor() {return null}
132 |
133 | @Override
134 | protected boolean getIncremental() {//return true 支持增量; false:不支持增量
135 | //MergeResources:增量任务过程和全量其实差异不大,只不过是在获取 resourceSets 的时候,使用的是修改后的文件
136 | return super.getIncremental()} */
137 | }
--------------------------------------------------------------------------------
/manifestplugin/src/main/groovy/com/wj/manifest/utils/SystemPrint.java:
--------------------------------------------------------------------------------
1 | package com.wj.manifest.utils;
2 |
3 | /**
4 | * Created by wenjing.liu on 2021/6/9 in J1.
5 | *
6 | * 日志输出
7 | *
8 | * @author wenjing.liu
9 | */
10 | public class SystemPrint {
11 |
12 | private static final boolean DEBUG = true;
13 | private static final String TAG = "ManifestProject";
14 |
15 | public static void outPrintln(String tag, String info) {
16 | if (DEBUG) {
17 |
18 | System.out.println(String.format("<= %s => : %s ", getTag(tag), info));
19 | }
20 | }
21 |
22 | /**
23 | * \33[前背景色代号;背景色代号;数字m
24 | * 前背景色代号41-46
25 | * 背景色代号31-36
26 | * 数字m 1加粗 3斜体 4 下划线
27 | */
28 | private static void color(String info) {
29 | System.out.format("\33[32;1m%s%n", info);
30 | }
31 |
32 | public static void errorPrintln(String tag, String info) {
33 | if (DEBUG) {
34 | System.err.println(String.format("<= %s => : %s ", getTag(tag), info));
35 |
36 | }
37 | }
38 |
39 | public static void outPrintln(String info) {
40 | outPrintln(TAG, info);
41 | }
42 |
43 | public static void errorPrintln(String info) {
44 | errorPrintln(TAG, info);
45 | }
46 |
47 | private static String getTag(String tag) {
48 | if (tag.length() <= 20) {
49 | return tag;
50 | }
51 | return String.format("%s...", tag.substring(0, 19));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/manifestplugin/src/main/kotlin/com/wj/manifest/ManifestExtensionByKotlin.kt:
--------------------------------------------------------------------------------
1 | package com.wj.manifest;
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * Created by wenjing.liu on 2021/9/18 in J1.
7 | * 扩展属性
8 | * 然后再用来学习下kotlin语法
9 | *
10 | * Java的类和方法默认是open的,而Kotlin中默认都是final的。
11 | * 类的修饰符:
12 | * final:不可被继承,相关的成员不能被复写.默认为final
13 | * open:可以被继承
14 | * abstract:必须被复写
15 | * enum:枚举类
16 | * data:数据类.该类中没有函数只有属性定义,自动生成toString()
17 | * override:复写父类或接口中的成员
18 | * @author wenjing.liu
19 | */
20 | open class ManifestExtensionByKotlin {
21 | /**
22 | * object:相当于类中的所有方法、属性都为静态方法,object类实例本身为单例,object也可以表示为一个匿名类或不存在对象
23 | * component object:相当于代码块内的属性、方法为静态的属性、方法
24 | */
25 | companion object {
26 | //const 关键字用来修饰常量,且只能修饰 val,不能修饰var, companion object 的名字可以省略,可以使用 Companion来指代
27 | const val TAG: String = "ManifestPlugin"
28 | }
29 |
30 | //lateinit 用于var 变量初始化,避免非空检查;
31 | //lazy用于val 应用于单例模式,而且仅当变量被第一次调用的时候,委托方法才会执行,第二次调用只会返回结果
32 | lateinit var versionFile: File
33 |
34 | }
--------------------------------------------------------------------------------
/manifestplugin/src/main/kotlin/com/wj/manifest/ManifestProjectByKotlin.kt:
--------------------------------------------------------------------------------
1 | package com.wj.manifest
2 |
3 | import org.gradle.api.Plugin
4 | import org.gradle.api.Project
5 |
6 |
7 | /**
8 | * Created by wenjing.liu on 2021/9/18 in J1.
9 | *
10 | * @author wenjing.liu
11 | */
12 | class ManifestProjectByKotlin : Plugin {
13 |
14 | override fun apply(p0: Project) {
15 | SystemPrintByKotlin.outPrintln("Welcome ManifestProjectByKotlin")
16 |
17 | createExtension(p0)
18 | }
19 |
20 | private fun createExtension(project: Project) {
21 | project.extensions.create(
22 | ManifestExtensionByKotlin.TAG,
23 | ManifestExtensionByKotlin::class.javaObjectType
24 | )
25 | //Boolean::class.java指向的时候kotlin标准库里面定义的类Boolean.kt类
26 | //Boolean::class.javaObjectType指向的是 kotlin 标准库中 JvmClassMappingKt 中的一个方法,即Boolean.java类
27 |
28 | }
29 | }
--------------------------------------------------------------------------------
/manifestplugin/src/main/kotlin/com/wj/manifest/SystemPrintByKotlin.kt:
--------------------------------------------------------------------------------
1 | package com.wj.manifest
2 |
3 | /**
4 | * Created by wenjing.liu on 2021/9/18 in J1.
5 | *
6 | * 系统日志输出
7 | *
8 | * object:相当于类中的所有方法、属性都为静态方法,object类实例本身为单例,object也可以表示为一个匿名类或不存在对象
9 | * component object:相当于代码块内的属性、方法为静态的属性、方法.只不过Kotlin保证该类只会存在一个伴生类对象
10 | * @author wenjing.liu
11 | */
12 | object SystemPrintByKotlin {
13 | val TAG: String = "ManifestProjectByKotlin"
14 | val DEBUG: Boolean = true
15 |
16 | /**
17 | * 定义静态方法
18 | * (1)companion object { }:会在该类内部创建一个伴生类,而被修饰的方法为伴生类的实例方法,
19 | * 只不过Kotlin保证该类只会存在一个伴生类对象
20 | * 确确实实的静态方法:注解和顶层方法
21 | * (2)@JvmStatic
22 | * (3)顶层方法指的是定义在一个单独的类文件中,可通过@file:JvmName("")制定在java调用时的类名称,Kotlin会将所有的顶层方法编译成静态方法
23 | */
24 | // @JvmStatic
25 | fun outPrintln(info: String, tag: String) {
26 | if (DEBUG) {
27 | println("<- $tag -> : $info")
28 | }
29 | }
30 |
31 |
32 | // 定义方法
33 | //fun [方法名] ( [参数名] : [参数类型] ) : [返回类型]{
34 | // ...
35 | // return [返回值]
36 | //}
37 | //若无返回值,则可以去除: [返回类型]
38 |
39 | //@JvmStatic
40 | fun outPrintln(info: String) {
41 | outPrintln(info, TAG)
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/manifestplugin/src/main/resources/META-INF/gradle-plugins/com.wj.plugin.manifest.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.wj.manifest.ManifestProjectByKotlin
2 | //implementation-class=com.wj.manifest.ManifestProject
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.jar
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | 12cb17ca8007429ffe4b5f95d0567bee
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | 0511b80c074930bfdebe98dd654ea53a83a0f2c4
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.android.wj.plugins
6 | debugplugin
7 | 1.0.0
8 |
9 |
10 | com.android.tools.build
11 | gradle
12 | 4.2.1
13 | runtime
14 |
15 |
16 | org.ow2.asm
17 | asm
18 | 9.2
19 | runtime
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | a9bf42adbb69d1938a4d58e93146a35e
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/1.0.0/DebugPlugin-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | e7cc881c0778fb30f9265cf9c1f24ab92aea9555
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.android.wj.plugins
4 | debugplugin
5 |
6 | 1.0.0
7 |
8 | 1.0.0
9 |
10 | 20210813074022
11 |
12 |
13 |
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | 0aba6f90323d6d13a853a21b58922646
--------------------------------------------------------------------------------
/plugins/com/android/wj/plugins/DebugPlugin/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | a8f96d03dd6aa60bf1da8ff5770453ba125f168e
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0-classifiter.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0-classifiter.jar
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0-classifiter.jar.md5:
--------------------------------------------------------------------------------
1 | f662b057203fd1d20fe25294f01f6dba
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0-classifiter.jar.sha1:
--------------------------------------------------------------------------------
1 | b50ee1eb3f86e3fa21c7048e2cadc08f4f1eef56
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.jar
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | a5ef58a18143b79ea7d1d585c61fce04
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | 1e626858c6988be14541cef41fd182f8592ce4d6
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.wj.plugin
6 | firstplugin
7 | 1.0.0
8 |
9 |
10 | com.android.tools.build
11 | gradle
12 | 4.2.1
13 | runtime
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | 0c70298248cc04c1949618ab56dd4138
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/1.0.0/firstplugin-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | bb18bbea40fef6a19c7081ec845c806e912c9a83
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.wj.plugin
4 | firstplugin
5 |
6 | 1.0.0
7 |
8 | 1.0.0
9 |
10 | 20210913075454
11 |
12 |
13 |
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | c470a64e3638426b2fd0393237b108b5
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/firstplugin/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | 027f1f76bf68fc607cec0b443bb299efa62f4706
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.jar
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | 6c8ab52433d00f8290c8ce50d13996f9
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | f25f68569234e8ac48766620fd94fead2338569b
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.wj.plugin
5 | manifestplugin
6 | 1.0.0
7 |
8 |
9 | org.jetbrains.kotlin
10 | kotlin-stdlib
11 | 1.4.10
12 | compile
13 |
14 |
15 | com.android.tools.build
16 | gradle
17 | 4.2.2
18 | runtime
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | 2993a1f56e631da083a88b2148e76538
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/1.0.0/manifestplugin-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | e38e57e00a90b12d4582a4626c9a2d290847328a
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.wj.plugin
4 | manifestplugin
5 |
6 | 1.0.0
7 |
8 | 1.0.0
9 |
10 | 20210924055631
11 |
12 |
13 |
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | 1f76a02c802ee7371cfff0cb44cae177
--------------------------------------------------------------------------------
/plugins/com/wj/plugin/manifestplugin/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | 0559f30ce6780ebee16c93fb0a1e3d091e2f718c
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = "AndroidPlugin"
2 | include ':app'
3 | include ':firstplugin'
4 | include ':wjplugin'
5 | include ':manifestplugin'
6 |
7 | def prefix = "<<<<<<
41 | // println(prefix + "allprojects Project name = " + project.name)
42 | }
43 |
44 | //循环每个build.gradle来构建task依赖关系图
45 | this.gradle.beforeProject {
46 | Project project ->
47 | println(prefix + "beforeProject Project name = " + project.name)
48 | }
49 |
50 | this.gradle.afterProject {
51 | Project project ->
52 | //println(prefix + "afterProject Project name = " + project.name)
53 | }
54 | //输出task关系图中的所有task
55 | this.gradle.taskGraph.whenReady {
56 | // List taskList = this.gradle.taskGraph.allTasks
57 | // for (Task task : taskList) {
58 | // println(prefix + "in graph of task = " + task.name+" in project = "+task.getProject())
59 | // }
60 | }
61 |
62 | //每个task的执行过程的监听
63 | this.gradle.taskGraph.beforeTask {
64 | Task task ->
65 | // println(prefix + "beforeTask Task name = " + task.name)
66 | }
67 |
68 | this.gradle.taskGraph.afterTask {
69 | it ->
70 | // println(prefix + "afterTask Task name = " + it.name)
71 | }
72 |
73 |
74 |
--------------------------------------------------------------------------------
/testcopy.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wenjing-bonnie/AndroidPlugin/1d2216d0b6b335ff4fd5645b3d653f736d7fe402/testcopy.txt
--------------------------------------------------------------------------------
/wjplugin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/wjplugin/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'groovy'
2 | apply plugin: 'maven'
3 |
4 | dependencies {
5 | implementation gradleApi()
6 | implementation localGroovy()
7 | implementation 'com.android.tools.build:gradle:4.2.1'
8 | implementation 'org.ow2.asm:asm:9.2'
9 | }
10 |
11 | repositories {
12 | mavenCentral()
13 | }
14 |
15 | //
16 | uploadArchives {
17 | repositories {
18 | mavenDeployer {
19 | pom.groupId = "com.android.wj.plugins"
20 | pom.artifactId = "debugplugin"
21 | pom.version = "1.0.0"
22 |
23 | repository(url: uri('../plugins'))
24 | }
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/wjplugin/src/main/groovy/com/android/wj/debug/WjDebugPluginProject.groovy:
--------------------------------------------------------------------------------
1 | package com.android.wj.debug
2 |
3 | import com.android.build.gradle.AppExtension
4 | import com.android.wj.debug.autotransform.AutoLogTransform
5 | import com.android.wj.debug.utils.SystemOutPrintln
6 | import org.gradle.api.Plugin
7 | import org.gradle.api.Project
8 |
9 | class WjDebugPluginProject implements Plugin {
10 | @Override
11 | void apply(Project project) {
12 | SystemOutPrintln.println("Welcome to WjDebugPluginProject")
13 | project.extensions.findByType(AppExtension.class).registerTransform(new AutoLogTransform())
14 | }
15 | }
--------------------------------------------------------------------------------
/wjplugin/src/main/groovy/com/android/wj/debug/autologvisitor/AutoLogAdviceAdapter.java:
--------------------------------------------------------------------------------
1 | package com.android.wj.debug.autologvisitor;
2 |
3 | import com.android.wj.debug.utils.SystemOutPrintln;
4 |
5 | import org.objectweb.asm.Label;
6 | import org.objectweb.asm.MethodVisitor;
7 | import org.objectweb.asm.Opcodes;
8 | import org.objectweb.asm.Type;
9 | import org.objectweb.asm.commons.AdviceAdapter;
10 |
11 | /**
12 | * Created by wenjing.liu on 2021/8/3 in J1.
13 | * 查看.class文件的所有方法,为所有的方法添加日志
14 | *
15 | * @author wenjing.liu
16 | */
17 | public class AutoLogAdviceAdapter extends AdviceAdapter {
18 | private MethodVisitor methodVisitor;
19 | private String name;
20 | /**
21 | * 该方法的参数
22 | */
23 | private Type[] args;
24 | /**
25 | * 是否开启统计调用时间
26 | */
27 | private boolean isInjectCallTime = true;
28 |
29 | private Label labelBegin;
30 | private Label labelEnd;
31 |
32 | /**
33 | * Constructs a new {@link AdviceAdapter}.
34 | *
35 | * @param api the ASM API version implemented by this visitor. Must be one of {@link
36 | * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
37 | * @param methodVisitor the method visitor to which this adapter delegates calls.
38 | * @param access the method's access flags (see {@link Opcodes}).
39 | * @param name the method's name.
40 | * @param descriptor the method's descriptor (see {@link Type Type}).
41 | */
42 | protected AutoLogAdviceAdapter(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
43 | super(api, methodVisitor, access, name, descriptor);
44 | this.name = name;
45 | this.methodVisitor = methodVisitor;
46 | this.args = Type.getArgumentTypes(descriptor);
47 | for (Type type : args) {
48 | SystemOutPrintln.println("internal name = " + type.getInternalName() + " , classname = " + type.getClassName());
49 | }
50 | }
51 |
52 | @Override
53 | public void visitParameter(String name, int access) {
54 | super.visitParameter(name, access);
55 | SystemOutPrintln.println(" = visitParameter name = " + name);
56 | }
57 |
58 | /**
59 | * 开始扫描该方法时回调该方法
60 | */
61 | @Override
62 | public void visitCode() {
63 | super.visitCode();
64 | SystemOutPrintln.println(" = visitCode");
65 | }
66 |
67 | /**
68 | * 进入到该方法的时候回调该方法
69 | */
70 | @Override
71 | protected void onMethodEnter() {
72 | super.onMethodEnter();
73 | SystemOutPrintln.println(" = onMethodEnter");
74 | //methodVisitor.
75 | if (!isInjectCallTime()) {
76 | return;
77 | }
78 | //添加方法调用前的时间,对应代码:long beginTime = System.currentTimeMillis();
79 | systemCurrentTimeMillisOnMethodEnter();
80 | }
81 |
82 |
83 | /**
84 | * 添加方法调用前的时间,对应代码:long beginTime = System.currentTimeMillis();
85 | */
86 | private void systemCurrentTimeMillisOnMethodEnter() {
87 | labelBegin = new Label();
88 | methodVisitor.visitLabel(labelBegin);
89 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
90 | methodVisitor.visitVarInsn(Opcodes.LSTORE, 3);
91 | }
92 |
93 | /**
94 | * 之所以不在{@link #onMethodExit(int)}中,
95 | * 是因为如果源码的return带有运算表达式,会先调用{@link #onMethodExit(int)},然后在计算表达式,最后才调用{@link #visitInsn(int)}
96 | *
97 | * @param opcode
98 | */
99 | @Override
100 | public void visitInsn(int opcode) {
101 | if (!isInjectCallTime() || opcode < IRETURN || opcode > RETURN) {
102 | super.visitInsn(opcode);
103 | return;
104 | }
105 | //方法返回的时候,添加执行时间
106 | SystemOutPrintln.println(" = onMethodExit");
107 | //添加方法调用之后的时间,对应代码:long callTime = System.currentTimeMillis() - beginTime;
108 | systemCurrentTimeMillisOnMethodExit();
109 |
110 | //初级:输出方法执行时间,对应代码:Log.d("AUTO", String.valueOf(callTime));
111 | //logMethodExit();
112 | //中级:输出方法执行时间,对应代码:Log.d("AUTO", String.valueOf(callTime));
113 | logStringMethodExit();
114 |
115 | // visitLocalVariable()描述或定义存储在Code属性的LocalVariableTable和LocalVariableTypeTable属性中的调试信息。 它们不是正常操作所必需的,与StackMapTable存储的信息不同。
116 | // 换句话说,除非您想提供调试信息,否则无论是否自动计算堆栈映射帧,您都不需要调用visitLocalVariable() 。
117 | //请注意这些属性中存储的信息的差异。 LocalVariable[Type]Table存储有关源级语言的局部变量的名称和[泛型]类型及其范围。 StackMapTable存储有关字节码验证器的JVM类型系统的局部变量和操作数堆栈条目的类型信息。
118 | //methodVisitor.visitLocalVariable();
119 | super.visitInsn(opcode);
120 | }
121 |
122 | @Override
123 | public void visitEnd() {
124 | super.visitEnd();
125 | SystemOutPrintln.println(" = visitEnd");
126 | }
127 |
128 | /**
129 | * 添加方法调用之后的时间,对应代码:long callTime = System.currentTimeMillis() - beginTime;
130 | */
131 | private void systemCurrentTimeMillisOnMethodExit() {
132 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
133 | methodVisitor.visitVarInsn(LLOAD, 3);
134 | methodVisitor.visitInsn(LSUB);
135 | methodVisitor.visitVarInsn(LSTORE, 5);
136 | }
137 |
138 | /**
139 | * 输出方法执行时间,对应代码:Log.d("AUTO", String.valueOf(callTime));
140 | */
141 | private void logMethodExit() {
142 | //输出方法执行时间,对应代码:Log.d("AUTO", String.valueOf(callTime));
143 | methodVisitor.visitLdcInsn("AUTO");
144 | methodVisitor.visitVarInsn(LLOAD, 5);
145 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
146 | methodVisitor.visitMethodInsn(INVOKESTATIC, "android/util/Log", "d", "(Ljava/lang/String;Ljava/lang/String;)I", false);
147 | methodVisitor.visitInsn(POP);
148 | }
149 |
150 | /**
151 | * 输出方法执行时间,对应代码:Log.d("AUTO", String.format("cost time is [%l]ms", callTime));
152 | */
153 | private void logStringMethodExit() {
154 | methodVisitor.visitLdcInsn("AUTO");
155 | String stringFormat = "[" + name + "] cost time is [%d] ms;";
156 | methodVisitor.visitLdcInsn(stringFormat);
157 | methodVisitor.visitInsn(ICONST_1);
158 | //创建数组
159 | methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Object");
160 | //复制栈顶
161 | methodVisitor.visitInsn(DUP);
162 | //将常量压入栈
163 | methodVisitor.visitInsn(ICONST_0);
164 | methodVisitor.visitVarInsn(LLOAD, 5);
165 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
166 | methodVisitor.visitInsn(AASTORE);
167 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/String", "format", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", false);
168 | methodVisitor.visitMethodInsn(INVOKESTATIC, "android/util/Log", "d", "(Ljava/lang/String;Ljava/lang/String;)I", false);
169 | methodVisitor.visitInsn(POP);
170 | labelEnd = new Label();
171 | methodVisitor.visitLabel(labelEnd);
172 | methodVisitor.visitLocalVariable("beginTime", "J", null, labelBegin, labelEnd, 3);
173 | //当设置了ClassWriter.COMPUTE_MAXS的时候,会自动的计算visitMaxs(int maxStack, int maxLocals)
174 | //methodVisitor.visitMaxs(0,0);
175 | }
176 |
177 |
178 | private void logStringTagMethodExit() {
179 |
180 | }
181 |
182 | /**
183 | * 需要增加方法执行时间的标识
184 | *
185 | * @return
186 | */
187 | private boolean isInjectCallTime() {
188 | return !isInjectCallTime || !"".equals(name);
189 | }
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/wjplugin/src/main/groovy/com/android/wj/debug/autologvisitor/AutoLogClassVisitor.java:
--------------------------------------------------------------------------------
1 | package com.android.wj.debug.autologvisitor;
2 |
3 | import com.android.wj.debug.utils.SystemOutPrintln;
4 |
5 | import org.objectweb.asm.AnnotationVisitor;
6 | import org.objectweb.asm.ClassVisitor;
7 | import org.objectweb.asm.FieldVisitor;
8 | import org.objectweb.asm.MethodVisitor;
9 | import org.objectweb.asm.Opcodes;
10 | import org.objectweb.asm.commons.AdviceAdapter;
11 |
12 | /**
13 | * Created by wenjing.liu on 2021/8/3 in J1.
14 | *
15 | * 访问所有.class的类成员
16 | *
17 | * @author wenjing.liu
18 | */
19 | public class AutoLogClassVisitor extends ClassVisitor {
20 |
21 | public AutoLogClassVisitor(ClassVisitor visitor) {
22 | // ASM API versions. 由于AdviceAdapter目前只支持到ASM7,所以这里只能设置Opcodes.ASM7,有时候会抛出(不是必现,还没有得出规律)
23 | //设置成Opcodes.ASM9的时候,有时候会出现"com.android.tools.r8.errors.b: Absent Code attribute in method that is not native or abstract"异常,改成ASM7即没有,暂时还没有找到原因
24 | super(Opcodes.ASM9, visitor);
25 | }
26 |
27 | /**
28 | * 当开始扫描类的时候回调的第一个方法
29 | *
30 | * @param version jdk版本.如52则为jdk1.8,对应{@link Opcodes#V18}; 51则为jdk1.7,对应Opcodes的V17.具体参数对应值在 {@link Opcodes}中查看.
31 | * @param access 类修饰符.在ASM中以“ACC_”开头的常量:如 {@link Opcodes#ACC_PUBLIC}对应的public.具体参数对应值在 {@link Opcodes}中查看.
32 | * @param name 类名:包名+类名
33 | * @param signature 泛型信息,若未定义任何类型,则为空
34 | * @param superName 父类
35 | * @param interfaces 实现的接口的数组
36 | */
37 | @Override
38 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
39 | super.visit(version, access, name, signature, superName, interfaces);
40 | SystemOutPrintln.println("visit version = " + version + " , access = " + access + " , name = " + name + " , signature = " + signature);
41 | }
42 |
43 | /**
44 | * @param source 访问的源文件
45 | * @param debug
46 | */
47 | @Override
48 | public void visitSource(String source, String debug) {
49 | super.visitSource(source, debug);
50 | SystemOutPrintln.println("visitSource source =" + source + " , debug = " + debug);
51 | }
52 |
53 | /**
54 | * 扫描到类的方法回调该方法
55 | *
56 | * @param access 方法修饰符,同visit()中的access
57 | * @param name 方法名
58 | * @param descriptor 方法签名:(参数列表)返回类型,如void onCreate(Bundle savedInstanceState),返回的为(Landroid/os/Bundle;)V
59 | * I代表int;B代表byte;C代表char;D代表double;F代表float;J代表long;S代表short;Z代表boolean;V代表void;
60 | * [...;代表一维数组;[[...;代表二维数组;[[[...;代表三维数组
61 | * 例如输入参数为:
62 | * 1.String[]则返回[Ljava/lang/String;
63 | * 2.int,String,int[]则返回(ILjava/lang/String;[I)
64 | * @param signature 泛型相关信息
65 | * @param exceptions 会抛出异常
66 | * @return MethodVisitor
67 | */
68 | @Override
69 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
70 | SystemOutPrintln.println("visitMethod access = " + access + " , name = " + name + " , descriptor = " + descriptor);
71 | MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
72 | //TODO 这里传入ASM9为什么没有报错?
73 | AdviceAdapter logAdviceAdapter = new AutoLogAdviceAdapter(this.api, methodVisitor, access, name, descriptor);
74 | return logAdviceAdapter;
75 | // return super.visitMethod(access, name, descriptor, signature, exceptions);
76 | }
77 |
78 | /**
79 | * 扫描到类中的成员变量时回调该方法
80 | *
81 | * @param access 字段的修饰符
82 | * @param name 字段的名字
83 | * @param descriptor 同visitMethod()的descriptor
84 | * @param signature 泛型相关的信息
85 | * @param value 默认值
86 | * @return
87 | */
88 | @Override
89 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
90 | SystemOutPrintln.println("visitField descriptor = " + descriptor + " , name = " + name);
91 | return super.visitField(access, name, descriptor, signature, value);
92 | }
93 |
94 | /**
95 | * 扫描到类注解时回调该方法
96 | *
97 | * @param descriptor 注解类型
98 | * @param visible 在JVM是是否可见
99 | * @return AnnotationVisitor
100 | */
101 | @Override
102 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
103 | SystemOutPrintln.println("visitAnnotation descriptor = " + descriptor + " , visible = " + visible);
104 | return super.visitAnnotation(descriptor, visible);
105 | }
106 |
107 | /**
108 | * 该类扫描结束才会调用,用于在该类中追加方法
109 | */
110 |
111 | @Override
112 | public void visitEnd() {
113 | super.visitEnd();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/wjplugin/src/main/groovy/com/android/wj/debug/autologvisitor/AutoLogFieldVistor.java:
--------------------------------------------------------------------------------
1 | package com.android.wj.debug.autologvisitor;
2 |
3 | import org.objectweb.asm.AnnotationVisitor;
4 | import org.objectweb.asm.Attribute;
5 | import org.objectweb.asm.FieldVisitor;
6 | import org.objectweb.asm.TypePath;
7 |
8 | /**
9 | * Created by wenjing.liu on 2021/8/4 in J1.
10 | *
11 | * 访问类的成员变量.有其访问顺序:{@link #visitAnnotation(String, boolean)}->{@link #visitTypeAnnotation(int, TypePath, String, boolean)}
12 | * ->{@link #visitAttribute(Attribute)}->{@link #visitEnd()}
13 | *
14 | * @author wenjing.liu
15 | */
16 | public class AutoLogFieldVistor extends FieldVisitor {
17 | public AutoLogFieldVistor(int api, FieldVisitor fieldVisitor) {
18 | super(api, fieldVisitor);
19 | }
20 |
21 | /**
22 | * 访问成员变量的注解的时回调该方法
23 | *
24 | * @param descriptor
25 | * @param visible 在运行的时候是否可见
26 | * @return
27 | */
28 | @Override
29 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
30 | return super.visitAnnotation(descriptor, visible);
31 | }
32 |
33 | /**
34 | * 访问具体的成员变量的时回调该方法
35 | *
36 | * @param attribute
37 | */
38 | @Override
39 | public void visitAttribute(Attribute attribute) {
40 | super.visitAttribute(attribute);
41 | }
42 |
43 | /**
44 | * 访问具体的成员变量的类型上的注解时回调该方法
45 | *
46 | * @param typeRef
47 | * @param typePath
48 | * @param descriptor
49 | * @param visible
50 | * @return
51 | */
52 | @Override
53 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
54 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
55 | }
56 |
57 | /**
58 | * 访问完成员变量的时候必须回调该方法
59 | */
60 | @Override
61 | public void visitEnd() {
62 | super.visitEnd();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/wjplugin/src/main/groovy/com/android/wj/debug/autotransform/AutoLogTransform.java:
--------------------------------------------------------------------------------
1 | package com.android.wj.debug.autotransform;
2 |
3 | import com.android.build.api.transform.DirectoryInput;
4 | import com.android.build.api.transform.Format;
5 | import com.android.build.api.transform.JarInput;
6 | import com.android.build.api.transform.QualifiedContent;
7 | import com.android.build.api.transform.Transform;
8 | import com.android.build.api.transform.TransformException;
9 | import com.android.build.api.transform.TransformInput;
10 | import com.android.build.api.transform.TransformInvocation;
11 | import com.android.build.api.transform.TransformOutputProvider;
12 | import com.android.build.gradle.internal.pipeline.TransformManager;
13 | import com.android.utils.FileUtils;
14 | import com.android.wj.debug.autologvisitor.AutoLogClassVisitor;
15 | import com.android.wj.debug.utils.SystemOutPrintln;
16 |
17 | import org.objectweb.asm.ClassReader;
18 | import org.objectweb.asm.ClassVisitor;
19 | import org.objectweb.asm.ClassWriter;
20 |
21 | import java.io.File;
22 | import java.io.FileInputStream;
23 | import java.io.FileNotFoundException;
24 | import java.io.FileOutputStream;
25 | import java.io.IOException;
26 | import java.util.Collection;
27 | import java.util.Set;
28 |
29 | /**
30 | * Created by wenjing.liu on 2021/7/19 in J1.
31 | *
32 | * 为所有的方法添加日志
33 | *
34 | * @author wenjing.liu
35 | */
36 | public class AutoLogTransform extends Transform {
37 |
38 | /**
39 | * 设置该Task的名字
40 | *
41 | * @return
42 | */
43 | @Override
44 | public String getName() {
45 | return String.format("%sTask", getClass().getSimpleName());
46 | }
47 |
48 | /**
49 | * 设置输入文件类型
50 | *
51 | * @return
52 | */
53 | @Override
54 | public Set getInputTypes() {
55 | return TransformManager.CONTENT_CLASS;
56 | }
57 |
58 | /**
59 | * 设置输入文件的作用域
60 | *
61 | * @return
62 | */
63 | @Override
64 | public Set super QualifiedContent.Scope> getScopes() {
65 | return TransformManager.SCOPE_FULL_PROJECT;
66 | }
67 |
68 | /**
69 | * 是否增量编译
70 | *
71 | * @return
72 | */
73 | @Override
74 | public boolean isIncremental() {
75 | return false;
76 | }
77 |
78 |
79 | @Override
80 | public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
81 | super.transform(transformInvocation);
82 | //TODO 根据是否需要增量编译,做下处理
83 |
84 | Collection transformInputs = transformInvocation.getInputs();
85 | TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
86 | for (TransformInput input : transformInputs) {
87 | //jar包不做处理,原样复制
88 | for (JarInput jar : input.getJarInputs()) {
89 | //获取Transforms的输出目录
90 | File dest = outputProvider.getContentLocation(jar.getFile().getAbsolutePath(), jar.getContentTypes(), jar.getScopes(), Format.JAR);
91 | //将修改之后的文件拷贝到对应outputProvider的目录中
92 | FileUtils.copyFile(jar.getFile(), dest);
93 | }
94 | //处理class文件
95 | for (DirectoryInput directory : input.getDirectoryInputs()) {
96 | //获取Transforms的输出目录
97 | File dest = outputProvider.getContentLocation(directory.getName(), directory.getContentTypes(), directory.getScopes(), Format.DIRECTORY);
98 | handleDirectoryInput(directory);
99 | //将修改之后的文件拷贝到对应outputProvider的目录中
100 | FileUtils.copyDirectory(directory.getFile(), dest);
101 | }
102 | }
103 | }
104 |
105 |
106 | /**
107 | * 找到Application,然后添加代码
108 | *
109 | * @param directoryInput
110 | */
111 | private void handleDirectoryInput(DirectoryInput directoryInput) {
112 | File directorFile = directoryInput.getFile();
113 | SystemOutPrintln.println("Handle the directory path = " + directorFile.getPath());
114 | handleFileInDirectory(directorFile);
115 | }
116 |
117 | /**
118 | * 遍历里面的file,进行处理file
119 | *
120 | * @param input
121 | */
122 | private void handleFileInDirectory(File input) {
123 | if (!input.isDirectory()) {
124 | addLogForClass(input.getAbsolutePath(), input.getAbsolutePath());
125 | return;
126 | }
127 | for (File file : input.listFiles()) {
128 | handleFileInDirectory(file);
129 | }
130 |
131 | }
132 |
133 | /**
134 | * 为每个class文件的方法添加日志文件
135 | *
136 | * @param input
137 | * @param output
138 | */
139 | private void addLogForClass(String input, String output) {
140 | if (input == null || output == null) {
141 | return;
142 | }
143 | SystemOutPrintln.println(String.format("Add log for \" %s \"", input));
144 | try {
145 | FileInputStream is = new FileInputStream(input);
146 | ClassReader reader = new ClassReader(is);
147 | /***
148 | * @param reader ClassReader
149 | * @param flags:
150 | * 0:不自动计算操作数栈和局部变量表的大小,需要手动指定
151 | * COMPUTE_MAXS:自动计算操作数栈和局部变量表的大小,但必须手动调用{@link org.objectweb.asm.MethodVisitor#visitMaxs()}触发,可以传入任意参数
152 | * 但栈帧的大小需要手动计算
153 | * COMPUTE_FRAMES:自动计算所有内容。不仅自动计算操作数栈和局部变量表的大小,还会自动计算StackMapFrames,但是仍需要手动调用{@link org.objectweb.asm.MethodVisitor#visitMaxs()}触发,可以传入任意参数
154 | * 但是这些标识也会让性能损失:COMPUTE_MAXS慢10% COMPUTE_FRAMES慢2倍
155 | *
156 | */
157 | ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
158 | //将文件进行增加log
159 | AutoLogClassVisitor classVisitor = new AutoLogClassVisitor(writer);
160 |
161 | /**
162 | * @param classVisitor:给定具体处理逻辑的ClassVisitor,通常需要自定义类来继承抽象类ClassVisitor.
163 | * @param parsingOptions:解析.class文件的选项.其中有几个取值:
164 | * {@link ClassReader.SKIP_CODE}:
165 | * 跳过方法体的code属性,即Code属性下的内容不会被转换或访问;
166 | * {@link ClassReader.SKIP_DEBUG}:
167 | * 跳过文件中的调试信息,即源文件、源码调试扩展、局部变量表、行号表属性、局部变量表类型表;
168 | * 下面的这些方法不会被调用到 {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable},
169 | * {@link MethodVisitor#visitLineNumber} and {@link MethodVisitor#visitParameter}
170 | * {@link ClassReader.SKIP_FRAMES}:
171 | * 跳过文件StackMapTable和StackMap属性,即{@link MethodVisitor#visitFrame}不会被方法;
172 | * 当ClassWriter设置 {@link ClassWriter#COMPUTE_FRAMES}才会起作用
173 | * {@link ClassReader.EXPAND_FRAMES}:
174 | * 跳过文件的StackMapTable属性.默认的栈图是以原始格式被访问,设置此标识,栈图始终以扩展格式进行访问,大幅度降低性能
175 | *
176 | */
177 | reader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
178 | //然后将内容重新写入该文件中
179 | FileOutputStream fos = new FileOutputStream(output);
180 | fos.write(writer.toByteArray());
181 | fos.close();
182 | } catch (FileNotFoundException e) {
183 | e.printStackTrace();
184 | } catch (IOException e) {
185 | e.printStackTrace();
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/wjplugin/src/main/groovy/com/android/wj/debug/utils/SystemOutPrintln.java:
--------------------------------------------------------------------------------
1 | package com.android.wj.debug.utils;
2 |
3 | /**
4 | * Created by wenjing.liu on 2021/7/19 in J1.
5 | *
6 | * 输出日志信息
7 | *
8 | * @author wenjing.liu
9 | */
10 | public class SystemOutPrintln {
11 | private static final boolean DEBUG = false;
12 | private static String TAG = " ~*~*~*~*~*~* DebugPlugin ~*~*~*~*~*~* ";
13 |
14 | /**
15 | * 前面自动添加tag,输出日志信息
16 | *
17 | * @param info
18 | */
19 | public static void println(String info) {
20 | if (DEBUG) {
21 | System.out.println(String.format("%s %s", TAG, info));
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/wjplugin/src/main/resources/META-INF/gradle-plugins/com.android.wj.debug.properties:
--------------------------------------------------------------------------------
1 | implementation-class = com.android.wj.debug.WjDebugPluginProject
--------------------------------------------------------------------------------