Android Gradle插件中,包含了一些task可以帮我们做一些编译、引入依赖、打包等工作,比如assembleBuild,clean等等。可以使用多种语言来实现Gradle插件,其实只要最终被编译为JVM字节码的都可以,常用的有Groovy、Java、Kotlin。
自定义gradle插件的官方网址
比如在模块的build.gradle下需要引入的插件,这两个插件就是两个java程序。
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
什么是 Gradle 插件
Gradle 和 Gradle 插件是两个完全不同的概念,Gradle 提供的是一套核心的构建机制,而 Gradle 插件则是运行在这套机制上的一些具体构建逻辑,本质上和 .gradle 文件是相同。例如,我们熟悉的编译 Java 代码的能力,都是由插件提供的。
Gradle 插件的优点
Gradle 插件使用了独立模块封装构建逻辑,无论是从开发开始使用来看,Gradle 插件的整体体验都更友好。
1.逻辑复用: 将相同的逻辑提供给多个相似项目复用,减少重复维护类似逻辑开销。当然 .gradle 文件也能做到逻辑复用,但 Gradle 插件的封装性更好;
2.组件发布: 可以将插件发布到 Maven 仓库进行管理,其他项目可以使用插件 ID 依赖。当然 .gradle 文件也可以放到一个远程路径被其他项目引用;
3.构建配置: Gradle 插件可以声明插件扩展来暴露可配置的属性,提供定制化能力。当然 .gradle 文件也可以做到,但实现会麻烦些。
Gradle 插件的核心类是 Plugin,一般使用 Project 作为泛型实参。当使用方引入插件后,其实就是调用了 Plugin#apply() 方法,我们可以把 apply() 方法理解为插件的执行入口。例如:
public class MyPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("This is my plugin");
}
}
应用插件的步骤
1、将插件添加到 classpath: 将插件添加到构建脚本的 classpath 中,我们的 Gradle 构建脚本才能应用插件。这里区分本地依赖和远程依赖两种情况。
本地依赖: 指直接依赖本地插件源码,一般在调试插件的阶段是使用本地依赖的方式。例如:
buildscript {
...
dependencies {
// For Debug
classpath project(":myplugin")
}
}
远程依赖: 指依赖已发布到 Maven 仓库的插件,一般我们都是用这种方式依赖官方或第三方实现的 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 应用插件: 在需要使用插件的 .gradle 脚本中使用 apply 应用插件,这将创建一个新的 Plugin 实例,并执行 Plugin#apply() 方法。例如:
apply plugin: 'com.android.application'
// 或者
plugins {
id 'com.android.application'
}
开发Gradle插件有3种方式,如下:
以下示例Demo Github下载地址:
CreateGradlePlugin
1.直接引用插件
创建java模块,写一个自定义Plugin
public class MyPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("This is my plugin");
}
}
在引入模块下比如app模块下,引用该Plugin
import com.github.buildsrc.MyPlugin
apply plugin: MyPlugin
2.通过特殊的 buildSrc 模块写插件
插件模块的名称是任意的,除非使用了一个特殊的名称 “buildSrc”,buildSrc 模块是 Gradle 默认的插件模块。buildSrc 模块本质上和普通的插件模块是一样的,有一些小区别:
1、buildSrc 模块会被自动识别为参与构建的模块,因此不需要在 settings.gradle 中使用 include 引入,就算引入了也会编译出错
2、buildSrc 模块会自动被添加到构建脚本的 classpath 中,不需要手动添加
3、buildSrc 模块的 build.gradle 执行时机早于其他 Project
开发Gradle插件入门示例:
使用buildSrc目录方法。
1.在项目下创建Directory,命名为buildSrc,然后创建目录src/main/java。
2.同步一下项目,会在该目录下生成build目录。
3.在java目录下创建包,并创建一个自己的插件类,比如命名BuildSrcPlugin。
class BuildSrcPlugin implements Plugin<Project> {
@Override
public void apply(Project target) {
System.out.println("This is buildSrc plugin");
}
}
在buildSrc目录下写代码特殊性:
1.它是提供给各个模块guild.gradle使用的
2.这个模块默认会有gradle所需库
3.这个模块不需要setting.gradle去引用
在模块 build.gradle 文件中增加以下配置,gradlePlugin 定义了插件 ID 和插件实现类的映射关系:
gradlePlugin {
plugins {
buildsrc {
// Plugin id.
id = 'com.github.buildsrc'
// Plugin implementation.
implementationClass = 'com.github.buildsrc.BuildSrcPlugin'
}
}
}
这其实是 Java Gradle Plugin 提供的一个简化 API,其背后会自动帮我们创建一个 [插件ID].properties 配置文件,Gradle 就是通过这个文件类进行匹配的。如果你不使用 gradlePlugin API,直接手动创建 [插件ID].properties 文件,作用是完全一样的。
properties内容:
implementation-class=com.github.customplugin.CustomPlugin
4.使用该插件,在需要引入插件的build文件中引入:
plugins {
id 'com.github.buildsrc' //引用buildSrc下声明的插件
}
在执行build文件时,执行该行代码,会加载该类,去执行BuildSrcPlugin的apply方法。
打印apply方法里的文本:
3.独立模块发布插件
1.新建Java module
2.在build.gradle中应用gradle插件:
plugins {
id 'java-gradle-plugin'
}
写一个Plugin类
public class CustomPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("This is a custom plugin");
}
}
声明一个插件id以及对应Plugin类:
gradlePlugin {
plugins {
customplugin {
// Plugin id.
id = 'com.github.customplugin'
// Plugin implementation.
implementationClass = 'com.github.customplugin.CustomPlugin'
}
}
}
定义发布代码到本地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任务中使用pushing发布jar包到本地:
在本地生成jar包仓库:
添加该插件classpath:
buildscript {
dependencies {
classpath "com.github.custom:CustomPlugin:1.0.0"
}
}
在模块级 build.gradle 文件中 apply 插件:
plugins {
id 'com.github.customplugin'
}
CustomPlugin的apply方法执行:
我们能做哪些gradle插件
- Apk防破解插件:
利用破解之后打包的签名与我们自己的包签名不相同来处理,这里RuntimeException的抛出也可以用字节码插桩,将崩溃处理的代码插到启动activity里,让破解的包无法再正常运行。
public class AntiCrackPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
TaskProvider<Jar> jarTask = project.getTasks().named("jar", Jar.class);
// 在打包任务之前执行防破解逻辑
jarTask.configure(task -> {
task.doFirst("AntiCrack", t -> {
System.out.println("Running anti-crack checks...");
// 添加你的防破解逻辑,例如检查签名、防止二次打包等
// 这里只是一个简单的示例,实际应用需要更加复杂的逻辑
if (isCracked(project)) {
throw new RuntimeException("The application has been cracked!");
}
});
});
}
private boolean isCracked(Project project) {
// 添加你的防破解逻辑,例如检查签名、防止二次打包等
// 这里只是一个简单的示例,实际应用需要更加复杂的逻辑
// 获取应用的签名信息
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());
}
// 比较实际签名和期望签名
return !actualSignature.toString().equals(expectedSignature);
} catch (Exception e) {
e.printStackTrace();
return true; // 发生异常时可能是被篡改过的APK,视为破解
}
}
}
- 隐私合规处理的gradle插件
在Android应用中处理隐私合规性是非常重要的,特别是在涉及用户隐私信息的处理方面。首先,让我们考虑一些隐私合规性的基本要求:
权限检查: 确保应用只请求和使用了必要的权限。
隐私政策链接: 确保应用中包含隐私政策链接,并提供用户访问的方式。
数据收集通知: 如果应用收集用户数据,确保提供了适当的通知和用户同意。
我们可以创建一个Gradle插件,执行这些检查:
public class PrivacyCompliancePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
TaskProvider<PrivacyComplianceCheckTask> privacyCheckTask = project.getTasks().register("privacyCheck", PrivacyComplianceCheckTask.class);
project.afterEvaluate(p -> {
// 在构建前执行隐私合规性检查
project.getTasks().getByName("assemble").dependsOn(privacyCheckTask);
});
}
}
然后,创建一个任务用于执行实际的隐私合规性检查:
public class PrivacyComplianceCheckTask extends DefaultTask {
@TaskAction
public void checkPrivacyCompliance() {
// 添加你的隐私合规性检查逻辑
checkPermissions();
checkPrivacyPolicyLink();
checkDataCollectionNotice();
}
private void checkPermissions() {
System.out.println("Checking permissions...");
List<String> requiredPermissions = Arrays.asList(
"android.permission.CAMERA",
"android.permission.WRITE_EXTERNAL_STORAGE",
// 添加其他需要的权限
);
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() {
// 在这里,你可以通过读取应用的配置文件或其他方式获取数据收集的状态
return true; // 假设数据收集是启用的
}
}
参考:
https://blog.csdn.net/wumeixinjiazu/article/details/124691763
https://segmentfault.com/a/1190000041856822