Android gradle task types

Задачи Gradle

Задача (task) является основным компонентом процесса сборки в файле build.gradle. Задачи представляют собой именованные наборы инструкций, которые Gradle запускает, выполняя сборку приложения. Задачи Gradle являются полнофункциональными объектами, которыми вы можете управлять программно.

Объявление задач

Запустите любой ваш проект в Android Studio для дальнейших опытов. Откройте файл build.gradle, который относится к модулю app.

Простой способ создания задачи — указать имя задачи. В конце открытого файла добавляем строчку:

После любого изменения файла синхронизуруйтесь.

Мы объявили задачу по одному только имени. На правой стороне Android Studio имеется вертикальная вкладка Gradle, которую можно развернуть. Она содержит список задач (task), которая выполняет Gradle при работе с текущим проектом. Вы можете выделить любую из этих задач и запустить её двойным щелчком. Можно выделить несколько задач.

Найдите свою задачу, она будет находиться по пути :app | Tasks | other. Задачи сортируются по алфавиту. Запустите задачу двойным щелчком.

Когда выполняется какая-то задача Gradle, то ход её выполнения можно увидеть в окне Gradle Console. Открыть её можно через вкладку Gradle Console в нижней правой части студии. После выполнения задачи вы увидите что-то типа такого:

Теперь, поняв, как создаются и запускаются задачи, продолжим эксперименты.

Операция задачи (Task Action)

Выполнение задачи не произведёт никакого результата, поскольку мы не присвоили ей ни одной операции (action). Операцию можно присвоить используя оператор сдвиг влево. Перепишем пример:

Источник

Шпаргалка по Gradle

Как мне кажется, большинство людей начинают разбираться с gradle только тогда, когда в проекте что-то надо добавить или что-то внезапно ломается — и после решения проблемы «нажитый непосильным трудом» опыт благополучно забывается. Причём многие примеры в интернете похожи на ускоспециализированные заклинания, не добавляющие понимания происходящего:

Я не собираюсь подробно описывать, для чего нужна каждая строчка выше — это частные детали реализации андроид-плагина. Есть кое-что более ценное — понимание того, как всё организовано. Информация раскидана по различным сайтам/официальной документации/исходникам градла и плагинов к нему — в общем, это чуть более универсальное знание, которое не хочется забывать.

Дальнейший текст можно рассматривать как шпаргалку для тех, кто только осваивает gradle или уже забыл.

Полезные ссылки

Консоль

Android studio/IDEA старательно прячет команды gradle от разработчика, а ещё при изменении build.gradle файликов начинает тупить или перезагружать проект.

В таких случаях вызывать gradle из консоли оказывается намного проще и быстрее. Враппер для gradle обычно идёт вместе с проектом и прекрасно работает в linux/macos/windows, разве что в последнем надо вызывать bat-файлик вместо враппера.

Вызов задач

пишет доступные задачи.

Можно вывести задачи отдельного подпроекта, а ещё с опцией —all будут выведены все задачи, включая второстепенные.

Можно вызвать любую задачу, при этом будут вызваны все задачи, от которых она зависит.

Если лень писать название целиком, можно выкинуть маленькие буковки:

Если градл не сможет однозначно угадать, какую именно задачу имели ввиду, то выведет список подходящих вариантов.

Логгинг

Количество выводимой в консоль информации при запуске задачи сильно зависит от уровня логгинга.
Кроме дефолтного есть -q, -w, -i, -d , ну или —quiet, —warn, —info, —debug по возрастанию количества информации. На сложных проектах вывод с -d может занимать больше мегабайта, а поэтому его лучше сразу сохранять в файл и там уже смотреть поиском по ключевым словам:

Если где-то кидается исключение, для stacktrace опция -s .

Можно и самому писать в лог:

логгер является имплементацией SLF4J.

Groovy

Происходящее в build.gradle файликах — просто код на groovy.

Groovy как язык программирования почему-то не очень популярен, хотя, как мне кажется, он сам по себе достоин хотя бы небольшого изучения. Язык появился на свет ещё в 2003 году и потихоньку развивался. Интересные особенности:

  • Практически любой java код является валидным кодом на groovy. Это очень помогает интуитивно писать работающий код.
  • Одновременно вместе со статической, в груви поддерживается динамическая типизация, вместо String a = «a» можно смело писать def a = «a» или даже def map = [‘one’:1, ‘two’:2, ‘list’ = [1,false]]
  • Есть замыкания, для которых можно динамически определить контекст исполнения. Те самые блоки android <. >принимают замыкания и потом исполняют их для какого-то объекта.
  • Есть интерполяция строк «$a, $» , multiline-строки «»»yep, $«»» , а обычные java-строки обрамляются одинарными кавычками: ‘text’
  • Есть подобие extension-методов. В стандартной коллекции языка уже есть методы типа any, every, each, findAll. Лично мне названия методов кажутся непривычными, но главное что они есть.
  • Вкусный синтаксический сахар, код становится намного короче и проще. Можно не писать скобки вокруг аргументов функции, для объявления списков и хеш-табличек приятный синтаксис: [a,b,c], Android gradle task types

В общем, почему языки типа Python/Javascript взлетели, а Groovy нет — для меня загадка. Для своего времени, когда в java даже лямбд не было, а альтернативы типа kotlin/scala только-только появлялись или ещё не существовали, Groovy должен был выглядеть реально интересным языком.

Именно гибкость синтаксиса groovy и динамическая типизация позволила в gradle создавать лаконичные DSL.

Сейчас в официальной документации Gradle примеры продублированы на Kotlin, и вроде как планируется переходить на него, но код уже не выглядит таким простым и становится больше похожим на обычный код:

Впрочем, переименование в Kradle пока не планируется.

Стадии сборки

Их делят на инициализацию, конфигурацию и выполнение.

Идея состоит в том, что gradle собирает ациклический граф зависимостей и вызывает только необходимый минимум их них. Если я правильно понял, стадия инициализации происходит в тот момент, когда исполняется код из build.gradle.

Возможно, это неочевидно, но вышеуказанное будет тормозить инициализацию. Чтобы не заниматься копированием файлов при каждой инициализации, нужно в задаче использоваль блок doLast <. >или doFirst <. >— тогда код завернётся в замыкание и его позовут в момент выполнения задачи.

В старых примерах вместо doLast можно встретить оператор , но от него потом отказались из-за неочевидности поведения.

tasks.all

Что забавно, с помощью doLast и doFirst можно навешивать какие-то действия на любые задачи:

IDE подсказывает, что у tasks есть метод whenTaskAdded(Closure . ) , но метод all(Closure . ) работает намного интереснее — замыкание вызывается для всех существующих задач, а так же на новых задачах при их добавлении.

Создадим задачу, которая распечатает зависимости всех задач:

Если tasks.all<> вызвать во время выполнения (в блоке doLast ), то мы увидим все задачи и зависимости.
Если сделать то же самое без doLast (т.е., во время инициализации), то у распечатанных задач может не хватать зависимостей, так как они ещё не были добавлены.

Ах да, зависимости! Если другая задача должна зависеть от результатов выполнения нашей, то стоит добавить зависимость:

inputs, outputs и инкрементальная сборка

Обычная задача будет вызываться каждый раз. Если указать, что задача на основе файла А генерирует файл Б, то gradle будет пропускать задачу, если эти файлы не изменились. Причём gradle проверяет не дату изменения файла, а именно его содержимое.

Аналогично можно указать папки, а так же какие-то значения: inputs.property(name, value) .

task description

При вызове ./gradlew tasks —all стандартные задачи имеют красивое описание и как-то сгруппированы. Для своих задач это добавляется очень просто:

task.enabled

можно «выключить» задачу — тогда её зависимости будут всё равно вызваны, а она сама — нет.

несколько проектов (модулей)

В основном проекте можно расположить ещё несколько модулей. Например, такое используется в андроид проектах — в рутовом проекте почти ничего нет, в подпроекте включается android плагин. Если захочется добавить новый модуль — можно добавить ещё один, и там, например, тоже подключить android плагин, но использовать другие настройки для него.

Ещё пример: при публикации проекта с помощью jitpack в рутовом проекте описывается, с какими настройками публиковать дочерний модуль, который про факт публикации может даже не подозревать.

Дочерние модули указываются в settings.gradle:

Подробнее про зависимости между проектами можно почитать здесь

buildSrc

Если кода в build.gradle много или он дублируется, его можно вынести в отдельный модуль. Нужна папка с магическим именем buildSrc , в которой можно расположить код на groovy или java. (ну, вернее, в buildSrc/src/main/java/com/smth/ код, тесты можно добавить в buildSrc/src/test ). Если хочется что-то ещё, например, написать свою задачу на scala или использовать какие-то зависимости, то прямо в buildSrc надо создать build.gradle и в нём указать нужные зависимости/включить плагины.

К сожалению, с проектом в buildSrc IDE может тупить c подсказками, там придётся писать импорты и классы/задачи оттуда в обычный build.gradle тоже придётся импортировать. Написать import com.smth.Taskname — не сложно, просто надо это помнить и не ломать голову, почему задача из buildSrc не найдена).

По этой причине удобно сначала написать что-то работающее прямо в build.gradle , и только потом переносить код в buildSrc .

Свой тип задачи

Задача наследуется от DefaultTask , в которой есть много-много полей, методов и прочего. Код AbstractTask, от которой унаследована DefaultTask.

  • вместо ручного добавления inputs и outputs можно использовать поля и аннотации к ним: @Input, @OutputFile и т.п.
  • метод, который будут запускать при выполнении задачи: @TaskAction .
  • удобные методы типа copy всё ещё можно вызвать, но придётся их явно вызывать для проекта: project.copy

Когда для нашей задачи кто-то в build.gradle пишет

у задачи вызывается метод configure(Closure) .

Я не уверен, что это правильных подход, но если у задачи есть несколько полей, взаимное состояние которых сложно контролировать геттерами-сеттерами, то кажется вполне удобным переопределить метод следующим образом:

Причём даже если написать

то метод configure всё равно будет вызван.

Свой плагин

Подобно задаче, можно написать свой плагин, который будет что-то настраивать или создавать задачи. Например, происходящее в android <. >— полностью заслуга тёмной магии андроид плагина, который вдобавок создаёт целую кучу задач типа app:assembleDevelopDebug на все возможные сочетания flavor/build type/dimenstion. Ничего сложного в написании своего плагина нет, для лучшего понимания можно посмотреть код других плагинов.

Есть ещё третья ступенька — можно код расположить не в buildSrc , а сделать его отдельным проектом. Потом с помощью https://jitpack.io или ещё чего-то опубликовать плагин и подключать его аналогично остальным.

The end

В примерах выше могут быть опечатки и неточности. Пишите в личку или отмечайте с ctrl+enter — исправлю. Конкретные примеры лучше брать из документации, а на эту статью смотреть как на списочек того «как можно делать».

Источник

Build Script Basics

This chapter introduces you to the basics of writing Gradle build scripts. It uses toy examples to explain basic functionality of Gradle, which is helpful to get an understanding of the basic concepts. Especially if you move to Gradle from other build tools like Ant and want to understand differences and advantages.

However, to get started with a standard project setup, you don’t even need to go into these concepts in detail. Instead, you can have a quick hands-on introduction, through our step-by-step samples.

Projects, plugins and tasks

Every Gradle build is made up of one or more projects. What a project represents depends on what it is that you are doing with Gradle. For example, a project might represent a library JAR or a web application. It might represent a distribution ZIP assembled from the JARs produced by other projects. A project does not necessarily represent a thing to be built. It might represent a thing to be done, such as deploying your application to staging or production environments. Don’t worry if this seems a little vague for now. Gradle’s build-by-convention support adds a more concrete definition for what a project is.

The work that Gradle can do on a project is defined by one or more tasks. A task represents some atomic piece of work which a build performs. This might be compiling some classes, creating a JAR, generating Javadoc, or publishing some archives to a repository.

Typically, tasks are provided by applying a plugin so that you do not have to define them yourself. Still, to give you an idea of what a task is, we will look at defining some simple tasks in a build with one project in this chapter.

Источник

Developing Custom Gradle Task Types

Gradle supports two types of task. One such type is the simple task, where you define the task with an action closure. We have seen these in Build Script Basics. For this type of task, the action closure determines the behaviour of the task. This type of task is good for implementing one-off tasks in your build script.

The other type of task is the enhanced task, where the behaviour is built into the task, and the task provides some properties which you can use to configure the behaviour. We have seen these in Authoring Tasks. Most Gradle plugins use enhanced tasks. With enhanced tasks, you don’t need to implement the task behaviour as you do with simple tasks. You simply declare the task and configure the task using its properties. In this way, enhanced tasks let you reuse a piece of behaviour in many different places, possibly across different builds.

The behaviour and properties of an enhanced task are defined by the task’s class. When you declare an enhanced task, you specify the type, or class of the task.

Implementing your own custom task class in Gradle is easy. You can implement a custom task class in pretty much any language you like, provided it ends up compiled to JVM bytecode. In our examples, we are going to use Groovy as the implementation language. Groovy, Java or Kotlin are all good choices as the language to use to implement a task class, as the Gradle API has been designed to work well with these languages. In general, a task implemented using Java or Kotlin, which are statically typed, will perform better than the same task implemented using Groovy.

Packaging a task class

There are several places where you can put the source for the task class.

You can include the task class directly in the build script. This has the benefit that the task class is automatically compiled and included in the classpath of the build script without you having to do anything. However, the task class is not visible outside the build script, and so you cannot reuse the task class outside the build script it is defined in.

You can put the source for the task class in the rootProjectDir/buildSrc/src/main/groovy directory (or rootProjectDir/buildSrc/src/main/java or rootProjectDir/buildSrc/src/main/kotlin depending on which language you prefer). Gradle will take care of compiling and testing the task class and making it available on the classpath of the build script. The task class is visible to every build script used by the build. However, it is not visible outside the build, and so you cannot reuse the task class outside the build it is defined in. Using the buildSrc project approach separates the task declaration — that is, what the task should do — from the task implementation — that is, how the task does it.

See Organizing Gradle Projects for more details about the buildSrc project.

You can create a separate project for your task class. This project produces and publishes a JAR which you can then use in multiple builds and share with others. Generally, this JAR might include some custom plugins, or bundle several related task classes into a single library. Or some combination of the two.

In our examples, we will start with the task class in the build script, to keep things simple. Then we will look at creating a standalone project.

Writing a simple task class

To implement a custom task class, you extend DefaultTask.

This task doesn’t do anything useful, so let’s add some behaviour. To do so, we add a method to the task and mark it with the TaskAction annotation. Gradle will call the method when the task executes. You don’t have to use a method to define the behaviour for the task. You could, for instance, call doFirst() or doLast() with a closure in the task constructor to add behaviour.

Let’s add a property to the task, so we can customize it. Tasks are objects, and when you declare a task, you can set the properties or call methods on the task object. Here we add a greeting property, and set the value when we declare the greeting task.

A standalone project

Now we will move our task to a standalone project, so we can publish it and share it with others. This project is simply a Groovy project that produces a JAR containing the task class. Here is a simple build script for the project. It applies the Groovy plugin, and adds the Gradle API as a compile-time dependency.

We just follow the convention for where the source for the task class should go.

Example: A custom task

Using your task class in another project

To use a task class in a build script, you need to add the class to the build script’s classpath. To do this, you use a buildscript < >block, as described in External dependencies for the build script. The following example shows how you might do this when the JAR containing the task class has been published to a local repository:

Writing tests for your task class

You can use the ProjectBuilder class to create Project instances to use when you test your task class.

Example: Testing a custom task

Incremental tasks

With Gradle, it’s very simple to implement a task that is skipped when all of its inputs and outputs are up to date (see Incremental Builds). However, there are times when only a few input files have changed since the last execution, and you’d like to avoid reprocessing all of the unchanged inputs. This can be particularly useful for a transformer task that converts input files to output files on a 1:1 basis.

If you’d like to optimize your build so that only out-of-date input files are processed, you can do so with an incremental task.

There is the IncrementalTaskInputs API, which is available in Gradle versions before 5.4. When using IncrementalTaskInputs, it is only possible to query for all file changes of the task inputs. It is not possible to query for changes of individual input file properties. Moreover, the old API does not distinguish between incremental and non-incremental task inputs, so the task itself needs to determine where the changes originated from. Therefore, the usage of this API is deprecated, and it will be removed eventually. The new InputChanges API, which is documented here, replaces the old API and addresses its shortcomings. If you need to use the old API, have a look at the documentation in the user manual for Gradle 5.3.1.

Implementing an incremental task

For a task to process inputs incrementally, that task must contain an incremental task action. This is a task action method that has a single InputChanges parameter. That parameter tells Gradle that the action only wants to process the changed inputs. In addition, the task needs to declare at least one incremental file input property by using either @Incremental or @SkipWhenEmpty.

To query incremental changes for an input file property, that property always needs to return the same instance. The easiest way to accomplish this is to use one of the following types for such properties: RegularFileProperty, DirectoryProperty or ConfigurableFileCollection.

You can learn more about RegularFileProperty and DirectoryProperty in the Lazy Configuration chapter, especially the sections on using read-only and configurable properties and lazy file properties.

The incremental task action can use InputChanges.getFileChanges() to find out what files have changed for a given file-based input property, be it of type RegularFileProperty , DirectoryProperty or ConfigurableFileCollection . The method returns an Iterable of type FileChanges, which in turn can be queried for the following:

the change type ( ADDED , REMOVED or MODIFIED )

the normalized path of the changed file

the file type of the changed file

The following example demonstrates an incremental task that has a directory input. It assumes that the directory contains a collection of text files and copies them to an output directory, reversing the text within each file. The key things to note are the type of the inputDir property, its annotations, and how the action ( execute() ) uses getFileChanges() to process the subset of files that have actually changed since the last build. You can also see how the action deletes a target file if the corresponding input file has been removed:

If for some reason the task is executed non-incrementally, for example by running with —rerun-tasks , all files are reported as ADDED , irrespective of the previous state. In this case, Gradle automatically removes the previous outputs, so the incremental task only needs to process the given files.

For a simple transformer task like the above example, the task action simply needs to generate output files for any out-of-date inputs and delete output files for any removed inputs.

A task may only contain a single incremental task action.

Which inputs are considered out of date?

When there is a previous execution of the task, and the only changes since that execution are to incremental input file properties, then Gradle is able to determine which input files need to be processed (incremental execution). In this case, the InputChanges.getFileChanges() method returns details for all input files for the given property that were added, modified or removed.

However, there are many cases where Gradle is unable to determine which input files need to be processed (non-incremental execution). Examples include:

There is no history available from a previous execution.

You are building with a different version of Gradle. Currently, Gradle does not use task history from a different version.

An upToDateWhen criterion added to the task returns false .

An input property has changed since the previous execution.

A non-incremental input file property has changed since the previous execution.

One or more output files have changed since the previous execution.

In all of these cases, Gradle will report all input files as ADDED and the getFileChanges() method will return details for all the files that comprise the given input property.

You can check if the task execution is incremental or not with the InputChanges.isIncremental() method.

An incremental task in action

Given the example incremental task implementation above, let’s walk through some scenarios based on it.

First, consider an instance of IncrementalReverseTask that is executed against a set of inputs for the first time. In this case, all inputs will be considered added, as shown here:

Naturally when the task is executed again with no changes, then the entire task is up to date and the task action is not executed:

When an input file is modified in some way or a new input file is added, then re-executing the task results in those files being returned by InputChanges.getFileChanges(). The following example modifies the content of one file and adds another before running the incremental task:

The various mutation tasks ( updateInputs , removeInput , etc) are only present to demonstrate the behavior of incremental tasks. They should not be viewed as the kinds of tasks or task implementations you should have in your own build scripts.

When an existing input file is removed, then re-executing the task results in that file being returned by InputChanges.getFileChanges() as REMOVED . The following example removes one of the existing files before executing the incremental task:

When an output file is deleted (or modified), then Gradle is unable to determine which input files are out of date. In this case, details for all the input files for the given property are returned by InputChanges.getFileChanges(). The following example removes just one of the output files from the build directory, but notice how all the input files are considered to be ADDED :

The last scenario we want to cover concerns what happens when a non-file-based input property is modified. In such cases, Gradle is unable to determine how the property impacts the task outputs, so the task is executed non-incrementally. This means that all input files for the given property are returned by InputChanges.getFileChanges() and they are all treated as ADDED . The following example sets the project property taskInputProperty to a new value when running the incrementalReverse task and that project property is used to initialize the task’s inputProperty property, as you can see in the first example of this section. Here’s the output you can expect in this case:

Storing incremental state for cached tasks

Using Gradle’s InputChanges is not the only way to create tasks that only work on changes since the last execution. Tools like the Kotlin compiler provide incrementality as a built-in feature. The way this is typically implemented is that the tool stores some analysis data about the state of the previous execution in some file. If such state files are relocatable, then they can be declared as outputs of the task. This way when the task’s results are loaded from cache, the next execution can already use the analysis data loaded from cache, too.

However, if the state files are non-relocatable, then they can’t be shared via the build cache. Indeed, when the task is loaded from cache, any such state files must be cleaned up to prevent stale state from confusing the tool during the next execution. Gradle can ensure such stale files are removed if they are declared via task.localState.register() or if a property is marked with the @LocalState annotation.

Declaring and Using Command Line Options

Sometimes a user wants to declare the value of an exposed task property on the command line instead of the build script. Being able to pass in property values on the command line is particularly helpful if they change more frequently. The task API supports a mechanism for marking a property to automatically generate a corresponding command line parameter with a specific name at runtime.

Declaring a command-line option

Exposing a new command line option for a task property is straightforward. You just have to annotate the corresponding setter method of a property with Option. An option requires a mandatory identifier. Additionally, you can provide an optional description. A task can expose as many command line options as properties available in the class.

Let’s have a look at an example to illustrate the functionality. The custom task UrlVerify verifies whether a given URL can be resolved by making a HTTP call and checking the response code. The URL to be verified is configurable through the property url . The setter method for the property is annotated with @Option.

Example: Declaring a command line option

All options declared for a task can be rendered as console output by running the help task and the —task option.

Using an option on the command line

Using an option on the command line has to adhere to the following rules:

The option uses a double-dash as prefix e.g. —url . A single dash does not qualify as valid syntax for a task option.

The option argument follows directly after the task declaration e.g. verifyUrl —url=http://www.google.com/ .

Multiple options of a task can be declared in any order on the command line following the task name.

Getting back to the previous example, the build script creates a task instance of type UrlVerify and provides a value from the command line through the exposed option.

Supported data types for options

Gradle limits the set of data types that can be used for declaring command line options. The use on the command line differ per type.

Describes an option with the value true or false . Passing the option on the command line treats the value as true . For example —enabled equates to true . The absence of the option uses the default value of the property.

Describes an option with an arbitrary String value. Passing the option on the command line also requires a value e.g. —container-id=2x94held or —container-id 2x94held .

Describes an option as an enumerated type. Passing the option on the command line also requires a value e.g. —log-level=DEBUG or —log-level debug . The value is not case sensitive.

Describes an option that can takes multiple values of a given type. The values for the option have to be provided as multiple declarations e.g. —image-id=123 —image-id=456 . Other notations such as comma-separated lists or multiple values separated by a space character are currently not supported.

Documenting available values for an option

In theory, an option for a property type String or List can accept any arbitrary value. Expected values for such an option can be documented programmatically with the help of the annotation OptionValues. This annotation may be assigned to any method that returns a List of one of the supported data types. In addition, you have to provide the option identifier to indicate the relationship between option and available values.

Passing a value on the command line that is not supported by the option does not fail the build or throw an exception. You’ll have to implement custom logic for such behavior in the task action.

This example demonstrates the use of multiple options for a single task. The task implementation provides a list of available values for the option output-type .

Example: Declaring available values for an option

Listing command line options

Command line options using the annotations Option and OptionValues are self-documenting. You will see declared options and their available values reflected in the console output of the help task. The output renders options in alphabetical order.

Example: Listing available values for option

Limitations

Support for declaring command line options currently comes with a few limitations.

Command line options can only be declared for custom tasks via annotation. There’s no programmatic equivalent for defining options.

Options cannot be declared globally e.g. on a project-level or as part of a plugin.

When assigning an option on the command line then the task exposing the option needs to be spelled out explicitly e.g. gradle check —tests abc does not work even though the check task depends on the test task.

The Worker API

As can be seen from the discussion of incremental tasks, the work that a task performs can be viewed as discrete units (i.e. a subset of inputs that are transformed to a certain subset of outputs). Many times, these units of work are highly independent of each other, meaning they can be performed in any order and simply aggregated together to form the overall action of the task. In a single threaded execution, these units of work would execute in sequence, however if we have multiple processors, it would be desirable to perform independent units of work concurrently. By doing so, we can fully utilize the available resources at build time and complete the activity of the task faster.

The Worker API provides a mechanism for doing exactly this. It allows for safe, concurrent execution of multiple items of work during a task action. But the benefits of the Worker API are not confined to parallelizing the work of a task. You can also configure a desired level of isolation such that work can be executed in an isolated classloader or even in an isolated process. Furthermore, the benefits extend beyond even the execution of a single task. Using the Worker API, Gradle can begin to execute tasks in parallel by default. In other words, once a task has submitted its work to be executed asynchronously, and has exited the task action, Gradle can then begin the execution of other independent tasks in parallel, even if those tasks are in the same project.

Using the Worker API

A step-by-step description of converting a normal task action to use the worker API can be found in the section on developing parallel tasks.

In order to submit work to the Worker API, two things must be provided: an implementation of the unit of work, and the parameters for the unit of work.

The parameters for the unit of work are defined as an interface or abstract class that implements WorkParameters. The parameters type must be a managed type.

You can find out more about implementing work parameters in Developing Custom Gradle Types.

The implementation is a class that extends WorkAction. This class should be abstract and should not implement the getParameters() method. Gradle will inject an implementation of this method at runtime with the parameters object for each unit of work.

Источник

Читайте также:  Vlc android нет звука с медиаплеера
Оцените статью