目前 Android 工程的默認構建工具為 Gradle,我們在構建 APK 的時候往往會執行 ./gradlew assembleDebug 這樣的命令,,
那么這個命令到底代表著什么含義呢?命令的執行究竟是在做什么事情呢?我們能不能在命令執行的程序中做一些自己的操作呢?接下來我們來具體的進行分析,
Gradle 的構建程序
Gradle Wrapper 是個啥
當我們在 Android Studio 中新建一個工程時,你會發現在工程的根目錄下會創建以下幾個檔案:
在這里插入圖片描述
實際上這幾個檔案是通過執行 $ Gradle Wrapper 生成的,Gradle Wrapper,顧名思義就是對 Gradle 構建工具的一層封裝,
在和其他同事共同管理某個 Android 工程的時候,肯定會存在同事 A 電腦上的 Gradle 版本和同事 B 電腦上的 Gradle 版本不一樣,那么這個不一樣可能導致的問題是需要在 build.gradle 檔案中添加不同的配置,甚至有的 Gradle 版本都無法成功跑通工程,
所以,Gradle 的工程師們將 Gradle 添加了一層簡單的封裝,Linux 用戶可以通過執行 gradlew 來代替 gradle 命令,Windows 用戶可以通過 gradlew.bat 來代替,實際上這倆檔案就是個可執行腳本,我們可以直接打開這個腳本來看里面到底有什么,
# 只貼上主要代碼 gradlew 檔案
...
JAVACMD="java"
...
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
...
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
從里面的代碼我們可以看到,實際上 gradlew 的腳本也只是執行了一下 Java 命令,assembleDebug 只是執行腳本的引數,我們修改一下腳本列印出來這個命令:
java -Xdock:name=Gradle -Xdock:icon=/media/gradle.icns -Dorg.gradle.appname=gradlew -classpath /xxx/food/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain assembleDebug -s
從這里可以看到,腳本所做的事情就是通過 Java 命令來執行 /gradle/wrapper/ 下的 gradle-wrapper.jar 包,通過 JD-GUI 工具打開這個可執行 jar 包,找到那個 jar 包的入口類,也就是 org.gradle.wrapper.GradleWrapperMain,我們大致看一下這個 jar 包到底干了什么,
// org.gradle.wrapper.GradleWrapperMain檔案
public static void main(String[] args) throws Exception {
File wrapperJar = wrapperJar(); // 獲取這個可執行jar包的具體路徑
File propertiesFile = wrapperProperties(wrapperJar); // 獲取gradle-wrapper.properties的位置
File rootDir = rootDir(wrapperJar); // 獲取工程的根目錄
// 下面的一段代碼用來決議命令列,我們暫時不去管它
CommandLineParser parser = new CommandLineParser();
...
WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile);
// 下面這個方法便是決議gradle/wrapper/gradle-wrapper.properties檔案里面的內容,然后根據里面的配置到相應的地址需下載另一個可執行jar包
wrapperExecutor.execute(
args,
new Install(logger, new Download(logger, "gradlew", wrapperVersion()), new PathAssembler(gradleUserHome)),
new BootstrapMainStarter());
}
通過分析 execute() 方法的執行,我們會發現,這個方法執行了 gradle-wrapper.properties 檔案的決議和下載作業,我們先看一下這個檔案里面有什么:
// gradle/wrapper/gradle-wrapper.properties 檔案配置 distributionBase=GRADLE_USER_HOME // GRADLE_USER_HOME的地址默認在用戶目錄下的.gradle/檔案夾里 distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https://services.gradle.org/distributions/gradle-4.4-all.zip
如上所示,代碼會找到 distributionUrl 這個路徑,然后下載相應的檔案,在這里,工程會指定固定的一個 Gradle 版本,然后所有的開發者都會在相同的路徑下下載到相同的 ZIP 包,這樣也就保證了在不同電腦上執行構建操作結果的一致性,下載檔案的程序就不多述了,因為比較簡單,
檔案下載以后,當前就會去執行這個檔案:
// org.gradle.wrapper.BootstrapMainStarter
public class BootstrapMainStarter {
public void start(String[] args, File gradleHome) throws Exception {
File gradleJar = findLauncherJar(gradleHome);
URLClassLoader contextClassLoader = new URLClassLoader(new URL[]{gradleJar.toURI().toURL()}, ClassLoader.getSystemClassLoader().getParent());
Thread.currentThread().setContextClassLoader(contextClassLoader);
// 通過反射找到org.gradle.launcher.GradleMain 進行具體的呼叫
Class<?> mainClass = contextClassLoader.loadClass("org.gradle.launcher.GradleMain");
Method mainMethod = mainClass.getMethod("main", String[].class);
mainMethod.invoke(null, new Object[]{args});
if (contextClassLoader instanceof Closeable) {
((Closeable) contextClassLoader).close();
}
}
}
如上所示,Java 代碼找到下載的 jar 包,然后通過 ClassLoader 加載到記憶體里,加載以后通過反射呼叫里面的入口類,
到此,Gradle Wrapper 的整個呼叫程序結束了,它的功能就是保證多個電腦上能夠以相同的 Gradle 版本構建 Android 的工程代碼,后續的執行則是開啟了 Gradle 的真正構建程序,我們接下來進行分析,
Gradle 的構建生命周期
Gradle 的整個構建程序共分為三個階段:init 初始化階段、config 配置階段和 build 執行階段,下面簡單說一下這三個階段分別做什么作業,
init 初始化階段
初始化階段主要是決議 settings.gradle 檔案,查看該工程引入了多少個 module,如下所示,可以在 settings.gradle 檔案下定義需要引入的 module 和其對應的目錄:
include ':app'
include ':library'
project(':library').projectDir = new File('../library')
config 階段
在 config 階段便是去決議每個 module 里的 build.gradle 檔案,并逐行執行,完成對 project 的配置,并構造 Task 任務依賴關系圖以便在執行階段按照依賴關系執行 Task,
build 執行階段
執行階段便是根據 config 階段生成的 Task 依賴關系圖,來挨個地去執行各個 Task,每個 Task 可以看做是一個功能體,比如說,在構建程序中 Java 檔案需要先轉換為 class 檔案,然后 class 檔案要再次轉換成 dex 檔案,然后 dex 檔案最終組合生成 APK,這個程序中每一步都是由一個 Task 來執行的,后續在介紹自定義 Gradle 插件的時候會講到 Task 相關的東西,
Gradle 構建程序代碼分析
剛才梳理了一下 Gradle 構建程序的生命周期,分為上面那三個階段,那么,具體到代碼是如何實作這三個宣告周期的呢?我們具體進行一下分析,
在 Gradle Wrapper 的末尾,我們提到構建程序走到了通過反射找到 org.gradle.launcher.GradleMain 進行具體的調用,那么我們就繼續跟著原始碼走,
// org.gradle.launcher.GradleMain
public class GradleMain {
public static void main(String[] args) throws Exception {
new ProcessBootstrap().run("org.gradle.launcher.Main", args);
}
}
org.gradle.launcher.GradleMain 是真正執行構建程序的入口類,深入到 new ProcessBootstrap().run() 方法中繼續執行,
private void runNoExit(String mainClassName, String[] args) throws Exception {
ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new DefaultClassPathProvider(new DefaultModuleRegistry(CurrentGradleInstallation.get())));
ClassLoaderFactory classLoaderFactory = new DefaultClassLoaderFactory();
ClassPath antClasspath = classPathRegistry.getClassPath("ANT");
ClassPath runtimeClasspath = classPathRegistry.getClassPath("GRADLE_RUNTIME");
ClassLoader antClassLoader = classLoaderFactory.createIsolatedClassLoader(antClasspath);
// 我們發現通過新建classLoader,在classLoader增加了新的依賴項,但這些依賴項不知道是什么,不過這不是我們關注的重點,我們繼續代碼的執行
ClassLoader runtimeClassLoader = new VisitableURLClassLoader(antClassLoader, runtimeClasspath);
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(runtimeClassLoader);
try {
// 在此處通過反射呼叫了org.gradle.launcher.Main這個類的run方法,
Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName);
Object entryPoint = mainClass.newInstance();
Method mainMethod = mainClass.getMethod("run", String[].class);
mainMethod.invoke(entryPoint, new Object[]{args});
} finally {
...
}
}
主流程是通過反射呼叫了 org.gradle.launcher.Main 這個類的 run 方法,我們具體看一下這段代碼,
// org.gradle.launcher.Main
public class Main extends EntryPoint {
public static void main(String[] args) {
new Main().run(args);
}
protected void doAction(String[] args, ExecutionListener listener) {
UnsupportedJavaRuntimeException.assertUsingVersion("Gradle", JavaVersion.VERSION_1_7);
createActionFactory().convert(Arrays.asList(args)).execute(listener);
}
CommandLineActionFactory createActionFactory() {
return new CommandLineActionFactory();
}
}
org.gradle.launcher.Main 這個類實際上繼承了 org.gradle.launcher.bootstrap.EntryPoint 這個類,而它的 run 方法實際也就是提供了對 Main 中 doAction 方法的回呼,
// org.gradle.launcher.cli.CommandLineActionFactory
public Action<ExecutionListener> convert(List<String> args) {
ServiceRegistry loggingServices = createLoggingServices();
LoggingConfiguration loggingConfiguration = new DefaultLoggingConfiguration();
return new WithLogging(loggingServices,
buildLayoutFactory,
args,
loggingConfiguration,
new ExceptionReportingAction(
new ParseAndBuildAction(loggingServices, args),
new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), loggingConfiguration, clientMetaData())));
}
CommandLineActionFactory.convert 回傳 WithLogging 的實體,然后呼叫了 WithLogging 的 execute 方法,
然后經過各種回呼等,最終呼叫的是 ParseAndBuildAction 的 execute 方法(中間的回呼程序略過,有興趣的同學可以自己查看一下,我們只分析主流程的代碼):
// org.gradle.launcher.cli.CommandLineActionFactory#ParseAndBuildAction
public void execute(ExecutionListener executionListener) {
List<CommandLineAction> actions = new ArrayList<CommandLineAction>();
// BuildInActions 是處理help version 這些命令的
actions.add(new BuiltInActions());
// 如果不是上面的兩條命令列引數,則執行BuildActionsFactory里的引數,
createActionFactories(loggingServices, actions);
CommandLineParser parser = new CommandLineParser();
for (CommandLineAction action : actions) {
action.configureCommandLineParser(parser);
}
Action<? super ExecutionListener> action;
try {
ParsedCommandLine commandLine = parser.parse(args);
action = createAction(actions, parser, commandLine);
} catch (CommandLineArgumentException e) {
action = new CommandLineParseFailureAction(parser, e);
}
action.execute(executionListener);
}
private Action<? super ExecutionListener> createAction(Iterable<CommandLineAction> factories, CommandLineParser parser, ParsedCommandLine commandLine) {
for (CommandLineAction factory : factories) {
// 根據命令列引數選中相關的處理的Action,比如說在gradle引數后面跟著 help 或者version 引數,則選中BuiltInActions
Runnable action = factory.createAction(parser, commandLine);
if (action != null) {
return Actions.toAction(action);
}
}
throw new UnsupportedOperationException("No action factory for specified command-line arguments.");
}
如果在 gradlew 引數后面加 help 或者 version 時,將交給 BuiltInActions 進行處理,其它的則會交給 BuildActionsFactory 類處理,我們直接查看一下 BuildActionsFactory 的執行代碼,
// org.gradle.launcher.cli.BuildActionsFactory
public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
Parameters parameters = parametersConverter.convert(commandLine, new Parameters());
parameters.getStartParameter().setInteractive(ConsoleStateUtil.isInteractive());
parameters.getDaemonParameters().applyDefaultsFor(jvmVersionDetector.getJavaVersion(parameters.getDaemonParameters().getEffectiveJvm()));
// 下面是通過各種不同的引數
if (parameters.getDaemonParameters().isStop()) { /
return stopAllDaemons(parameters.getDaemonParameters(), loggingServices);
}
if (parameters.getDaemonParameters().isStatus()) {
return showDaemonStatus(parameters.getDaemonParameters(), loggingServices);
}
if (parameters.getDaemonParameters().isForeground()) {
DaemonParameters daemonParameters = parameters.getDaemonParameters();
ForegroundDaemonConfiguration conf = new ForegroundDaemonConfiguration(
UUID.randomUUID().toString(), daemonParameters.getBaseDir(), daemonParameters.getIdleTimeout(), daemonParameters.getPeriodicCheckInterval());
return new ForegroundDaemonAction(loggingServices, conf);
}
if (parameters.getDaemonParameters().isEnabled()) {
return runBuildWithDaemon(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
}
if (canUseCurrentProcess(parameters.getDaemonParameters())) {
return runBuildInProcess(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
}
return runBuildInSingleUseDaemon(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
}
后三個方法中都會呼叫到 runBuildAndCloseServices,這也是執行 Gradle 構建的方法,
最終代碼會執行到 RunBuildAction 的 run 方法,
// org.gradle.launcher.cli.RunBuildAction
public void run() {
try {
// 這個executer實際上是 InProcessBuildActionExecuter 的實體
executer.execute(
new ExecuteBuildAction(startParameter),
new DefaultBuildRequestContext(new DefaultBuildRequestMetaData(clientMetaData, startTime), new DefaultBuildCancellationToken(), new NoOpBuildEventConsumer()),
buildActionParameters,
sharedServices);
} finally {
if (stoppable != null) {
stoppable.stop();
}
}
}
最終查看 InProcessBuildActionExecuter 執行的代碼,
public Object execute(BuildAction action, BuildRequestContext buildRequestContext, BuildActionParameters actionParameters, ServiceRegistry contextServices) {
// 最終通過呼叫獲取到了GradleLauncher的實體,他的一個實作類DefaultGradleLauncher
GradleLauncher gradleLauncher = gradleLauncherFactory.newInstance(action.getStartParameter(), buildRequestContext, contextServices);
try {
RootBuildLifecycleListener buildLifecycleListener = contextServices.get(ListenerManager.class).getBroadcaster(RootBuildLifecycleListener.class);
buildLifecycleListener.afterStart();
try {
GradleBuildController buildController = new GradleBuildController(gradleLauncher);
buildActionRunner.run(action, buildController);
return buildController.getResult();
} finally {
buildLifecycleListener.beforeComplete();
}
} finally {
gradleLauncher.stop();
}
}
在 GradleLauncher 的實作類中,我們看到了熟悉的東西:
private enum Stage {
Load, Configure, Build
}
然后代碼便會呼叫到 DefaultGradleLauncher 的 run ()->doBuild () 方法
private BuildResult doBuild(final Stage upTo) {
// TODO:pm Move this to RunAsBuildOperationBuildActionRunner when BuildOperationWorkerRegistry scope is changed
final AtomicReference<BuildResult> buildResult = new AtomicReference<BuildResult>();
WorkerLeaseService workerLeaseService = buildServices.get(WorkerLeaseService.class);
workerLeaseService.withLocks(workerLeaseService.getWorkerLease()).execute(new Runnable() {
@Override
public void run() {
Throwable failure = null;
try {
// 開始構建之前
buildListener.buildStarted(gradle);
// 開始構建
doBuildStages(upTo);
} catch (Throwable t) {
failure = exceptionAnalyser.transform(t);
}
buildResult.set(new BuildResult(upTo.name(), gradle, failure));
// 構建完成之后
buildListener.buildFinished(buildResult.get());
if (failure != null) {
throw new ReportedException(failure);
}
}
});
return buildResult.get();
}
然后我們看一下開始構建時的代碼:
private void doBuildStages(Stage upTo) {
if (stage == Stage.Build) {
throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
}
if (stage == null) {
// Evaluate init scripts
initScriptHandler.executeScripts(gradle);
// 初始化階段,決議Settings.gradle檔案夾
settings = settingsLoader.findAndLoadSettings(gradle);
stage = Stage.Load;
}
if (upTo == Stage.Load) {
return;
}
if (stage == Stage.Load) {
// 配置階段
buildOperationExecutor.run(new ConfigureBuild());
stage = Stage.Configure;
}
if (upTo == Stage.Configure) {
return;
}
stage = Stage.Build;
// 繪制task的依賴樹
buildOperationExecutor.run(new CalculateTaskGraph());
// 執行task,
buildOperationExecutor.run(new ExecuteTasks());
}
到最后一步,感覺所有的努力都沒有白費,終于看到了熟悉的 Gradle 構建的三個階段,原來 Gradle 的構建也是用代碼寫出來的,并沒有想象的那么高深,
Gradle 插件
什么是 Gradle 插件
我們上面講解過,Gradle 的構建程序實際上就是各個 Task 的執行程序,那么這些執行的 Task 從哪里來呢?答案就是從 Gradle 插件里來,
我們發現當我們新建一個 Android 工程時,在 App 這個 module 的 build.gradle 檔案的第一行里會有以下代碼:
apply plugin: 'com.android.application'
這句代碼的作用便是將構建 Android 應用的所有需要 Task 都加載進來了,所以我們看到,Gradle 生命周期的三個階段僅僅是個殼子,如果想構建 Android 工程,那么就用 apply plugin: 'com.android.application' 引入所有的構建 Android 應用所需要的 Task;如果想要構建 Java 工程,那么只需要通過 apply plugin: 'java' 來引入 Java 工程所需要的 Task 便可,
那么知道了 Gradle 插件的強大功能,我們將如何按照自己的需要自定義 Gradle 插件呢?我們下面來進行講解,
自定義 Gradle 插件
我們在自定義 Gradle 插件的時候,需要解決以下問題:
-
問題一:如何自定義一個 Gradle Plugin?
-
問題二:Gradle Plugin 怎么除錯?
-
問題三:Gradle Plugin 的 apply 方法是什么時候觸發的?
下面以實際的例子來介紹如何自定義 Gradle 插件,并對 Gradle 插件進行除錯,在這個實際的例子中,Gradle 插件的定義和使用分別在兩個不同的工程中,這樣定義出來的 plugin 能夠供外部使用,
問題一:自定義一個插件
新建兩個工程 CustomPlugin、GradleProject,前者是定義插件的地方,后者是使用插件的地方,我們先定義插件,
下面是完成插件自定義以后的目錄結構,我們先來一個總覽,
在這里插入圖片描述
新建一個 module,洗掉里面的所有檔案,然后新建成如上圖所示的目錄結構,其中 MyCustomPlugin 是定義的插件類,而 mycustomplugin.properties 是配置的插件屬性,
首先在 build.gradle 檔案中添加如下代碼,
// 應用另外兩個插件
apply plugin:"groovy"
apply plugin: "maven"
dependencies {
// 使用gradle的api
compile gradleApi()
// 使用groovy的api
compile localGroovy()
}
repositories {
// 下載api相關檔案的倉庫
mavenCentral()
}
添加以后點擊 Sync Project with Gradle 按鈕,就是這個:
在這里插入圖片描述
然后 Android Studio 就會識別出 groovy 檔案夾,groovy 檔案夾就變成了藍色,
然后在 MyCustomPlugin.groovy 添加如下代碼,在 apply 方法中具體執行我們想要這個插件去做的事情,
class MyCustomPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("start mycustomplugin")
println(project.name)
}
}
最后一步,在 mycustomplugin.properties 檔案中添加如下代碼,用來指明插件的處理類:
implementation-class=com.dianping.myplugin.MyCustomPlugin
其中 mycustomplugin.properties 中的 mycustomplugin,代表著這個插件在使用時的名稱,例如,使用時就是 apply plugin:'mycustomplugin’,使用方通過名稱找到這個插件的組態檔,然后根據組態檔找到這個插件具體執行的類,
到此,自定義一個插件的基本作業就完成了,下面就講一講如何使用,
如何使用
打包
在 myplugin 這個 module 中的 build.gradle 檔案中添加一些代碼,添加后整個代碼結構如下,
apply plugin:"groovy"
apply plugin: "maven"
dependencies {
compile gradleApi()
compile localGroovy()
}
repositories {
mavenCentral()
}
// 此處為新添加的代碼
// 定義組
group='com.dianping.myplugin'
//定義版本
version='1.0.0'
uploadArchives {
repositories {
mavenDeployer {
// 定義插件打包后上傳的位置,可以隨意指定,但是在使用時需要指定同樣的檔案才能找到
repository(url: uri('../../repo'))
}
}
}
添加完成后再次點擊:
在這里插入圖片描述
然后會在 Gradle project 面板中出現 uploadArchives 的 Task,
在這里插入圖片描述
雙擊它,就會在 …/…/repo 目錄下出現相關檔案,
使用
在 GradleProject 的根級別的 build.gradle 中 buildscript 節點上添加代碼,添加完后如下所示:
buildscript {
repositories {
google()
jcenter()
// 添加的代碼
maven {
url uri('../repo') // 指定路徑,這個路徑和上面的生成路徑是一致的
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
// 添加的代碼,myplugin是上面定義的時候module的名稱,com.dianping.myplugin是group,
classpath 'com.dianping.myplugin:myplugin:1.0.0'
}
}
最后在 app 這個 module 中添加插件使用,
apply plugin: 'mycustomplugin'
添加完后執行 ./gradlew :app:assembleDebug 就能看到列印結果:
start mycustomplugin app
至此,使用上也講完了,下面該講一講如何除錯了,
問題二:如何除錯
在剛才的 CustomPlugin 工程下在選單欄中選擇 Run >> Edit Configurations,然后點擊 remote,新建遠程除錯,其他東西都不用改,直接點擊 OK 就行,
在這里插入圖片描述
新建完成以后再 GradleProject 中運行如下命令:
./gradlew :app:assembleDebug -Dorg.gradle.debug=true
然后在 CustomPlugin 需要斷點的地方打上斷點,點擊下面紅框里的按鈕,啟動除錯,斷點處就會終止執行,
在這里插入圖片描述
至此,插件的開發也能夠除錯了,
問題三:apply 方法什么時候執行
Gradle 構建的程序總共分為三個階段:初始化階段、配置階段、運行階段,初始化階段是執行 settings.gradle 檔案中的內容,看看這個 Project 需要構建哪幾個 module,在配置階段是從根 Project 依次遍歷 module,并為每個 module 生成一個 Project 物件,配置階段完成時就形成了一個完整的 Task 依賴圖,然后就是執行階段執行相關的 Task,
那么 apply 方法是什么時候執行的呢?是在配置階段遇到 apply plugin:'mycustomplugin’ 就開始執行,我們可以在前后打 log 來驗證,結果和預期一樣,apply 方法中傳入的 Project 物件就是某個使用該插件的 Project 的物件,
println 'before' apply plugin: 'mycustomplugin' println 'after'
非獨立工程定義和使用插件
如果想要在自己的工程里面使用 Gradle 插件,那么更加簡單,
新建一個 Project,叫做 PluginDemo, 在 app 的 build.gradle 中寫上如下代碼:
class ApkDistPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task("apkdist") << {
println 'hello world'
}
}
}
apply plugin: ApkDistPlugin
命令列輸入:
./gradlew -q -p app/ apkdist
// 輸出結果為:
hello world
讓插件是可以配置的
大多數插件都需要在 build script 中獲取到一定的配置資訊,其中一個方法就是通過 Extension 類來進行,Project 類中持有了 ExtensionContainer 物件,包含了對這個 Project 所有的配置,那么我們就可以通過它來添加我們自己的配置,下面是一個例子,
class ApkDistExtension {
Closure nameMap = null
String destDir = null
}
class GreetingPluginExtension {
String message = null
}
class ApkDistPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.extensions.create("apkdistconf",ApkDistExtension)
def extension = project.extensions.create("greet",GreetingPluginExtension)
project.task("apkdist") << {
def closure = project['apkdistconf'].nameMap
closure('hello world closure')
println 'hello world'
println project['apkdistconf'].destDir
println extension.message
}
}
}
apply plugin: ApkDistPlugin
apkdistconf {
nameMap { name ->
println "$name haha"
}
destDir 'heiheihei'
}
greet.message = "greet"
下面是運行結果
// 執行的命令 ./gradlew -q -p app/ apkdist // 運行的結果 hello world closure haha hello world heiheihei greet
現在都說互聯網寒冬,其實只要自身技術能力夠強,咱們就不怕!我這邊專門針對Android開發工程師整理了一套【Android進階學習視頻】、【全套Android面試秘籍】、【Android知識點PDF】,如有需要獲取資料檔案的朋友,可以點擊我GitHub免費獲取!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/106817.html
標籤:其他
上一篇:Sagit.Framework For IOS 開發框架入門教程16:螢屏旋轉、螢屏強制旋轉功能。
下一篇:移動端初級相關解決方案
