Создание standalone библиотеки под android

В этой статье я расскажу как создать библиотеку, которая использует другие библиотеки и при этом уместить всё в одном jar.

Допустим мы пишем библиотеку, она использует другие библиотеки и, в частности, для примера, support library. Если бы мы использовали maven, то в pom файле просто прописали зависимости и не парились. Но что если нашу библиотеку будут использовать люди, которые не пользуются системами сборок или пользуются ant' ом?

Мы можем положить зависимости рядом с нашим jar. Но тогда неизбежны конфликты библиотек разных версий, например, если мы используем одну версию support library, а в самом приложении другая. Тогда придется руками одну из них удалять.

Можно пойти другим путем, вспомним что jar это обычный zip файл. Мы распакуем все зависимости, получим байт-код в виде .class файлов, далее компилируем код нашей библиотеки, кладем все .class файлы в одно место и собираем из них jar. Но если в приложении используются те же библиотеки что и у нас, то получим ошибку что в проекте два одинаковых класса.

Для решения этой проблемы воспользуемся утилитой jarjar. Она переименовывает все классы в jar файле. На примере support library — все классы находятся в пакете android.support.v4

Пример использования:
java -jar jarjar.jar process <rulesFile> <inJar> <outJar>
rulesFile - файл с правилами переименования
inJar, outJar - тут все понятно

Создадим правило для переименования классов android.support.v4.* в inner.android.support.v4.*
rule android.support.v4.** inner.android.support.v4.@1
и сохраним в файл rules.txt

Запускаем
java -jar jarjar.jar rules.txt android-support-v4.jar android-support-v4-renamed.jar

В итоге получили jar'ку с переименованными классами. Далее мы распаковываем все наши зависимости с переименованными классами и компилируем. Получаем библиотеку, которая содержит весь код нужный для выполнения.

Наша библиотека содержит код всех зависимостей, которые могут весить немало. Чтобы уменьшить размер применим proguard для удаления неиспользуемых классов и методов.

И в заключение небольшой скрипт на gradle который всё это делает

apply plugin: 'java'

defaultTasks 'proguard'

task unpackJars(dependsOn: compileJava) {
    //распаковываем классы из библиотек
    file('libs').listFiles().each { File file ->
        if (file.name.endsWith('.jar')) {
            copy {
                from(zipTree(file.path))
                into('build/classes/main')
            }
        }
    }
    //собираем jar'ку
    tasks.jar.execute()
}

task proguard(type: proguard.gradle.ProGuardTask, dependsOn: unpackJars) {
    injars 'build/libs/library.jar'
    outjars 'build/libs/proguard_library.jar'
    libraryjars '/Applications/my/Android Studio.app/sdk/platforms/android-10/android.jar'

    //укажем что классы нашей библиотеки удалять не нужно
    keep 'class com.CasualSoftware.classloader.library.**'
}

dependencies {
    compile 'com.google.android:android:2.2.1'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

Вои и всё, после выполнения скрипта мы получим proguard_library.jar, который можно добавлять к приложению и использовать.

В моём случае библиотека использует support library и dropbox sdk, и весит 50 кБ.

Этим же способом можно уменьшать размер вашего apk, все зависимости можно подключать не как jar файлы, а распаковывать в .classes и применять proguard, тогда он удалит неиспользуемые классы из библиотек. Так можно подключать к проекту тяжелые фреймворки не боясь увеличения размера apk
  • +8
  • 12,9k
  • 8
Поделиться публикацией

Комментарии 8

    0
    Пишете в комментариях что вы думаете об этом способе
      0
      я правильно понял, что Вы из ~800 килобайт библиотек удалили лишние классы и получили 522 байта? и все работает?)
        0
        Да, proguard удаляет неиспользуемые классы(методы и переменные), а так как они не используются то все работает
          +2
          что же входит в 522 байта?
        +1
        Да, извиняюсь, забыл поставить одну звездочку и proguard удалил все кроме одного класса
        keep 'class com.CasualSoftware.classloader.library.**' — должно быть так
        Теперь библиотека весит 50кб
          +1
          Мы можем положить зависимости рядом с нашим jar. Но тогда неизбежны конфликты библиотек разных версий, например, если мы используем одну версию support library, а в самом приложении другая. Тогда придется руками одну из них удалять.

          А разве в этом случае нам не нужно просто положить support library с последней версией которую используют другие библиотеки или наше приложение? Там же нет чего-либо ломающего обратную совместимость со старыми версиями.

          Вообще идея интересная, но не совсем понятно на счет неиспользуемых классов во фреймворках. Прогард может провести агрессивную оптимизацию, например если библиотека инстанциирует объекты через рефлекшн и потом в райнтайме упадет ClassNotFoundException в стороннем sdk.
            0
            А разве в этом случае нам не нужно просто положить support library с последней версией которую используют другие библиотеки или наше приложение? Там же нет чего-либо ломающего обратную совместимость со старыми версиями.

            Ну да, в случае support library, можно просто положить новую версию. Но не факт что другие библиотеки поддерживают обратную совместимость.

            А на счет reflection, там не все так плохо
            Does ProGuard handle Class.forName calls?

            Yes. ProGuard automatically handles constructs like Class.forName(«SomeClass») and SomeClass.class. The referenced classes are preserved in the shrinking phase, and the string arguments are properly replaced in the obfuscation phase.
            With variable string arguments, it's generally not possible to determine their possible values. They might be read from a configuration file, for instance. However, ProGuard will note a number of constructs like "(SomeClass)Class.forName(variable).newInstance()". These might be an indication that the class or interface SomeClass and/or its implementations may need to be preserved. The developer can adapt his configuration accordingly.
              0
              Уберите статью из хаба «Android», пожалуйста. Те, кому эта статья может пригодится, и так её увидят, потому что подписаны на хаб «Разработка под Android»

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое