SDK 项目结构和构建 Gradle?

SDK Project structure and building with Gradle?

由于 AndroidStudio 正式成为 IDE 用于 Android 开发,我想从 Eclipse 迁移一些项目。

我已经在多个 Android 示例应用程序中共享了 Java 库项目。 在 Eclipse 中,我将 Java 库项目称为项目依赖项,但是当我分发时,Ant 脚本生成库 jar 并将其复制到 Android libs 文件夹。我能够轻松调试我的代码,而无需在分发时公开库源代码或整个项目。

分发后的最终结果必须是带有文档和库 jar 的压缩示例应用程序。关于如何在 Android Studio 中实现它的任何建议?

经过长时间的搜索,我决定编写自己的自定义 gradle 插件,但首先:

项目结构如下所示:

+--------------------+         +---------------------+
|Workspace (Project) |         |Distributed archive  |
|                    |         |                     |
|                    |         |                     |
|    +------------+  |         |  +---------------+  |
|  +-+    SDK     |  |         |  |  SDK Jar lib  |  |
|  | +------------+  |         |  +---------------+  |
|  |                 |         |                     |
|  | +------------+  +--------->  +---------------+  |
|  +->  Sample 1  |  |         |  |  SDK Javadoc  |  |
|  | +------------+  |         |  +---------------+  |
|  |                 |         |                     |
|  | +------------+  |         |  +---------------+  |
|  +->  Sample 2  |  |         |  | Sample source |  |
|    +------------+  |         |  +---------------+  |
|                    |         |                     |
+--------------------+         |  +----------------+ |
                               |  |   Sample APK   | |
                               |  +----------------+ |
                               |                     |
                               +---------------------+

所以我们有几个项目的工作区:一个用于 SDK(纯 Java) 几个示例(Java 或 Android) 这取决于它,我们想分发一个包含以下内容的存档:

  • SDK Jar 文件
  • SDK Java文档
  • 示例应用程序之一(来源 与 SDK jar 捆绑在一起)
  • 示例应用程序 APK
  • 带日期、版本项目和示例名称等的存档名称...

在 Eclipse 中很容易 - 一些依赖项、Ant 脚本和复制到这里,复制到那里,但是现在是迁移到 Intellij 和 Android Studio 的时候了。这是我成功编写自定义 Gradle 脚本的尝试:

apply plugin: CustomProcessor


import com.android.build.gradle.AppPlugin
import com.android.build.gradle.LibraryPlugin

class CustomProcessor implements Plugin<Project> {

    private Project mRoot;
    private List<WrappedProject> mProjects = new ArrayList<>();
    private File mDistribOutput;

    class WrappedProject {
        private final Project project;
        private final Object android;

        WrappedProject(Project project) {
            this.project = project;

            if (isLibrary()) {
                this.android = project.plugins.getPlugin(LibraryPlugin)
                project.logger.info "${project.name}: SDK (API) project detected."
            } else if(isApplication()) {
                project.logger.info "${project.name}: Demo (Sample) project detected."
                this.android = project.plugins.getPlugin(AppPlugin)
            } else {
                project.logger.warn "${project.name}: No Android plugin detected."
                this.android = null
            }
        }

        public boolean isLibrary() {
            return project.plugins.any { p -> p instanceof LibraryPlugin }
        }

        public boolean isApplication() {
            return project.plugins.any { p -> p instanceof AppPlugin }
        }
    }

    @Override
    void apply(Project target) {

        this.mRoot = target
        this.mDistribOutput = new File(target.buildDir, "custom")

        target.configure(target.allprojects) { module ->
            module.afterEvaluate {
                def wrapped = new WrappedProject(module)
                mProjects.add(wrapped)

                if (wrapped.isLibrary()) {
                    addSdkTasks(wrapped.project)
                }

                if (wrapped.isApplication()) {
                    addSampleTasks(wrapped.project)
                }
            }
        }
    }

    void addSdkTasks(Object project) {
        project.android.libraryVariants.all { variant ->
            if (variant.name.equalsIgnoreCase("release")) {
                Task t = project.task("generate${project.name.capitalize()}Javadoc", type: Javadoc, overwrite: true) {
                    description "Generates Javadoc for $project.name."
                    source = variant.javaCompile.source
                    ext.androidJar = "${project.android.sdkDirectory}/platforms/${project.android.compileSdkVersion}/android.jar"
                    classpath = project.files(variant.javaCompile.classpath.files) + project.files(ext.androidJar)
                    exclude '**/BuildConfig.java'
                    exclude '**/R.java'
                    failOnError = false
                }
                t.group = 'custom'

                t = project.task("javadoc${project.name.capitalize()}", type: Jar, overwrite: true) {
                    description "Bundles Javadoc into a JAR file for $project.name."

                    classifier = "javadoc"
                    appendix = project.name
                    baseName = mRoot.name
                    version = project.version

                    from project.tasks["generate${project.name.capitalize()}Javadoc"]

                }
                t.group = 'custom'

                t = project.task("jar${project.name.capitalize()}", type: Jar, overwrite: true) {
                    description "Bundles compiled .class files into a JAR file for $project.name."

                    baseName = mRoot.name
                    appendix = project.name
                    version = project.version

                    dependsOn: [project.tasks.assemble ]
                    from variant.javaCompile.destinationDir
                    exclude '**/R.class', '**/R$*.class', '**/R.html', '**/R.*.html'
                }
                t.group = 'custom'
            }
        }
    }

    void addSampleTasks(Object sample) {
        Task t = sample.task(
                "distribute${sample.name.capitalize()}",
                dependsOn: [sample.tasks.assemble, ":sdk:jarSdk", ":sdk:javadocSdk"],
                overwrite: true,
                type: Zip
         ) {
            baseName = mRoot.name
            appendix = project.name
            version = "v${mRoot.project(':sdk').getVersion()}_${getDate()}"
            classifier = 'src'

            from("${project.buildDir}/outputs/apk/") {
                include '**-debug.apk'
                into "apk"
            }

            from("${mRoot.project(':sdk').buildDir}/docs/javadoc") {
                into "doc"
            }

            from("${mRoot.project(':sdk').buildDir}/libs") {
                into "libs"
            }

            from("${mRoot.project(':sdk').buildDir}/libs") {
                into "sample/app/libs"
            }

            from(sample.zipTree("${sample.rootDir}/sample.zip")) {
                into "/"
            }

            from(sample.projectDir) {
                exclude "build"
                exclude "**.iml"
                exclude "build.gradle"
                into "sample/app"
            }
        }
        t.group = 'custom'
    }

    static def getDate() {
        def date = new Date()
        def formattedDate = date.format('yyyyMMddHHmmss')
        return formattedDate
    }
}

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

要在 Android Studio 中生成示例源作为可打开项目,我使用 sample.zip,它是空的 Android Studio 项目模板。