Android Gradle插件中,包含了一些task可以幫我們做一些編譯、引入依賴、打包等工作,比如assembleBuild,clean等等??梢允褂枚喾N語言來實(shí)現(xiàn)Gradle插件,其實(shí)只要最終被編譯為JVM字節(jié)碼的都可以,常用的有Groovy、Java、Kotlin。
自定義gradle插件的官方網(wǎng)址
比如在模塊的build.gradle下需要引入的插件,這兩個(gè)插件就是兩個(gè)java程序。
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
什么是 Gradle 插件
Gradle 和 Gradle 插件是兩個(gè)完全不同的概念,Gradle 提供的是一套核心的構(gòu)建機(jī)制,而 Gradle 插件則是運(yùn)行在這套機(jī)制上的一些具體構(gòu)建邏輯,本質(zhì)上和 .gradle 文件是相同。例如,我們熟悉的編譯 Java 代碼的能力,都是由插件提供的。
Gradle 插件的優(yōu)點(diǎn)
Gradle 插件使用了獨(dú)立模塊封裝構(gòu)建邏輯,無論是從開發(fā)開始使用來看,Gradle 插件的整體體驗(yàn)都更友好。
1.邏輯復(fù)用: 將相同的邏輯提供給多個(gè)相似項(xiàng)目復(fù)用,減少重復(fù)維護(hù)類似邏輯開銷。當(dāng)然 .gradle 文件也能做到邏輯復(fù)用,但 Gradle 插件的封裝性更好;
2.組件發(fā)布: 可以將插件發(fā)布到 Maven 倉庫進(jìn)行管理,其他項(xiàng)目可以使用插件 ID 依賴。當(dāng)然 .gradle 文件也可以放到一個(gè)遠(yuǎn)程路徑被其他項(xiàng)目引用;
3.構(gòu)建配置: Gradle 插件可以聲明插件擴(kuò)展來暴露可配置的屬性,提供定制化能力。當(dāng)然 .gradle 文件也可以做到,但實(shí)現(xiàn)會(huì)麻煩些。
Gradle 插件的核心類是 Plugin,一般使用 Project 作為泛型實(shí)參。當(dāng)使用方引入插件后,其實(shí)就是調(diào)用了 Plugin#apply() 方法,我們可以把 apply() 方法理解為插件的執(zhí)行入口。例如:
public class MyPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("This is my plugin");
}
}
應(yīng)用插件的步驟
1、將插件添加到 classpath: 將插件添加到構(gòu)建腳本的 classpath 中,我們的 Gradle 構(gòu)建腳本才能應(yīng)用插件。這里區(qū)分本地依賴和遠(yuǎn)程依賴兩種情況。
本地依賴: 指直接依賴本地插件源碼,一般在調(diào)試插件的階段是使用本地依賴的方式。例如:
buildscript {
...
dependencies {
// For Debug
classpath project(":myplugin")
}
}
遠(yuǎn)程依賴: 指依賴已發(fā)布到 Maven 倉庫的插件,一般我們都是用這種方式依賴官方或第三方實(shí)現(xiàn)的 Gradle 插件。例如:
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30"
}
...
}
2、使用 apply 應(yīng)用插件: 在需要使用插件的 .gradle 腳本中使用 apply 應(yīng)用插件,這將創(chuàng)建一個(gè)新的 Plugin 實(shí)例,并執(zhí)行 Plugin#apply() 方法。例如:
apply plugin: 'com.android.application'
// 或者
plugins {
id 'com.android.application'
}
開發(fā)Gradle插件有3種方式,如下:

以下示例Demo Github下載地址:
CreateGradlePlugin
1.直接引用插件
創(chuàng)建java模塊,寫一個(gè)自定義Plugin
public class MyPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("This is my plugin");
}
}
在引入模塊下比如app模塊下,引用該P(yáng)lugin
import com.github.buildsrc.MyPlugin
apply plugin: MyPlugin

2.通過特殊的 buildSrc 模塊寫插件
插件模塊的名稱是任意的,除非使用了一個(gè)特殊的名稱 “buildSrc”,buildSrc 模塊是 Gradle 默認(rèn)的插件模塊。buildSrc 模塊本質(zhì)上和普通的插件模塊是一樣的,有一些小區(qū)別:
1、buildSrc 模塊會(huì)被自動(dòng)識(shí)別為參與構(gòu)建的模塊,因此不需要在 settings.gradle 中使用 include 引入,就算引入了也會(huì)編譯出錯(cuò)
2、buildSrc 模塊會(huì)自動(dòng)被添加到構(gòu)建腳本的 classpath 中,不需要手動(dòng)添加
3、buildSrc 模塊的 build.gradle 執(zhí)行時(shí)機(jī)早于其他 Project
開發(fā)Gradle插件入門示例:
使用buildSrc目錄方法。
1.在項(xiàng)目下創(chuàng)建Directory,命名為buildSrc,然后創(chuàng)建目錄src/main/java。
2.同步一下項(xiàng)目,會(huì)在該目錄下生成build目錄。

3.在java目錄下創(chuàng)建包,并創(chuàng)建一個(gè)自己的插件類,比如命名BuildSrcPlugin。
class BuildSrcPlugin implements Plugin<Project> {
@Override
public void apply(Project target) {
System.out.println("This is buildSrc plugin");
}
}
在buildSrc目錄下寫代碼特殊性:
1.它是提供給各個(gè)模塊guild.gradle使用的
2.這個(gè)模塊默認(rèn)會(huì)有g(shù)radle所需庫
3.這個(gè)模塊不需要setting.gradle去引用
在模塊 build.gradle 文件中增加以下配置,gradlePlugin 定義了插件 ID 和插件實(shí)現(xiàn)類的映射關(guān)系:
gradlePlugin {
plugins {
buildsrc {
// Plugin id.
id = 'com.github.buildsrc'
// Plugin implementation.
implementationClass = 'com.github.buildsrc.BuildSrcPlugin'
}
}
}
這其實(shí)是 Java Gradle Plugin 提供的一個(gè)簡化 API,其背后會(huì)自動(dòng)幫我們創(chuàng)建一個(gè) [插件ID].properties 配置文件,Gradle 就是通過這個(gè)文件類進(jìn)行匹配的。如果你不使用 gradlePlugin API,直接手動(dòng)創(chuàng)建 [插件ID].properties 文件,作用是完全一樣的。

properties內(nèi)容:
implementation-class=com.github.customplugin.CustomPlugin
4.使用該插件,在需要引入插件的build文件中引入:
plugins {
id 'com.github.buildsrc' //引用buildSrc下聲明的插件
}
在執(zhí)行build文件時(shí),執(zhí)行該行代碼,會(huì)加載該類,去執(zhí)行BuildSrcPlugin的apply方法。
打印apply方法里的文本:

3.獨(dú)立模塊發(fā)布插件
1.新建Java module

2.在build.gradle中應(yīng)用gradle插件:
plugins {
id 'java-gradle-plugin'
}
寫一個(gè)Plugin類
public class CustomPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("This is a custom plugin");
}
}
聲明一個(gè)插件id以及對(duì)應(yīng)Plugin類:
gradlePlugin {
plugins {
customplugin {
// Plugin id.
id = 'com.github.customplugin'
// Plugin implementation.
implementationClass = 'com.github.customplugin.CustomPlugin'
}
}
}
定義發(fā)布代碼到本地maven-push配置:
apply plugin: 'maven-publish'
publishing {
publications{
maven(MavenPublication) {
groupId "com.github.custom"
artifactId 'CustomPlugin'
version "1.0.0"
//如果是war包填寫components.web,如果是jar包填寫components.java
from components.java
}
}
repositories {
maven {
url = "../repo"
}
}
}
在task任務(wù)中使用pushing發(fā)布jar包到本地:

在本地生成jar包倉庫:

添加該插件classpath:
buildscript {
dependencies {
classpath "com.github.custom:CustomPlugin:1.0.0"
}
}
在模塊級(jí) build.gradle 文件中 apply 插件:
plugins {
id 'com.github.customplugin'
}
CustomPlugin的apply方法執(zhí)行:

我們能做哪些gradle插件
- Apk防破解插件:
利用破解之后打包的簽名與我們自己的包簽名不相同來處理,這里RuntimeException的拋出也可以用字節(jié)碼插樁,將崩潰處理的代碼插到啟動(dòng)activity里,讓破解的包無法再正常運(yùn)行。
public class AntiCrackPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
TaskProvider<Jar> jarTask = project.getTasks().named("jar", Jar.class);
// 在打包任務(wù)之前執(zhí)行防破解邏輯
jarTask.configure(task -> {
task.doFirst("AntiCrack", t -> {
System.out.println("Running anti-crack checks...");
// 添加你的防破解邏輯,例如檢查簽名、防止二次打包等
// 這里只是一個(gè)簡單的示例,實(shí)際應(yīng)用需要更加復(fù)雜的邏輯
if (isCracked(project)) {
throw new RuntimeException("The application has been cracked!");
}
});
});
}
private boolean isCracked(Project project) {
// 添加你的防破解邏輯,例如檢查簽名、防止二次打包等
// 這里只是一個(gè)簡單的示例,實(shí)際應(yīng)用需要更加復(fù)雜的邏輯
// 獲取應(yīng)用的簽名信息
String expectedSignature = "your_expected_signature"; // 期望的簽名信息,可以從安全的渠道獲取
try {
JarFile jarFile = new JarFile(project.getTasks().getByPath("jar").getOutputs().getFiles().getSingleFile());
Certificate[] certificates = jarFile.getJarEntry("META-INF/MANIFEST.MF").getCodeSigners()[0].getSignerCertPath().getCertificates();
StringBuilder actualSignature = new StringBuilder();
for (Certificate certificate : certificates) {
actualSignature.append(certificate.getPublicKey().toString());
}
// 比較實(shí)際簽名和期望簽名
return !actualSignature.toString().equals(expectedSignature);
} catch (Exception e) {
e.printStackTrace();
return true; // 發(fā)生異常時(shí)可能是被篡改過的APK,視為破解
}
}
}
- 隱私合規(guī)處理的gradle插件
在Android應(yīng)用中處理隱私合規(guī)性是非常重要的,特別是在涉及用戶隱私信息的處理方面。首先,讓我們考慮一些隱私合規(guī)性的基本要求:
權(quán)限檢查: 確保應(yīng)用只請(qǐng)求和使用了必要的權(quán)限。
隱私政策鏈接: 確保應(yīng)用中包含隱私政策鏈接,并提供用戶訪問的方式。
數(shù)據(jù)收集通知: 如果應(yīng)用收集用戶數(shù)據(jù),確保提供了適當(dāng)?shù)耐ㄖ陀脩敉狻?/strong>
我們可以創(chuàng)建一個(gè)Gradle插件,執(zhí)行這些檢查:
public class PrivacyCompliancePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
TaskProvider<PrivacyComplianceCheckTask> privacyCheckTask = project.getTasks().register("privacyCheck", PrivacyComplianceCheckTask.class);
project.afterEvaluate(p -> {
// 在構(gòu)建前執(zhí)行隱私合規(guī)性檢查
project.getTasks().getByName("assemble").dependsOn(privacyCheckTask);
});
}
}
然后,創(chuàng)建一個(gè)任務(wù)用于執(zhí)行實(shí)際的隱私合規(guī)性檢查:
public class PrivacyComplianceCheckTask extends DefaultTask {
@TaskAction
public void checkPrivacyCompliance() {
// 添加你的隱私合規(guī)性檢查邏輯
checkPermissions();
checkPrivacyPolicyLink();
checkDataCollectionNotice();
}
private void checkPermissions() {
System.out.println("Checking permissions...");
List<String> requiredPermissions = Arrays.asList(
"android.permission.CAMERA",
"android.permission.WRITE_EXTERNAL_STORAGE",
// 添加其他需要的權(quán)限
);
Set<String> missingPermissions = new HashSet<>(requiredPermissions);
missingPermissions.removeAll(getDeclaredPermissions());
if (!missingPermissions.isEmpty()) {
throw new RuntimeException("Missing required permissions: " + missingPermissions);
}
}
private Set<String> getDeclaredPermissions() {
AndroidManifest androidManifest = new AndroidManifest(getProject().file("src/main/AndroidManifest.xml"));
return androidManifest.getPermissions();
}
private void checkPrivacyPolicyLink() {
System.out.println("Checking privacy policy link...");
String privacyPolicyLink = getPrivacyPolicyLink();
if (privacyPolicyLink == null || privacyPolicyLink.isEmpty()) {
throw new RuntimeException("Privacy policy link is missing or empty.");
}
}
private String getPrivacyPolicyLink() {
// 在這里,你可以通過讀取資源文件或其他配置方式獲取隱私政策鏈接
return "https://www.example.com/privacy-policy";
}
private void checkDataCollectionNotice() {
System.out.println("Checking data collection notice...");
boolean dataCollectionEnabled = isDataCollectionEnabled();
if (!dataCollectionEnabled) {
throw new RuntimeException("Data collection is not enabled.");
}
}
private boolean isDataCollectionEnabled() {
// 在這里,你可以通過讀取應(yīng)用的配置文件或其他方式獲取數(shù)據(jù)收集的狀態(tài)
return true; // 假設(shè)數(shù)據(jù)收集是啟用的
}
}
參考:
https://blog.csdn.net/wumeixinjiazu/article/details/124691763
https://segmentfault.com/a/1190000041856822