创建一个包含所有颤振库和依赖项的 aar

Man*_*oto 5 android gradle build.gradle android-gradle-plugin flutter

我需要创建一个包含我的 flutter 项目的所有库的 aar,我创建了一个 flutter 模块,现在我必须在 android 中创建一个 sdk 以嵌入到客户端应用程序中,因为拥有一个 aar 会很好文件。我尝试了 Mobbeel fat AAR Gradle 插件,但无济于事。我知道我可以创建一个 Maven 存储库,但这不是我现在正在寻找的解决方案。 在此处输入图片说明

我的项目 build.gradle

buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
        google()
        jcenter()

    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath "com.mobbeel.plugin:fat-aar:2.0.1"
    }
}




allprojects {
    repositories {
        google()
        jcenter()
    }
}
Run Code Online (Sandbox Code Playgroud)

和应用程序 build.graddle

def flutterPluginVersion = 'managed'

apply plugin: 'com.android.library'
apply plugin: "com.mobbeel.plugin"

android {
    compileSdkVersion 28

    compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }

    defaultConfig {
        minSdkVersion 21
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

buildDir = new File(rootProject.projectDir, "../build/host")



dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    api (project(':flutter'))


    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'androidx.annotation:annotation:1.1.0'
    implementation 'androidx.lifecycle:lifecycle-common:2.0.0'
}

aarPlugin {
    includeAllInnerDependencies false
    packagesToInclude = ["mobbeel"]
}
Run Code Online (Sandbox Code Playgroud)

编辑:我找到了一个解决方案,但我不是 android 开发人员,因此对 mobbeel 插件进行了一些更改并将其添加到 build.gradle。之后,我可以通过执行 api project(":vibrate") 将所有库添加到我的 aar

String archiveAarName

project.afterEvaluate {
    project.android.libraryVariants.all { variant ->

            variant.outputs.all {
                archiveAarName = outputFileName
            }

            print "afterEvaluate\n"

            def copyTask = createBundleDependenciesTask(variant)

            String rsDirPath = "${copyTask.temporaryDir.path}/rs/"
            String rsCompiledDirPath = "${copyTask.temporaryDir.path}/rs-compiled/"
            String sourceAarPath = "${copyTask.temporaryDir.path}/${variant.name}/"


            String taskNameCompileRs = "SDKcompileRs${variant.name.capitalize()}"
            String taskNameRsJa = "CreateRsJar${variant.name.capitalize()}"
            String taskNameCreateZip = "createZip${variant.name.capitalize()}"

            def compileRsTask = R2ClassTask(variant, rsDirPath, rsCompiledDirPath, taskNameCompileRs)
            def rsJarTask = bundleRJarTask(variant, rsCompiledDirPath, sourceAarPath, taskNameRsJa)
            def aarTask = bundleFinalAAR(variant, sourceAarPath, "finalname", taskNameCreateZip)


            def assembleTask = project.tasks.findByPath("assemble${variant.name.capitalize()}")

            assembleBundleDependenciesTask(variant).finalizedBy(assembleTask)
            assembleTask.finalizedBy(copyTask)
            copyTask.finalizedBy(compileRsTask)
            compileRsTask.finalizedBy(rsJarTask)
            rsJarTask.finalizedBy(aarTask)
        }
    }


Task assembleBundleDependenciesTask(def variant) {
    println "assembleBundleDependenciesTask -> ${variant.name}"

    return project.getTasks().create("hello_${variant}", {
        project.configurations.api.getDependencies().each { dependency ->

            if (dependency instanceof ProjectDependency) {

                Project dependencyProject = project.parent.findProject(dependency.name)

                String dependencyPath = "${dependencyProject.buildDir}"
                println "dependencyPath -> ${dependencyPath}"


                String variantName = "${variant.name}"

                def assembleTask = project.tasks.findByPath(":${dependency.name}:assemble${variant.name.capitalize()}")

                assembleTask.finalizedBy(copyTo( "${dependencyPath}/outputs/aar", variantName, dependency.name))
            }

            println ''
        }
    })

}

Task copyTo(String fromz, String variant, String dependency) {
    println "copyTo fromz -> $fromz "
    return project.task(type: Copy, "copyFile$dependency$variant") {
        from fromz
        into project.projectDir.path + "/build/outputs/aar/"
        include('*.aar')
        rename { String fileName ->
            fileName = "${dependency}-${variant}.aar"
        }
    }

}

Task createBundleDependenciesTask(def variant) {
    println "createBundleDependenciesTask -> ${variant.name}"

    String taskName = "copy${variant.name.capitalize()}Dependencies"
    return project.getTasks().create(taskName, CopyDependenciesTask.class, {
        it.includeInnerDependencies = true
        it.dependencies = project.configurations.api.getDependencies()
        it.variantName = variant.name
        it.gradleVersion = "3.2.1"
        it.buildAARDir = project.projectDir.path + "/build/outputs/aar/"
    })
}

Task R2ClassTask(def variant, String sourceDir, String destinationDir, String taskName) {
    print "R2ClassTask sourceDir -> $sourceDir to destDir -> $destinationDir"
    project.mkdir(destinationDir)

    def classpath

    classpath = project.files(project.projectDir.path +
            "/build/intermediates/javac/${variant.name}/compile${variant.name.capitalize()}JavaWithJavac/classes")



    return project.getTasks().create(taskName, JavaCompile.class, {
        it.source = sourceDir
        it.sourceCompatibility = project.android.compileOptions.sourceCompatibility
        it.targetCompatibility = project.android.compileOptions.targetCompatibility
        it.classpath = classpath
        it.destinationDir project.file(destinationDir)
    })
}

Task bundleRJarTask(def variant, String fromDir, String aarPath, String taskName) {
    print "bundleRJarTask\n"

    return project.getTasks().create(taskName, Jar.class, {
        it.from fromDir
        it.archiveName = "r-classes.jar"
        it.destinationDir project.file("${aarPath}/libs")
    })
}

Task bundleFinalAAR(def variant, String fromPath, name, String taskName) {
    print "bundleFinalAAR -> from ${fromPath} to > " + project.file(project.projectDir.path + "/build/outputs/aar/") + "\n"

    return project.getTasks().create(taskName, Zip.class, {
        it.from fromPath
        it.include "**"
        it.archiveName = "${name}-${variant.name}.aar"
        it.destinationDir(project.file(project.projectDir.path + "/build/outputs/aar/"))
    })
}

import groovy.xml.XmlUtil

class CopyDependenciesTask extends DefaultTask {

    Boolean includeInnerDependencies
    DependencySet dependencies
    String variantName
    String gradleVersion
    String[] packagesToInclude = [""]
    String buildAARDir

    @TaskAction
    def executeTask() {
        if (temporaryDir.exists()) {
            temporaryDir.deleteDir()
        }
        temporaryDir.mkdir()

        copyProjectBundles()
        analyzeDependencies()
    }


    def copyProjectBundles() {
        println "copyProjectBundles"

        if (gradleVersion.contains("3.2")) { // Version 3.4.x
            println "packaged-classes -> ${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/packaged-classes/"
            project.copy {
                from "${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/packaged-classes/"
                include "${variantName}/**"
                into temporaryDir.path
            }


            project.copy {
                from("${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/res/symbol-table-with-package/${variantName}") {
                    include "package-aware-r.txt"
                    rename '(.*)', 'R.txt'
                }

                from("${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/aapt_friendly_merged_manifests/" +
                        "${variantName}/process${variantName.capitalize()}Manifest/aapt/") {
                    include "AndroidManifest.xml"
                }

                into "${temporaryDir.path}/${variantName}"
            }

            println " check this -> ${temporaryDir.path}/${variantName}/R.txt"

            processRsAwareFile(new File("${temporaryDir.path}/${variantName}/R.txt"))

            project.copy {
                from "${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/packaged_res/${variantName}"
                include "**"
                into "${temporaryDir.path}/${variantName}/res"
            }

            project.copy {
                from "${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/library_assets/${variantName}/packageDebugAssets/out/"
                include "**"
                into "${temporaryDir.path}/${variantName}/assets"
            }
        }  else { // Version 3.0.x
            project.copy {
                from "${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/bundles/"
                from "${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/manifests/full/"
                include "${variantName}/**"
                exclude "**/output.json"
                into temporaryDir.path
            }
        }
    }

    def analyzeDependencies() {
        print "analyzeDependencies\n"
        dependencies.each { dependency ->
            def dependencyPath
            def archiveName
            print "dependency -> " + dependency
            if (dependency instanceof ProjectDependency) {
                print " instanceof -> ProjectDependency\n"
                String group = dependency.group
                Project dependencyProject

                dependencyProject = project.parent.findProject(dependency.name)


                println "dependencyProject -> ${dependencyProject}"


                if (dependencyProject.plugins.hasPlugin('java-library')) {
                    println "Internal java dependency detected -> " + dependency.name

                    archiveName = dependencyProject.jar.archiveName

                    dependencyPath = "${dependencyProject.buildDir}/libs/"
                } else {
                    println "Internal android dependency detected -> " + dependency.name

                    dependencyProject.android.libraryVariants.all {
                        if (it.name == variantName) {
                            it.outputs.all { archiveName = outputFileName }
                        }
                    }

                    dependencyPath = buildAARDir
                }

                processDependency(dependency, archiveName, dependencyPath)
            } else if (dependency instanceof ExternalModuleDependency) {
                println "External dependency detected -> " + dependency.group + ":" + dependency.name + ":" + dependency.version
                dependencyPath = project.gradle.getGradleUserHomeDir().path + "/caches/modules-2/files-2.1/"
                dependencyPath += dependency.group + "/" + dependency.name + "/" + dependency.version + "/"

                processDependency(dependency, archiveName, dependencyPath)
            } else {
                println "Not recognize type of dependency for " + dependency
                println()
            }
        }
    }

    /**
     * In this case dependency is outside from workspace, download from maven repository if file is
     * a jar directly move to lib/ folder and analyze pom file for detect another transitive dependency
     * @param dependency
     * @return
     */
    def processDependency(Dependency dependency, String archiveName, String dependencyPath) {
        println "processDependency -> ${archiveName} in ${dependencyPath}"
        project.fileTree(dependencyPath).getFiles().each { file ->
            println "processDependency file.name  -> ${file.name} "
            if (file.name.endsWith(".pom")) {
                println "POM: " + file.name
                processPomFile(file.path)
            } else {
                if (archiveName == null || file.name == archiveName) {
                    println "Artifact: " + file.name
                    if (file.name.endsWith(".aar")) {
                        processZipFile(file, dependency)
                    } else if (file.name.endsWith(".jar")) {
                        if (!file.name.contains("sources")) {
                            copyArtifactFrom(file.path)
                        } else {
                            println "   |--> Exclude for source jar"
                        }
                    }
                }
            }
        }
        println()
    }

    def processZipFile(File aarFile, Dependency dependency) {
        println "processZipFile"

        String tempDirPath = "${temporaryDir.path}/${dependency.name}_zip"

        println "tempDirPath -> ${tempDirPath}"

        project.copy {
            from project.zipTree(aarFile.path)
            include "**/*"
            into tempDirPath
        }

        File tempFolder = new File(tempDirPath)

        println "temporaryDir -> ${temporaryDir.path}/${variantName}/"

        project.copy {
            from "${tempFolder.path}"
            include "classes.jar"
            into "${temporaryDir.path}/${variantName}/libs"
            def jarName = getJarNameFromDependency(dependency)
            rename "classes.jar", jarName
        }

        project.copy {
            from "${tempFolder.path}/libs"
            include "**/*.jar"
            into "${temporaryDir.path}/${variantName}/libs"
        }

        project.copy {
            from "${tempFolder.path}/jni"
            include "**/*.so"
            into "${temporaryDir.path}/${variantName}/jni"
        }

        project.copy {
            from "${tempFolder.path}/assets"
            include "**/*"
            into "${temporaryDir.path}/${variantName}/assets"
        }

        project.copy {
            from "${tempFolder.path}/res"
            include "**/*"
            exclude "values/**"
            into "${temporaryDir.path}/${variantName}/res"
        }

        processValuesResource(tempFolder.path)
        processRsFile(tempFolder)

        println "tempFolder.deleteDir()"
        tempFolder.deleteDir()
    }

    def getJarNameFromDependency(Dependency dependency) {
        def jarName = ""
        if (null != dependency.group) {
            jarName += dependency.group.toLowerCase() + "-"
        }
        jarName += dependency.name.toLowerCase()
        if(null != dependency.version && !dependency.version.equalsIgnoreCase('unspecified')) {
            jarName += "-" + dependency.version
        }
        jarName += ".jar"

        return jarName
    }

    def processRsAwareFile(File resAwareFile) {
        println "processRsAwareFile"
        RandomAccessFile raf = new RandomAccessFile(resAwareFile, "rw")

        long writePosition = raf.getFilePointer()
        raf.readLine() // Move pointer to second line of file
        long readPosition = raf.getFilePointer()

        byte[] buffer = new byte[1024]
        int bytesInBuffer

        while (-1 != (bytesInBuffer = raf.read(buffer))) {

            raf.seek(writePosition)

            raf.write(buffer, 0, bytesInBuffer)
            readPosition += bytesInBuffer
            writePosition += bytesInBuffer

            raf.seek(readPosition)
        }
        raf.setLength(writePosition)

        raf.seek(0)

        if (gradleVersion.contains("3.2")) {
            String filePath = "${project.projectDir.parentFile.parent}/finalProjname/app/build/intermediates/symbols/${variantName}/R.txt"
            Scanner resourcesOriginal = new Scanner(new File(filePath))

            raf.seek(0) // Move pointer to first line

            String line
            int offset = 0
            while (resourcesOriginal.hasNextLine()) {
                boolean match = false
                line = resourcesOriginal.nextLine()
                println line

                line += "\n"

                byte[] data = line.getBytes()

                raf.seek(offset)
                raf.write(data, 0, data.length)
                offset += data.length

                raf.seek(offset + 1)

            }
        }

        raf.close()
    }

    def processRsFile(File tempFolder) {
        println "processRsFile"

        def mainManifestFile = project.android.sourceSets.main.manifest.srcFile
        def libPackageName = ""

        if (mainManifestFile.exists()) {
            println "processRsFile -> mainManifestFile.exists()"
            libPackageName = new XmlParser().parse(mainManifestFile).@package
        }

        def manifestFile = new File("$tempFolder/AndroidManifest.xml")
        if (manifestFile.exists()) {
            println "processRsFile -> manifestFile.exists()"
            def aarManifest = new XmlParser().parse(manifestFile)
            def aarPackageName = aarManifest.@package

            String packagePath = aarPackageName.replace('.', '/')

            // Generate the R.java file and map to current project's R.java
            // This will recreate the class file
            def rTxt = new File("$tempFolder/R.txt")
            def rMap = new ConfigObject()

            if (rTxt.exists()) {
                println "processRsFile -> rTxt.exists()"
                rTxt.eachLine { line ->
                    //noinspection GroovyUnusedAssignment
                    def (type, subclass, name, value) = line.tokenize(' ')
                    rMap[subclass].putAt(name, type)
                }
            }

            def sb = "package $aarPackageName;" << '\n' << '\n'
            sb << 'public final class R {' << '\n'

            rMap.each { subclass, values ->
                sb << "  public static final class $subclass {" << '\n'
                values.each { name, type ->
                    sb << "    public static $type $name = com.company.native_sdk.R.${subclass}.${name};" << '\n'
                }
                sb << "    }" << '\n'
            }

            sb << '}' << '\n'

            new File("${temporaryDir.path}/rs/$packagePath").mkdirs()
            FileOutputStream outputStream = new FileOutputStream("${temporaryDir.path}/rs/$packagePath/R.java")
            println "R file path -> ${temporaryDir.path}/rs/$packagePath/R.java"
            outputStream.write(sb.toString().getBytes())
            outputStream.close()
        }
    }

    def processValuesResource(String tempFolder) {
        println "processValuesResource"

        File valuesSourceFile = new File("${tempFolder}/res/values/values.xml")
        File valuesDestFile = new File("${temporaryDir.path}/${variantName}/res/values/values.xml")

        if (valuesSourceFile.exists()) {
            println "processValuesResource -> valuesSourceFile.exists"
            if (!valuesDestFile.exists()) {
                println "processValuesResource -> !valuesDestFile.exists"
                project.copy {
                    from "${tempFolder}/res"
                    include "values/*"
                    into "${temporaryDir.path}/${variantName}/res"
                }
            } else {
                println "processValuesResource -> valuesDestFile.exists"
                def valuesSource = new XmlSlurper().parse(valuesSourceFile)
                def valuesDest = new XmlSlurper().parse(valuesDe

Ask*_*kov 5

  1. 在 Android Studio 中将Android 原生库添加到您的项目中:文件 -> 新建 -> 新模块 -> Android 库。

  2. 之后将这个插件https://github.com/kezong/fat-aar-android添加到项目中,并用 'embed' 关键字替换 'implementation'。那么您的项目结构将如下所示: 在此处输入图片说明

  3. flutter_library目录中运行命令flutter build aar -v。注意:flutter_library 包含 Flutter 相关文件,例如 lib/、.android、.ios、pubspec.yaml 等

  4. 根项目目录中运行./gradlew assemble

  5. aar 将位于 library/build/outputs/aar

看我的例子:https : //github.com/askarsyzdykov/native_flutter_lib


Gab*_*tti 3

aar 文件不包含传递依赖项,也没有描述库使用的依赖项的 pom 文件。

这意味着,如果您使用flatDir存储库导入 aar 文件,则还必须在项目中指定依赖项。

我知道这不是您正在寻找的解决方案,但您应该使用 Maven 存储库来解决此问题。在这种情况下,gradle 使用包含依赖项列表的 pom 文件下载依赖项。