Как создать Jar библиотеки Android с помощью gradle без публичного раскрытия исходного кода?

Я хотел бы создать Jar из проекта библиотеки Android. Он устроен следующим образом:

ProjectName
    \- lib
    |   \- lib
    |       \- armeabi
    |           \- libNativeFirst.so
    |           \- libNativeSecond.so
    \- src
        \- main
            \- java
                \- com.package.sdk
                    \- PackageSDK.java

Я хотел бы, чтобы все это было упаковано в Jar, но без раскрытия исходного кода, присутствующего в PackageSDK.java.

Я настроил свой файл build.gradle следующим образом:

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 в каталоге проекта, файл Jar создается в ProjectName\build\libs с именем ProjectName.jar. Его структура следующая:

ProjectName.jar
    \- lib
    |   \- armeabi
    |       \- libNativeFirst.so
    |       \- libNativeSecond.so
    \- com
        \- package
            \- sdk
                \- PackageSDK.java

Я хотел бы, чтобы скомпилированный PackageSDK.class был включен вместо файла PackageSDK.java при выполнении задачи jar. Что я могу изменить, чтобы добиться этого?

Редактировать:

По предложению Бена Манеса я изменил конфигурацию sourceSets на следующую:

sourceSets {
    main {
        java {
            srcDir 'src/main/java'
        }
        resources {
            srcDir 'src/../lib'
        }
        output {
            classesDir 'build/classes'
            resourcesDir 'build/javaResources'
        }
    }
}

А задача jar к следующему:

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.


person BVB    schedule 26.09.2013    source источник
comment
попробуйте использовать android.sourceSets.main.output для файлов классов вместо источников, чтобы создать банку из   -  person Ben Manes    schedule 26.09.2013
comment
Что я должен установить sourceSets { main { output { srcDir '_____' } } }?   -  person BVB    schedule 26.09.2013
comment
подключаемый модуль Android не использует подключаемый модуль Java? Большинство из них предварительно настроены. Может быть, это должно быть output.dirs. Взгляните на руководство пользователя Gradle   -  person Ben Manes    schedule 26.09.2013
comment
Я думаю, что они чем-то отличаются, но я не уверен на 100%. Я пробовал это с .dirs, но у него была та же проблема. Я обнаружил, что могу просто использовать файл classes.jar из build/bundles/release и переименовать его в ProjectName.jar, но я бы хотел, чтобы мне не приходилось этого делать.   -  person BVB    schedule 26.09.2013


Ответы (2)


Примечание. Ответ был отредактирован. Пожалуйста, ознакомьтесь с обновлением от 28.07.2014 ниже.

Вот решение, к которому я пришел. Возможно, есть способ лучше, но я его пока не нашел.

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 makeJar создает ProjectName.jar в каталоге build/libs. Структура этой баночки следующая:

ProjectName.jar
    \- lib
    |   \- armeabi
    |       \- libNativeFirst.so
    |       \- libNativeSecond.so
    \- com
        \- package
            \- sdk
                \- PackageSDK.class

Это именно тот результат, который мне был нужен. Теперь я могу успешно использовать ProjectName.jar в других проектах.

РЕДАКТИРОВАТЬ: хотя я могу использовать полученный jar в проектах в Android Studio, я не могу сделать это в проектах, созданных в ADT, из-за предупреждения о наличии собственного кода внутри файла jar. Предположительно в настройках стоит флаг отключения этой проверки, но он работает некорректно. Таким образом, если вы хотите создать библиотеку, использующую собственный код, тем, кто использует ADT, придется вручную скопировать каталог armeabi в libs/.

28.07.2014 Обновление:

Начиная с Android Studio 0.8.0, каталоги вывода Gradle были изменены, и описанная выше конфигурация не будет работать. Я изменил свою конфигурацию на следующую:

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/.

person BVB    schedule 26.09.2013
comment
Это работало раньше, но не с последней версией студии, не могли бы вы проверить ее и обновить. - person pyus13; 25.07.2014
comment
@ pyus13 Я обновил ответ с изменениями конфигурации, чтобы он работал с последней версией Android Studio. - person BVB; 28.07.2014
comment
Спасибо большое. Но я все еще в замешательстве и хотел узнать, почему каталог имеет значение, если мы пишем задачу самостоятельно. Я знал, что выходной каталог изменился в 0.8.0 для задачи по умолчанию (создание apk-ссылки и все такое), но как это может повлиять на задачу, которую мы написали, я хочу знать только для себя? - person pyus13; 29.07.2014
comment
Вот именно — задачи Gradle перемещались по файлам из определенных каталогов. Поскольку каталоги изменились, задачи нужно было обновить, чтобы посмотреть на правильные. В остальном все примерно так же. - person BVB; 29.07.2014
comment
+1 за поддержание этого ответа в актуальном состоянии. Пожалуйста, продолжайте в том же духе :) - person Martin Konecny; 01.08.2014
comment
@BVB Не могли бы вы помочь мне решить мой вопрос? stackoverflow.com/questions/30520314/ - person Jerikc XIONG; 29.05.2015
comment
class.jar не был создан в моем каталоге сборки, однако классы были там, и я смог встряхнуться с помощью этой команды. jar задачи (тип: Jar) { из ('сборка/промежуточные/классы/выпуск') } - person rlshep; 18.10.2015
comment
@rlshep, вы пробовали предложение в обновлении от 28.07.2014? Каков был результат? - person BVB; 20.10.2015
comment
@BVB Ничего не было создано. Я предполагаю, что в каталоге сборки нет class.jar. - person rlshep; 20.10.2015
comment
@rlshep А, так ваш процесс сборки не создал каталог build/intermediates/bundles/release/, а только build/intermediates/classes/release/? - person BVB; 20.10.2015
comment
@BVB Можем ли мы сделать class.jar вне каталога проекта где-то еще на диске? - person Sameer Hussain; 10.12.2015
comment
Теоретически у вас должно получиться. Измените задачу makeJar в соответствии с вашими потребностями. Вы можете указать там разные пути и имена файлов. - person BVB; 10.12.2015
comment
Похоже, это больше не работает с Gradle 4.4, какие-нибудь советы о том, как решить проблему? Спасибо! - person Aweb; 25.04.2018
comment
Извините, у меня не было ни необходимости, ни возможности экспериментировать с более новыми версиями Gradle. Какие ошибки вы видите? - person BVB; 16.06.2021

Просто чтобы добавить небольшую альтернативу ответу @BVB (хотя и в значительной степени основанному на нем), вот что мне нужно было сделать, чтобы вывести jar myapp-api.jar, который был предназначен только для проекта Java, который имел дело с остальным взаимодействием API. Это зависит от Android.jar, поэтому необходимо использовать apply plugin: 'com.android.application', а не просто apply plugin: 'java'

Вызов ./gradlew build jar из myJavaAPIProject для сборки и создания .jar для myJavaAPIProject/build/libs/myapp-api.jar

build.gradle

//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

<?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"
    />
person scottyab    schedule 13.01.2015