如何在不公开源代码的情况下使用 gradle 创建 Android Library Jar?

How to create an Android Library Jar with gradle without publicly revealing source code?

我想从一个 Android 库项目中创建一个 Jar。设置方式如下:

1
2
3
4
5
6
7
8
9
10
11
ProjectName
    \\- lib
    |   \\- lib
    |       \\- armeabi
    |           \\- libNativeFirst.so
    |           \\- libNativeSecond.so
    \\- src
        \\- main
            \\- java
                \\- com.package.sdk
                    \\- PackageSDK.java

我希望将所有这些都打包在一个 Jar 中,但不透露 PackageSDK.java 中存在的源代码。

我这样设置我的 build.gradle 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}

apply plugin: 'android-library'

repositories {
    mavenCentral()
}

android {
    compileSdkVersion 18
    buildToolsVersion"18.0.1"

    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 18
    }

    sourceSets {
        main {
            java {
                srcDir 'src/main/java'
            }
            resources {
                srcDir 'src/../lib'
            }
        }
    }
}

task jar(type: Jar) {
    from android.sourceSets.main.allSource
}

当我在项目目录中运行 gradlew clean jar 时,会在 ProjectName\\build\\libs 中创建一个名为 ProjectName.jar 的 Jar 文件。它的结构如下:

1
2
3
4
5
6
7
8
9
ProjectName.jar
    \\- lib
    |   \\- armeabi
    |       \\- libNativeFirst.so
    |       \\- libNativeSecond.so
    \\- com
        \\- package
            \\- sdk
                \\- PackageSDK.java

我希望在执行 jar 任务时包含已编译的 PackageSDK.class 而不是 PackageSDK.java 文件。我可以改变什么来实现这一点?

编辑:

根据 Ben Manes 的建议,我将 sourceSets 的配置更改为以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sourceSets {
    main {
        java {
            srcDir 'src/main/java'
        }
        resources {
            srcDir 'src/../lib'
        }
        output {
            classesDir 'build/classes'
            resourcesDir 'build/javaResources'
        }
    }
}

jar任务如下:

1
2
3
task jar(type: Jar) {
    from android.sourceSets.main.output
}

Gradle 现在给我这个输出:

Could not find method output() for arguments [build_25r1m0a3etn5cudtt5odlegprd$_run_closure2_closure9_closure10_closure13@138532dc] on source set main.


注意:答案已被编辑。请参阅下面的 2014 年 7 月 28 日更新。

这是我最终想出的解决方案。可能有更好的方法可用,但我还没有找到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
android {
    compileSdkVersion 18
    buildToolsVersion"18.0.1"

    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 18
    }

    sourceSets {
        main {
            java {
                srcDir 'src/main/java'
            }
            resources {
                srcDir 'src/../lib'
            }
        }
    }
}

task clearJar(type: Delete) {
    delete 'build/libs/ProjectName.jar'
}

task makeJar(type: Copy) {
    from('build/bundles/release/')
    into('build/libs/')
    include('classes.jar')
    rename ('classes.jar', 'ProjectName.jar')
}

makeJar.dependsOn(clearJar, build)

运行 gradlew makeJarbuild/libs 目录中创建一个 ProjectName.jar。这个jar的结构如下:

1
2
3
4
5
6
7
8
9
ProjectName.jar
    \\- lib
    |   \\- armeabi
    |       \\- libNativeFirst.so
    |       \\- libNativeSecond.so
    \\- com
        \\- package
            \\- sdk
                \\- PackageSDK.class

这正是我需要的结果。我现在可以在其他项目中成功使用 ProjectName.jar

编辑:虽然我可以在 Android Studio 中的项目中使用生成的 jar,但我不能在 ADT 中创建的项目中这样做,因为有关 jar 文件中存在本机代码的警告。据说有一个标志可以关闭此签入设置,但它无法正常工作。因此,如果你想创建一个使用本机代码的库,那些使用 ADT 的人必须手动将 armeabi 目录复制到 libs/.

2014 年 7 月 28 日更新:

从 Android Studio 0.8.0 开始,Gradle 输出目录已更改,上述配置将不起作用。我已将配置更改为以下内容:

1
2
3
4
5
6
7
8
9
10
task clearJar(type: Delete) {
    delete 'build/outputs/ProjectName.jar'
}

task makeJar(type: Copy) {
    from('build/intermediates/bundles/release/')
    into('build/outputs/')
    include('classes.jar')
    rename ('classes.jar', 'ProjectName.jar')
}

重要提示:请注意,ProjectName.jar 现在将被放入 build/outputs/ 而不是 build/libs/


只是为@BVB 的答案添加一个轻微的替代方案(尽管很大程度上基于它),这是我必须输出一个 jar myapp-api.jar 的方法,该 jar 用于处理 rest API 交互的纯 Java 项目。它依赖于 Android.jar,因此需要使用 apply plugin: 'com.android.application' 而不仅仅是 apply plugin: 'java'

从 myJavaAPIProject 调用 ./gradlew build jar 来构建和生成 .jar 到 myJavaAPIProject/build/libs/myapp-api.jar

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//Even though this is a Java project, we need to apply the android plugin otherwise it cannot find the SDK/android.jar and so cannot compile
apply plugin: 'com.android.application'

dependencies {
    //this ensures we have gson.jar and anything else in the /lib folder
    compile fileTree(dir: 'lib', include: '*.jar')
}

repositories {
    mavenCentral()
}
android{
    compileSdkVersion 21
    buildToolsVersion"21.0.1"

    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 21
    }

    sourceSets {
        main {
            java {
                //points to an empty manifest, needed just to get the build to work
                manifest.srcFile 'AndroidManifest.xml'
                //defined our src dir as it's not the default dir gradle looks for
                java.srcDirs = ['src']

            }
        }
    }

    //enforce java 7
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

//Actually created the .jar file
task jar(type: Jar) {
    //from android.sourceSets.main.java
    from 'build/intermediates/classes/release/'
    archiveName 'myapp-api.jar'
}

AndroidManifest.xml

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!-- this is a dummy file needed to ensure gradle validates and builds ok -->
<manifest
    package="com.myapp.android"
    />