One, foreword
The most common IDE for Android development today is Android Studio. Using Gradle builds in Android Studio makes it easy and clear to manage dependencies between modules.
At the same time, Android plug-ins and hot updates in China all involve the knowledge of Gradle plug-ins. A good command of Gradle will enable us to have a clearer understanding of the Android construction process and transform the construction process to meet certain functional requirements. From this point of view, Android development requires Gradle’s skills.
Second, Android Plugin source code acquisition
In the Android project build.gradle, there is a line like this:
apply plugin: 'com.android.application'
Copy the code
I’m sure you’re all familiar with this line of code. It means applying a plug-in called com.android.application to our project, which is known as the Android Plugin.
So how did the Android Plugin get into our project?
Build. gradle in the root directory
We copied the gradle plugin from above into our project’s build.gradle. .
Sync your project to find the Android Plugin source code in the project’s dependency tree.
Android Plugin source code analysis
On the above com. Android. View the build: gradle: 3.1.0 @ jar, you can see AppPlugin and LibraryPlugin, including AppPlugin is android plug-in project need to rely on, The LibraryPlugin is the plug-in that the component project depends on.
Let’s take a look at the source code of the AppPlugin and learn about the App building process. Let’s do it~
First, AppPlugin inherits BasePlugin, which implements Plugin interface and apply method. The apply method, as the BasePlugin entry class, is implemented as follows:
@override public void apply(@nonnull Project Project) { ThreadRecorder = threadRecorder. Get (); ProcessProfileWriter.getProject(project.getPath()) .setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION) .setAndroidPlugin(getAnalyticsPluginType()) .setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST) .setOptions(AnalyticsUtil.toProto(projectOptions)); BuildableArtifactImpl.Companion.disableResolution();if(! ProjectOptions. Get (BooleanOption. ENABLE_NEW_DSL_AND_API)) {/ / don't use the new DSL API TaskInputHelper enableBypass (); ThreadRecorder. Record (executionType.base_plugin_project_configure, project.getPath(), null, this::configureProject); // Config Extension configureExtension, ThreadRecorder. Record (ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION, project.getPath(), null, this::configureExtension); // createTasks Create the required task threadRecorder. Record (executionType.base_plugin_project_tasks_creation, project.getPath(), null, this::createTasks); }else {
//省略
}
}
Copy the code
The most important thing about the Apply method above is that it calls the ThreadRecorder record method three times. Note: this::configureProject is the lambda notation for java8, so let’s look at the implementation of the record method:
@Override public void record( @NonNull ExecutionType executionType, @NonNull String projectPath, @Nullable String variant, @NonNull VoidBlock block) { ProfileRecordWriter profileRecordWriter = ProcessProfileWriter.get(); / / create GradleBuildProfileSpan. Builder object GradleBuildProfileSpan. Builder currentRecord = the create (profileRecordWriter, executionType, null); Try {// Callback method block.call(); } catch (IOException e) { throw new UncheckedIOException(e); } finally {// Write the GradleBuildProfileSpan created above into the span variable of the ProfileRecordWriter object write(ProfileRecordWriter, currentRecord, projectPath, variant); }}Copy the code
Record method mainly completed the three logical: 1, created to create GradleBuildProfileSpan. Builder object; 2. Execute the callback method; 3. Write GradleBuildProfileSpan to the SPAN variable of ProfileRecordWriter.
First we see 1 logic, invoke the create method created GradleBuildProfileSpan. Builder object:
private GradleBuildProfileSpan.Builder create(
@NonNull ProfileRecordWriter profileRecordWriter,
@NonNull ExecutionType executionType,
@Nullable GradleTransformExecution transform) {
long thisRecordId = profileRecordWriter.allocateRecordId();
// am I a child ?
@Nullable
Long parentId = recordStacks.get().peek();
long startTimeInMs = System.currentTimeMillis();
final GradleBuildProfileSpan.Builder currentRecord =
GradleBuildProfileSpan.newBuilder()
.setId(thisRecordId)
.setType(executionType)
.setStartTimeInMs(startTimeInMs);
if(transform ! = null) { currentRecord.setTransform(transform); }if(parentId ! = null) { currentRecord.setParentId(parentId); } currentRecord.setThreadId(threadId.get()); recordStacks.get().push(thisRecordId);return currentRecord;
}
Copy the code
The logic of the create method is to create a GradleBuildProfileSpan. Builder object, and set some threads related variables in, and will save thisRecordId to a bidirectional queue.
Then look at the logic of 3:
private void write(
@NonNull ProfileRecordWriter profileRecordWriter,
@NonNull GradleBuildProfileSpan.Builder currentRecord,
@NonNull String projectPath,
@Nullable String variant) {
// pop this record from the stack.
if(recordStacks.get().pop() ! = currentRecord.getId()) { Logger.getLogger(ThreadRecorder.class.getName()) .log(Level.SEVERE,"Profiler stack corrupted");
}
currentRecord.setDurationInMs(
System.currentTimeMillis() - currentRecord.getStartTimeInMs());
profileRecordWriter.writeRecord(projectPath, variant, currentRecord);
}
Copy the code
The writeRecord method of the ProfileRecordWriter object is executed:
@Override
public void writeRecord(
@NonNull String project,
@Nullable String variant,
@NonNull final GradleBuildProfileSpan.Builder executionRecord) {
executionRecord.setProject(mNameAnonymizer.anonymizeProjectPath(project));
executionRecord.setVariant(mNameAnonymizer.anonymizeVariant(project, variant));
spans.add(executionRecord.build());
}
Copy the code
The GradleBuildProfileSpan object is written to the SPAN variable of the ProfileRecordWriter object using the Builder pattern.
Finally, we look at the logic of 2, the execution of the callback method. Remember the three Record methods executed by the Apply method of the BasePlugin mentioned above? Each of the Record methods has its own callback methods, namely configureProject, configureExtension, and createTasks.
BASE_PLUGIN_PROJECT_CONFIGURE: basic configuration information for the plug-in, initialization, etc. BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION: initialization of the plug-in Extension. 3. BASE_PLUGIN_PROJECT_TASKS_CREATION: Plug-in task creation.
Let’s look at the implementation logic of the first method first, and the next two methods in the next two articles.
private void configureProject() {
final Gradle gradle = project.getGradle();
extraModelInfo = new ExtraModelInfo(project.getPath(), projectOptions, project.getLogger());
checkGradleVersion(project, getLogger(), projectOptions);
sdkHandler = new SdkHandler(project, getLogger());
if(! gradle.getStartParameter().isOffline() && projectOptions.get(BooleanOption.ENABLE_SDK_DOWNLOAD)) { SdkLibData sdkLibData = SdkLibData.download(getDownloader(), getSettingsController()); sdkHandler.setSdkLibData(sdkLibData); } androidBuilder = new AndroidBuilder( project == project.getRootProject() ? project.getName() : project.getPath(), creator, new GradleProcessExecutor(project), new GradleJavaProcessExecutor(project), extraModelInfo.getSyncIssueHandler(), extraModelInfo.getMessageReceiver(), getLogger(), isVerbose()); dataBindingBuilder = new DataBindingBuilder(); dataBindingBuilder.setPrintMachineReadableOutput( SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);if (projectOptions.hasRemovedOptions()) {
androidBuilder
.getIssueReporter()
.reportWarning(Type.GENERIC, projectOptions.getRemovedOptionsErrorMessage());
}
if (projectOptions.hasDeprecatedOptions()) {
extraModelInfo
.getDeprecationReporter()
.reportDeprecatedOptions(projectOptions.getDeprecatedOptions());
}
// Apply the Java plugin
project.getPlugins().apply(JavaBasePlugin.class);
project.getTasks()
.getByName("assemble")
.setDescription(
"Assembles all variants of all applications and secondary packages.");
// call back on execution. This is called after the whole build is done (not
// after the current project is done).
// This is will be called for each (android) projects though, so this should support
// being called 2+ times.
gradle.addBuildListener(
new BuildListener() {
@Override
public void buildStarted(@NonNull Gradle gradle) {
TaskInputHelper.enableBypass();
BuildableArtifactImpl.Companion.disableResolution();
}
@Override
public void settingsEvaluated(@NonNull Settings settings) {}
@Override
public void projectsLoaded(@NonNull Gradle gradle) {}
@Override
public void projectsEvaluated(@NonNull Gradle gradle) {}
@Override
public void buildFinished(@NonNull BuildResult buildResult) {
// Do not run buildFinished for included project in composite build.
if(buildResult.getGradle().getParent() ! = null) {return;
}
sdkHandler.unload();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
project.getPath(),
null,
() -> {
WorkerActionServiceRegistry.INSTANCE
.shutdownAllRegisteredServices(
ForkJoinPool.commonPool());
PreDexCache.getCache()
.clear(
FileUtils.join(
project.getRootProject().getBuildDir(),
FD_INTERMEDIATES,
"dex-cache"."cache.xml"), getLogger()); Main.clearInternTables(); }); }}); gradle.getTaskGraph() .addTaskExecutionGraphListener( taskGraph -> { TaskInputHelper.disableBypass(); Aapt2DaemonManagerService.registerAaptService( Objects.requireNonNull(androidBuilder.getTargetInfo()) .getBuildTools(), loggerWrapper, WorkerActionServiceRegistry.INSTANCE);for (Task task : taskGraph.getAllTasks()) {
if (task instanceof TransformTask) {
Transform transform = ((TransformTask) task).getTransform();
if (transform instanceof DexTransform) {
PreDexCache.getCache()
.load(
FileUtils.join(
project.getRootProject()
.getBuildDir(),
FD_INTERMEDIATES,
"dex-cache"."cache.xml"));
break; }}}}); createLintClasspathConfiguration(project); }Copy the code
The configureProject method is a bit long, but it mainly performs the following logic: 1. Initializes SdkHandler, AndroidBuilder, and DataBindingBuilder objects. 2. Rely on the JavaBasePlugin plugin to create a lot of tasks about build, such as Build, Assemble, etc. 3. Monitored the creation of the project, and performed disk caching and other operations after the completion of construction.
Four,
This article mainly introduces how to view the Android Plugin source code, as well as the Implementation process of the Android Plugin Plugin for a simple analysis, will continue to analyze the plug-in Extension and plug-in task creation of these two processes.