In the previous article we learned how to modify the Flutter Engine code to achieve dynamic effects. This article describes the process of flutter build AAR and then modify the corresponding Gradle compilation script to create aar packages suitable for hybrid projects.

The result of flutter build AAR

In this section, let’s take a look at the structure of a FLUTTER Build AAR that can be packaged normally

1.1. Preconditions

Create a new Android project and create a flutter module. You can also create a flutter module using flutter create -t module –org com.example flutter_module.

CD flutter_module and execute flutter pub get to automatically create an. Android file

1.2. Execute flutter build aar

We only analyze aar of release version here, change the suffix of Flutter_release-1.0.aar to JAR, and then open Flutter_release-1.0.jar with decomcompiling tool JD-GUI. The result is as follows:

You can see that there is no code for flutter. Jar or libflutter. So in the package, where are these two files?

How do you package these two files into an AAR? With that in mind let’s take a look at what scripts are executed when flutter Build AAR is executed.

Here is a solution, that isfat-aarTo package AAR, please use your own baidu

Ii. Process analysis of Flutter build AAR

This section describes the two gradle files involved in the build process of the flutter command: flutter. Gradle and aar_init_script.gradle

2.1, flutter. Gradle

In: build of Flutter. Gradle cited the apply in the from: “$flutterRoot/packages/flutter_tools/gradle/Flutter. Gradle.” “

/ / reference the plugin
apply plugin: FlutterPlugin
// This is followed by the implementation of plugin
class FlutterPlugin implements Plugin<Project> {
  void apply(Project project) {
    // Create extension fields source, target
    project.extensions.create("flutter", FlutterExtension)
    // Execute addFlutterTasks after other tasks are complete (set flutter initializers)
    project.afterEvaluate this.&addFlutterTasks
    
    // Abi configuration
    if(shouldSplitPerAbi ())... .// Get the version of engine for easy download from Maven
    engineVersion = useLocalEngine() ? "+" : "1.0.0 -" + Paths.get(flutterRoot.absolutePath, "bin"."internal"."engine.version"). ToFile (). The text. The trim ()...Gradle.properties (); // If you use a local engine, you need to configure gradle.properties
    if (useLocalEngine()) {
      // This is required to pass the local engine to flutter build aot.
      String engineOutPath = project.property('local-engine-out')
      File engineOut = project.file(engineOutPath)
      if(! engineOut.isDirectory()) {throw new GradleException('local-engine-out must point to a local engine build')
      }
      localEngine = engineOut.name
      localEngineSrcPath = engineOut.parentFile.parent
    }
    // Add dependencies for each type
    project.android.buildTypes.each this.&addFlutterDependencies
    project.android.buildTypes.whenObjectAdded this.&addFlutterDependencies
  }
}
Copy the code

The apply method configures dependencies and other dependencies. The addFlutterDependencies method does the following:

/** * Adds the dependencies required by the Flutter project. * This includes: * 1. The embedding * 2. libflutter.so */
// This method is used to add flutter. Jar and libflutter. So
void addFlutterDependencies(buildType) {
  String flutterBuildMode = buildModeFor(buildType)
  // supportsBuildMode determines whether the local-engine-build-Mode in gradle.properites is the same as the current flutterBuildMode only if the local engine is used
  if(! supportsBuildMode(flutterBuildMode)) {return
  }
  // Set the maven source path
  String repository = useLocalEngine() ? project.property('local-engine-repo') : MAVEN_REPO
  project.rootProject.allprojects {
    repositories {
      maven {
        url repository
      }
    }
  }
  // Add the embedding dependency.
  // Add a reference to flutter. Jar
  addApiDependencies(project, buildType.name,   "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion")... platforms.each { platform -> String arch = PLATFORM_ARCH_MAP[platform].replace("-"."_")
    // Add libflutter. So dependency
    addApiDependencies(project, buildType.name,
                       "io.flutter:${arch}_$flutterBuildMode:$engineVersion")}}Copy the code

2.2, aar_init_script. Gradle

The script is there will be a flutter build aar triggered, file path $flutterRoot/packages/flutter_tools gradle/aar_init_script gradle

projectsEvaluated {
    assert rootProject.hasProperty("is-plugin")
    if (rootProject.property("is-plugin").toBoolean()) {
        assert rootProject.hasProperty("output-dir")
        // In plugin projects, the root project is the plugin.
        configureProject(rootProject, rootProject.property("output-dir"))
        return
    }
    // For hybrid engineering, ':flutter' module
    Project moduleProject = rootProject.subprojects.find { it.name == "flutter" }
    assertmoduleProject ! =null
    assert moduleProject.hasProperty("output-dir")
    configureProject(moduleProject, moduleProject.property("output-dir"))

    // Get all plugin subprojectsSet<Project> modulePlugins = rootProject.subprojects.findAll { it.name ! ="flutter"&& it.name ! ="app"
    }
    modulePlugins.each { pluginProject ->
        configureProject(pluginProject, moduleProject.property("output-dir"))
        moduleProject.android.libraryVariants.all { variant ->
            String variantName = variant.name.capitalize()
	          // Execute the moduleProject task after the plugin's 'assembleAar$variantName' task is complete
            moduleProject.tasks.findByPath("assembleAar$variantName")
                .dependsOn(pluginProject.tasks.findByPath("assembleAar$variantName"))}}}Copy the code

As you can see from the script above, there is a core method called configureProject. Let’s see what this method does:

void configureProject(Project project, String outputDir) {
  	// Ensure that the project is Android
    if(! project.hasProperty("android")) {
        throw new GradleException("Android property not found.")}// Make sure it is library instead of application
    if(! project.android.hasProperty("libraryVariants")) {
        throw new GradleException("Can't generate AAR on a non Android library project.");
    }
		// Use the Maven plugin
    project.apply plugin: "maven"

    project.android.libraryVariants.all { variant ->
      	// Execute this task after uploading Maven task is complete
        addAarTask(project, variant)
    }
   
  	// Upload maven path, here is a use of maven's local library
    project.version = project.version.replace("-SNAPSHOT"."")
    project.uploadArchives {
        repositories {
            mavenDeployer {
                repository(url: "file://${outputDir}/outputs/repo")}}}if(! project.property("is-plugin").toBoolean()) {
        return
    }
  	// Build AAR currently does not support using a local engine
    if (project.hasProperty('localEngineOut')) {
        throw new GradleException(
            "Local engine isn't supported when building the plugins as AAR. " +
            "See: https://github.com/flutter/flutter/issues/40866")}// Add repositories and dependencies
    project.repositories {
        maven {
            url "http://download.flutter.io"
        }
    }
    String engineVersion = Paths.get(getFlutterRoot(project), "bin"."internal"."engine.version").toFile().text.trim()
    project.dependencies {
				// Add a flutter. Jar dependency
        compileOnly ("IO. Flutter: flutter_embedding_release: 1.0.0 - $engineVersion") {
            // We just need to leak IO. Flutter. Plugin.*
            // There is no need to expose dependencies. The default value of transitive is true. Gradle automatically adds child dependencies
            transitive = false}}}Copy the code

This method is used to upload the maven task after adding the dependency. Let’s look at the addAarTask:

void addAarTask(Project project, variant) {
    String variantName = variant.name.capitalize()
    String taskName = "assembleAar$variantName"
    project.tasks.create(name: taskName) {
				// Check whether 'uploadArchives' is configured
        if(! project.gradle.startParameter.taskNames.contains(taskName)) {return
        }
      	// Upload maven configuration
        project.uploadArchives.repositories.mavenDeployer {
            pom {
                artifactId = "${project.name}_${variant.name.toLowerCase()}"
            }
        }
        overrideDefaultPublishConfig(project, variant)
        // Generate the Maven artifacts.
        finalizedBy "uploadArchives"}}Copy the code

Now that we have covered the script for the two core aar packages, how can we modify this script in the next section

How to modify gradle package AAR script

The main reasons we changed the packaging script were:

  1. The local engine can be used when the AAR is packaged
  2. Package the modified local engine into an AAR, which is the Engine artifactflutter.jarandlibflutter.soThe file is packaged into the AAR

3.1, modify,flutter.gradleIn theaddFlutterDependencies

void addFlutterDependencies(buildType) {
  String flutterBuildMode = buildModeFor(buildType)
  if(! supportsBuildMode(flutterBuildMode)) {return
  }
  // add local engine dependencies by panmin [start]
  // Add flutter. Jar and libflutter. So file dependencies when using local engine
  if(useLocalEngine()){
    String engineOutPath = project.property('local-engine-out')
    File engineOut = project.file(engineOutPath)
    if(! engineOut.isDirectory()) {throw new GradleException('local-engine-out must point to a local engine build')}// The path to the local engine's flutter. Jar file
    File flutterJar = Paths.get(engineOut.absolutePath, "flutter.jar").toFile()
    if(! flutterJar.isFile()) {throw new GradleException('Local engine build does not contain flutter.jar')}// The libflutter. So file path of the local engine
    File flutterSo = Paths.get(engineOut.absolutePath, "flutter_embedding_$flutterBuildMode").toFile()
    project.dependencies {
      / / add automatically generated ` GeneratedPluginRegistrant. Java ` files need @ Keep and @ NonNull need to rely on libraries
      implementation 'androidx. Annotation: the annotation: 1.1.0'
      // add flutter jar & libflutter so
      if (project.getConfigurations().findByName("api")) {
        "${flutterBuildMode}Api" project.files(flutterJar)
        "${flutterBuildMode}Api" project.files(flutterSo)
      } else {
        "${flutterBuildMode}Compile" project.files(flutterJar)
        "${flutterBuildMode}Compile" project.files(flutterSo)
      }
    }
    return;
  }
  // add local engine dependencies by panmin [end]. }Copy the code

3.2. Modify aar_init_script.gradle

void configureProject(Project project, String outputDir) {
    if(! project.hasProperty("android")) {
        throw new GradleException("Android property not found.")}if(! project.android.hasProperty("libraryVariants")) {
        throw new GradleException("Can't generate AAR on a non Android library project.");
    }
    project.apply plugin: "maven"
		// add local engine dependencies by panmin [start]
    if (project.hasProperty('local-engine-build-mode')){
        String flutterBuildMode = project.property('local-engine-build-mode').toLowerCase()
        project.android.libraryVariants.all { variant ->
            String variantName = variant.name.capitalize().toLowerCase()
          	// Determine the packaging mode of the current gradle.properties configuration
            if (variantName == flutterBuildMode) {
                addAarTask(project, variant)
            }
        }
    } else {
        project.android.libraryVariants.all { variant ->
            addAarTask(project, variant)
        }
    }
    // add local engine dependencies by panmin [end]
    project.version = project.version.replace("-SNAPSHOT"."")... }Copy the code

How to use the modified Gradle script

4.1, configuration,.androidProject of gradle. The properties

#Take packaging the Armeabi-V7A architecture as an exampleLocal-engine-repo =engine/ SRC /out/android_release # Local-engine-out =engine/ SRC /out/android_release # arm64 Android_release_arm64 local-engine-build-mode=releaseCopy the code

Error when using flutter Build AAR without modifying the script:

Could not determine the dependencies of task ':flutter:compileReleaseAidl'.
	> Could not resolve all task dependencies for configuration ':flutter:releaseCompileClasspath'.
	> Could not find any matches for io.flutter:flutter_embedding_release:+ as no versions of io.flutter:flutter_embedding_release are available.
		Required by:
	 	 project :flutter
  > Could not find any matches for io.flutter:armeabi_v7a_release:+ as no versions of io.flutter:armeabi_v7a_release are available.
  	Required by:
    	project :flutter
  > Could not find any matches for io.flutter:arm64_v8a_release:+ as no versions of io.flutter:arm64_v8a_release are available.
  	Required by:
    	project :flutter
  > Could not find any matches for io.flutter:x86_64_release:+ as no versions of io.flutter:x86_64_release are available.
  	Required by:
    		project :flutter

Copy the code

Flutter_embedding_release. jar is not referenced by Maven when ninja -c out/ Android_release is generated.

4.2 Package aar command

Gradle. properties is configured with local-engine-build-mode=release and local-engine-out is android_release. So the command to release the package is:

#Release mode corresponding to arm platform
flutter build aar --target-platform=android-arm --no-debug --no-profile --verbose
#The release mode corresponds to the ARM64 platform
flutter build aar --target-platform=android-arm64 --no-debug --no-profile --verbose
Copy the code

The packing result is:

This is the result of the package, the flutter. Jar and libflutter. So have been packaged, and this AAR product can be used directly if delivered to Android native projects.

Five, the summary

Gradle and aar_init_script.gradle are packaged with the buildingof the flutter engine, including the buildingof the flutter engine, and the buildingof the flutter engine. Convenient Android hybrid engineering development. In the next article, I will explain how to upload the build product to the Maven library and then modify the package script again for your brainless use. Welcome to follow and like the flutter engine, as well as communicate in the comments section.