Gradle的構建過程都不會?帶你全面瞭解Android如何自定義Gradle 插件

目前 Android 工程的默認構建工具爲 Gradle,咱們在構建 APK 的時候每每會執行 ./gradlew assembleDebug 這樣的命令。。java

那麼這個命令到底表明着什麼含義呢?命令的執行到底是在作什麼事情呢?咱們能不能在命令執行的過程當中作一些本身的操做呢?接下來咱們來具體的進行分析。android

Gradle 的構建過程

Gradle Wrapper 是個啥

當咱們在 Android Studio 中新建一個工程時,你會發如今工程的根目錄下會建立如下幾個文件:git

在這裏插入圖片描述github

實際上這幾個文件是經過執行 $ Gradle Wrapper 生成的。Gradle Wrapper,顧名思義就是對 Gradle 構建工具的一層封裝。面試

在和其餘同事共同管理某個 Android 工程的時候,確定會存在同事 A 電腦上的 Gradle 版本和同事 B 電腦上的 Gradle 版本不同,那麼這個不同可能致使的問題是須要在 build.gradle 文件中添加不一樣的配置,甚至有的 Gradle 版本都沒法成功跑通工程。bootstrap

因此,Gradle 的工程師們將 Gradle 添加了一層簡單的封裝,Linux 用戶能夠經過執行 gradlew 來代替 gradle 命令,Windows 用戶能夠經過 gradlew.bat 來代替,實際上這倆文件就是個可執行腳本,咱們能夠直接打開這個腳原本看裏面到底有什麼。api

# 只貼上主要代碼 gradlew 文件
...
JAVACMD="java"
...
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
...
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 

從裏面的代碼咱們能夠看到,實際上 gradlew 的腳本也只是執行了一下 Java 命令,assembleDebug 只是執行腳本的參數。咱們修改一下腳本打印出來這個命令:app

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 包到底幹了什麼。dom

// 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 文件的解析和下載工做,咱們先看一下這個文件裏面有什麼:jvm

// 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免費獲取!

相關文章
相關標籤/搜索