Отдельный apk для модуля android studio. Android Studio устанавливает APK для каждого модуля

У меня есть проект, созданный с помощью Gradle в Android Studio v 0.3.2. Мой проект имеет зависимости от двух других модулей ( ная библиотека). Структура проекта хорошо определена с помощью файлов build.gradle. Проблема в том, что … когда я запускаю проект на Android-устройстве, я устанавливаю 3 на моем устройстве. Один из них – основной проект (единственный правильный), а два других – импортированные модули (эти два я не хочу устанавливать). Как я могу это достичь? Или что я делаю неправильно?

Структура проекта:

  • MyLibModule
  • MainProject
  • MainProject-> libraries-> MyOtherModule

Где MyLibModule находится на том же пути, что и основной проект, потому что мне также нужен этот модуль в другом проекте.

Просто чтобы быть ясным: вся сборка проекта в порядке , все зависимости в порядке, но почему я получаю 3 APK на моем устройстве?

После целого дня, борющегося с этой проблемой, я нашел причину этого странного поведения. Проблема заключалась в проявлении библиотечного модуля. Прежде чем я переключился на студию Android, я использовал Eclipse. И у меня была testActivity, объявленная в манифесте проекта библиотеки. Удаление всех тестовых действий из манифеста из моих библиотечных модулей решило проблему. Теперь Android Studio устанавливает только APK MainProject.

Некоторый код: манифест MyLibModule:

Изменился на:

…. И то же самое для MyOtherModule.

ПРИМЕЧАНИЕ. Пустой узел приложения должен оставаться в манифесте, чтобы избежать ошибок сборки.

Удалить фильтр намерений из запуска вашей библиотеки

Изменился на

Это потому, что ваши библиотеки определены в файлах build.gradle как приложения, а не библиотеки. Посмотрите на эту строку:

Apply plugin: "android"

И замените его на:

Apply plugin: "android-library"

Возможно, вам придется внести другие изменения в файл сборки, так как не все, что относится к приложениям, может быть указано в файле сборки библиотеки. См. http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-projects для получения дополнительной информации.

Когда мы говорим о плагинах и модульных приложениях, то в первую очередь имеем в виду обычный пользовательский софт. Однако модульный дизайн может быть не менее полезен при разработке руткитов и бэкдоров. Обычными средствами обновлять такой софт слишком палевно, да и не всегда возможно, а вот незаметно подгрузить модуль с новой функциональностью по сети - это пожалуйста. А если вынести ключевую функциональность в модули и удалять их сразу после загрузки, то можно серьезно подпортить жизнь реверсеру.

Вместо введения

В классической Java есть класс под названием java.lang.ClassLoader . Его задача - загружать байт-код указанного класса (файл с расширением.class) в виртуальную машину во время исполнения приложения. Затем можно создать объект этого класса и вызывать его методы с помощью рефлексии. Такой вот способ динамической загрузки кода, который можно использовать для написания приложений с расширяемой функциональностью, или, попросту говоря, поддержкой плагинов.

В Android нет виртуальной машины Java и нет класса ClassLoader, но есть его аналог DexClassLoader, выполняющий ровно ту же функцию, но в отношении байт-кода Dalvik (и файлов.dex вместо.class соответственно). И, в отличие от настольной Java, где проще положить нужный jar-файл в CLASSPATH и не возиться с динамической загрузкой, в Android такой подход дает действительно много преимуществ, главное из которых в том, что функциональность приложения можно расширять и обновлять незаметно для пользователя и ни о чем его не спрашивая. В любой момент твое приложение может скачать файл с классом с сервера, загрузить, а затем удалить файл.

Кроме этого, классы можно хранить прямо в пакете APK и загружать во время старта приложения. Профит здесь в том, что код загружаемых классов будет отделен от кода самого приложения и находиться в APK «не по адресу»; инструменты типа apktool, которыми так любят пользоваться реверсеры, их просто не увидят. С другой стороны, это скорее защита от дурака, так как нормальный реверсер быстро смекнет, что к чему.

Как бы там ни было, динамическая загрузка классов - очень полезная штука при написании не совсем «белых» приложений, поэтому любой security-специалист должен знать, как этот механизм работает и как он используется в троянах.

Простейший пример

// Путь до jar-архива с нашим классом String modFile = "/sdcard/myapp/module.jar"; // Путь до приватного каталога приложения String appDir = getApplicationInfo().dataDir; // Подгружаем файл с диска DexClassLoader classLoader = new DexClassLoader(modFile, appDir, null, getClass().getClassLoader()); // Загружаем класс, создаем объект и пробуем вызвать метод run() с помощью рефлексии try { Class c = classLoader.loadClass("com.example.modules.simple.Module"); Method m = c.getMethod("run", null); m.invoke(c.newInstance(), null); } catch (Exception e) { e.printStackTrace(); }

В целом здесь все просто: код загружает jar-архив /sdcard/myapp/module.jar с нашим классом, загружает из него класс com.example.modules.simple.Module , создает объект и вызывает метод run() . Обрати внимание на три момента:

  • DexClassLoader умеет загружать как «просто» файлы.dex , так и jar-архивы, последние предпочтительнее из-за сжатия и возможности использовать цифровую подпись;
  • второй аргумент конструктора DexClassLoader - это каталог, который он использует для сохранения оптимизированного байт-кода (odex), для простоты мы указываем приватный каталог самого приложения;
  • в качестве аргумента метода loadClass всегда необходимо указывать адрес класса вместе с именем пакета.

Чтобы проверить данный код на работоспособность, создадим простейший модуль:

Package com.example.modules.simple.Module; import android.util.Log; public class Module { public void run() { Log.d("Module", "I am alive!!!"); } }

Не торопись создавать новый проект в Android Studio, можешь накидать этот код в блокноте и собрать его в jar-архив прямо из командной строки:

Javac -classpath /путь/до/SDK/platforms/android-23/android.jar Module.java /путь/до/SDK/build-tools/23.0.3/dx --dex --output=module.jar Module.class

Удостоверься, что каталоги platforms/android-23 и build-tools/23.0.3 существуют, в твоем случае их имена могут отличаться.

Если все пройдет гладко, на выходе ты получишь файл module.jar . Останется только добавить код загрузчика в приложение, положить module.jar на карту памяти, собрать и запустить приложение.

Долой рефлексию

Рефлексия - хорошая штука, но в данном случае она только мешает. Один метод без аргументов с ее помощью вызвать нетрудно, однако, если мы хотим, чтобы наше приложение имело развитый API модулей с множеством методов, принимающих несколько параметров, нужно придумать что-то более удобное. Например, использовать заранее определенный интерфейс, который будет реализовывать каждый модуль.

Применив такой подход к приведенному выше примеру, мы получим следующие три файла:

  1. Файл ModuleInterface.java с описанием API: package com.example.modules; public interface ModuleInterface { public void run(); }
  2. Файл Module.java с реализацией нашего модуля: package com.example.modules.simple.Module; import android.util.Log; public class Module implements ModuleInterface { public void run() { Log.d("Module", "I am alive!!!"); } }
  3. Новый загрузчик модуля (помести в свое приложение): String modFile = "/sdcard/myapp/module.jar"; String appDir = getApplicationInfo().dataDir; DexClassLoader classLoader = new DexClassLoader(modFile, appDir, null, getClass().getClassLoader()); // Загружаем класс и создаем объект с интерфейсом ModuleInterface ModuleInterface module; try { Class class = classLoader.loadClass("com.example.modules.simple.Module"); module = (ModuleInterface) class.newInstance(); } catch (Exception e) { e.printStackTrace(); } module.run()

Это все. Теперь мы можем работать с модулем, как с обычным объектом. Более того, система сама отбракует модули (классы), несовместимые с интерфейсом, еще на этапе загрузки, поэтому нам не придется задаваться вопросами, а есть ли в модуле нужный нам метод.

Когда модулей много

С одним модулем разобрались, но что, если их будет много? Как вести учет этих модулей и не потеряться среди них? На самом деле все просто - для этого можно использовать hashmap. Еще раз изменим загрузчик:

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «сайт», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score!

На недавно прошедшей Google I/O 2018 , среди множества нововведений, объявили также о добавлении нового формата приложений.

Этот формат получил название Android App Bundle и представляет собой улучшенный способ сборки вашего приложения. С его помощью можно легко оптимизировать размер приложения, при этом не нужно будет вносить какие-либо изменения в код. Android App Bundle включает весь скомпилированный код и ресурсы, отсеивая затем то, что не нужно конкретному устройству.

Важно! На данный момент Android App Bundle работает только в preview-версии Android Studio. Последняя версия Android Studio 3.2 Canary доступна .

Формат Android App Bundle

Android App Bundle представляет собой файл (с расширением .aab ), который загружается в Google. Каждый бандл включает скомпилированный код и ресурсы для всех модулей приложения и поддерживаемых конфигураций устройств.

Проще говоря, бандлы это подписанные ZIP-файлы, которые упорядочивают код и ресурсы приложения в модули.

Из этих модулей Google Play генерирует различные APK, которые предоставляются пользователям, такие как: базовые APK, dynamic feature APK, конфигурационные APK и (для устройств, которые не поддерживают разделённые APK) мульти-APK. Каталоги, окрашенные в синий цвет, представляют собой код и ресурсы, которые Google Play использует для создания конфигурационного APK для каждого модуля.

Примечание: бандл нужно создавать для каждого уникального приложения или applicationID . То есть, если вы используете несколько product flavor в своём приложении для создания различных APK, и каждая из этих веток использует уникальный applicationID, то вам нужно будет создать отдельный бандл для каждой ветки.

Код и ресурсы для каждого модуля организованы аналогично стандартным APK, и это логично, поскольку каждый из этих модулей может быть сгенерирован как отдельный APK. Ниже можно увидеть более подробное описание некоторых файлов и каталогов Android App Bundle:

  • base/, feature1/, feature2/ . Каждый из этих каталогов представляет собой модуль приложения. Базовый модуль приложения всегда содержится в базовом каталоге бандла. Директории же с дополнительными особенностями, каждой из которых присваивается специальное имя, находятся отдельно.
  • файлы Protocol Buffer (.pb) . Эти файлы содержат метаданные, которые помогают описать содержимое бандла в магазинах приложений. Например, BundleConfig.pb , находящийся в корневом каталоге бандла, предоставляет информацию о самом бандле, например, какая версия build tools используется для сборки. Другие файлы, такие как recourse.pb и native.pb , описывают, как определённый код и ресурсы должны использоваться для различных конфигураций устройств. Google Play использует эту информацию для генерации APK, оптимизированного для устройства пользователя.
  • manifest/ . В отличие от APK, бандлы хранят файл AndroidManifest.xml каждого модуля в отдельном каталоге.
  • dex/ . В отличие от APK, бандлы хранят DEX-файлы каждого модуля в отдельном каталоге.
  • root/ . Этот каталог хранит файлы, которые позже перемещаются в директорию root любого APK, который включает в себя модуль, в котором находится этот каталог. Например, каталог base/root/ бандла может включать ресурсы Java, которые загружаются приложением с помощью использования Class.getResources() . Эти файлы позже перемещаются в директорию root APK приложения и каждого мульти-APK, которые генерирует Google Play. Пути в этом каталоге также сохраняются, то есть, подкаталоги тоже перемещаются вместе с root.
    Примечание: если содержимое этого каталога конфликтует с другими файлами и каталогами в корне APK, Play Console отклонит загрузку бандла. Например, вы не сможете включить каталог root/lib/, поскольку он будет конфликтовать с каталогом lib, уже находящимся в APK.
  • res/, lib/, assets/. Эти каталоги идентичны тем, что используются в стандартном APK. При загрузке приложения, Google Play проверяет в этих каталогах и пакетах только те файлы, которые соответствуют конфигурации целевого устройства.

Сборка App Bundle с помощью Android Studio

Создание бандла с помощью Android Studio очень похоже на создание APK. Для сборки достаточно выбрать в меню Build - Build Bundle(s)/APK(s) > Build Bundle(s) и IDE создаст бандл для выбранного варианта сборки и разместит его в каталоге //build/outputs/bundle/ .

Если создаётся бандл для debug-версии приложения, Android Studio автоматически подпишет бандл с помощью отладочного ключа подписи. Для загрузки бандла в Google Play он должен быть подписан.

После того, как Android Studio завершит создание подписанного бандла, его можно будет открыть и проанализировать. Анализ бандла позволяет проверить содержимое и работает аналогично APK Analyzer .

Для создания App Bundle IDE использует тот же инструмент с открытыми исходным кодом, называемый bundletool , который Google Play использует для последующего преобразования бандла в подписанные APK.

Прежде чем загрузить бандл в консоль Google Play, его нужно подписать. Чтобы создать подписанный App Bundle, нужно выполнить следующие действия:


После того, как Android Studio завершит создание подписанного бандла, его можно будет найти и проанализировать, выбрав соответствующую опцию во всплывающем уведомлении. Если был выбран экспорт ключа подписи, то к нему можно будет быстро перейти, щёлкнув по стрелке вниз в правом нижнем углу всплывающего уведомления, чтобы развернуть его, и затем выбрав Show Exported Key File .

Загрузка App Bundle в консоль Google Play

После того, как бандл создан, его можно загрузить в Google Play для проверки, тестирования или публикации приложения. Прежде чем приступить к работе, следует соблюсти следующие условия:

  1. Зарегистрироваться в программе Google Play App Signing .
  2. Если приложение включает dynamic feature modules, то его можно загружать и тестировать через внутренний тестовый трек консоли Google Play. Однако чтобы опубликовать приложение, нужно принять программу Dynamic Feature, которая на данный момент находится в стадии бета-версии.
  3. Google Play поддерживает загрузку приложений размером не более 100 МБ .

Анализ APK с помощью проводника

Когда бандл загружен, Google Play автоматически генерирует разделённые APK и мульти-APK для всех конфигураций устройств, поддерживаемых приложением. В Play Console можно можно использовать App Bundle Explorer для просмотра всех вариантов APK, сгенерированных Google Play; анализа данных, таких как поддерживаемые устройства и экономия размера APK; загрузки созданных APK для тестирования.

Обновление приложения

После загрузки приложения в Play Console, для обновления приложения достаточно повысить код версии, а также создать и загрузить новый бандл. Затем Google Play сгенерирует обновлённые APK с новым кодом версии и будет предоставлять их по мере необходимости.

Заключение

Использование Android App Bundle даёт большие преимущества для оптимизации APK приложений. С помощью этого способа мы обновили одно из наших приложений, "Менеджер паролей от Wi-Fi сетей ", заменив в нём стандартный APK на App Bundle. Таким образом, размер APK файлов уменьшился на целый мегабайт, что является очень хорошим результатом.

Кроме того, Google на данный момент тестирует дополнение к App Bundle, Dynamic feature modules, с помощью которого можно разбивать базовый APK на части, которые будут докачиваться при необходимости, пока что эта технология находится в бета-версии.

Возможно, единственным недостатком банлдов на данный момент назвать необходимость использовать preview-версию Android Studio, однако эта проблема временная.

Для создания модуля в Android Studio выбираете

File > New > New Module

Далее есть 2 варианта. Если вы планируете создать «чистую» java-библиотеку, в качестве типа указывайте Java Library , в итоге код такой библиотеки скомпилируется в JAR файл. Это удобно, так как вы сможете использовать ее не только в Android приложениях. Если же вы собираетесь использовать android-специфичные вещи и вам понадобятся классы из пакетов android.* , то создавайте Android Library , которая при компиляции собирается в AAR файл. В данном случае вам понядобится последний вариант.

ВАЖНО: В minSDKVersion (файлы build.gradle модулей) приложения должен совпадать или быть больше, чем указанный в модуле библиотеки.Указанные buildToolsVersion должны быть установлены в Android-SDK. Каждый модуль библиотеки генерирует свой класс ресурсов (*.R.class). Когда Android библиотеки добавляются в проект и происходит его сборка, то их ресурсы сливаются, что может привести к конфликтам. Поэтому в документации определены следующие соглашения:

  • Если ID ресурса приложения совпадает с ID ресурса в библиотеке, то используется ресурс приложения
  • Если ID ресурса совпадает в разных библиотеках, то используется ресурс библиотеки, которая указана первой в списке зависимостей (находится выше в блоке dependecies)
  • Для избежания описанных выше конфликтов рекомендуется использовать префикс или другую последовательную схему именования ресурсов, которая будет уникальна для каждого из модулей (или уникальна для всего приложения вцелом)

Подключение Android библиотеки

Подключаются Android библиотеки как зависимости (если библиотека была создана как отдельный проект в AndroidStudio). Тут есть так же 2 варианта:

1.Либо добавить скомпилированный AAR (или JAR) файл:

File > New Module - Import .JAR/.AAR Package > Next - вводите путь до ARR (или JAR) файла > Finish

2.Либо импортировать библиотеку из исходников:

File > New > Import Module - вводите путь до директории, в которой находятся исходники библиотеки > Finish

ВАЖНО: убедиться, что имя Android библиотеки было добавлено в settings.gradle

Include ":app", ":core", ":personal", ":client"

и появилась в блоке dependencies файла build.gradle приложения

Dependencies { compile project(":core") compile project(":personal") compile project(":client") }

Android библиотеки могут содержать в себе ресурсы, другие JAR библиотеки, собственный AndroidManifest.xml .

Структура скомпилированной Android библиотеки

Скомпилированная Android библиотека представляет собой обычный zip архив с расширением.arr , который содержит следующие обязательные файлы и директории:

  • /AndroidManifest.xml
  • /classes.jar
  • /res/
  • /R.txt

и необязательные:

  • /assets/
  • /libs/name .jar
  • /jni/abi_name /name .so (где abi_name один из поддерживаемых Android ABIs)
  • /proguard.txt
  • /lint.jar