Використання ndk в android studio

В даний час серед Android-девелоперів велику популярність має середовище розробки Android Studio, заснована на IntelliJ IDEA від JetBrains. Однак, при використанні даної IDE, можуть виникнути проблеми при розробці додатків, що використовують нативний код, так як Android NDK розрахований переважно на використання IDE Eclipse і ADT.

Так як існує безліч статей, що описують роботу з NDK, я не буду використовувати в якості прикладу складні бібліотеки, а обмежуся лише найпростішим прикладом hello-jni. Вихідний код цього прикладу можна знайти в каталозі <путь_к_ndk>/ Samples / hello-jni

У середовищі Eclipse особливих проблем з використанням NDK не виникало. Каталог проекту виглядає приблизно так:

Використання ndk в android studio

Рис.1 Головний каталог проекту для Eclipse

Нас цікавлять каталоги jni і libs. Каталог jni містить вихідні коди на нативних мовах (* .c; * .cpp), заголовки (* .h), makefiles (* .mk). Довго зупинятися на призначення даних файлів не буду, так як цьому присвячено чимало матеріалів і статей. Згадаю лише, що jni означає java native interface. Саме через цей інтерфейс здійснюється виклик нативних процедур з коду на java. Тому не забувайте підключати бібліотеку в ваші c / c ++ файли і пам'ятайте про правильне синтаксисі функцій, які будуть викликатися через jni. Наприклад, в моєму випадку додаток має package name:
evi.ntest
тому опис функції виглядає так:

де jstring - назва типу даних c, відповідно до типу string в java, Java - в даному випадку службовий префікс, який показує язик, з якого буде викликана функція, evi_ntest - ім'я пакета, який буде викликати функцію, MainActivity - ім'я активують, з якої буде викликатися функція, stringFromJNI - назва функції.
У java-коді опис даної функції виглядає набагато простіше:

Не забувайте також вказувати використовувані файли коду в .mk файлах. Для початку можете скористатися .mk файлами з прикладів NDK, модифікуючи назви файлів, але в подальшому рекомендую вивчити їх структуру.

Каталог libs містить готові бінарні бібліотеки для різних архітектур процесорів (за замовчуванням - armeabi). Динамічна бібліотека являє собою файл з розширенням .so, статична - файл з розширенням .a. Для отримання даних бібліотек потрібно компіляція вихідних кодів за допомогою Android NDK. В Unix-системах (в моєму випадку - Mac OSX) для цього потрібно в терміналі ввести такі рядки:

При цьому NDK автоматично компілює вихідні коди з папки jni і поміщає отримані бібліотеки в libs / armeabi (також можна за допомогою параметрів командного рядка задати компіляцію під x86, mips, arm v7-neon процесори).
При використанні Windows потрібно скористатися додатковими утилітами, можливо - плагінами для MS Visual Studio.
У будь-якому випадку, не має значення, яким саме способом отримано готові бібліотеки, важливий факт, що якщо в папці з проектом знаходиться підкаталог libs, Eclipse при складанні автоматично включає його вміст в APK-файл.

Переходжу до основного розділу статті - налаштування IDE Android Studio для роботи з нативним кодом.

Середа Android Studio за замовчуванням збирає APK за допомогою gradle. Даний збирач має широкі можливості кастомізації, але при стандартних налаштуваннях gradle не включає нативні бібліотеки в APK-файл.

Розглянемо часткову структуру проекту в Android Studio:

Рис.2 Шлях до вихідного коду проекту Android Studio.

При роботі у мене виникло логічне бажання розмістити папку jni в каталозі src / main, так як саме там зберігаються всі інші файли з вихідним кодом. Зрозуміло, читач може розміщувати каталог jni там, де йому зручно. Головне - не забути зібрати бінарні бібліотеки за допомогою NDK (повторюся, в UNIX системах для цього потрібно в терміналі перейти до каталогу, який містить jni, потім викликавши виконуваний файл ndk-build, що лежить в папці з NDK, прописавши повний шлях до нього в цьому ж терміналі, в MS Windows потрібно використовувати додаткові утиліти). Проблема ж полягає в тому, що за замовчуванням gradle НЕ буде упаковувати в APK бібліотеки.

Однак gradle нескладно налаштувати на включення в збірку java-бібліотек (файлів * .jar). Варто зауважити, що jar-файли являють собою zip-архіви, що містять будь-які ресурси, а також об'єктний код. Таким чином, для включення бінарних бібліотек * .so і * .a досить упакувати їх в jar-файл.
Робиться це так:

  • Перейменовуємо папку libs, що містить наші бінарні бібліотеки в lib
  • Стискаємо дану папку будь-яким zip-архіватором
  • Міняємо розширення отриманого файлу на .jar

Отриману бібліотеку можна підключити на етапі складання проекту, при цьому отриманий APK-файл буде включати виконавчі бібліотеки, а додаток - викликати процедури, написані на нативному коді.
Дане питання не раз обговорювалося на різних англомовних форумах, наприклад Stack Overflow:

Однак, дана інформація досить коротка, розрізнена і вимагає від читача певних знань синтаксису gradle. Мета моєї статті - надати читачам докладний російськомовне пояснення, доступне навіть тим, хто тільки почав працювати з Android Studio і gradle.

Давайте розглянемо 2 способи упаковки бібліотек: ручний і автоматичний.

Ручний спосіб упаковки:

Даний спосіб досить незручний, однак має місце на існування. Допустимий випадок застосування на практиці: наявність готової бібліотеки і відсутність необхідності її змінювати. В такому випадку, описані нижче операції буде потрібно виконати лише один раз.

Даний файл спочатку виглядає приблизно так:

У самому низу є розділ dependencies. Туди слід додати такий рядок:

compile fileTree (dir: 'src / main /', include: '* .jar')

Ознайомтесь з модифікованим файлом build.gradle, що б не переплутати dependencies і buildscript.dependencies.

Читач швидше за все помітить, що даний спосіб вельми незручний при наявності необхідності частих змін в нативному коді, так як після кожної перекомпіляції необхідно видаляти старий jar-файл, перейменовувати папку libs в lib, архівувати її, змінювати розширення архіву. Тому скористаємося міццю gradle і автоматизуємо процес.

Складальник пакетів gradle дозволяє створювати завдання (функції), також в його можливості входить створення різних типів архівів, у тому числі zip. Скористаємося цим і додамо в build.gradle (розташування даного файлу розглянуто вище) такі рядки:

from fileTree (dir: 'src / main / libs', include: '** / *. so')

Вам слід використовувати:

from fileTree (dir: 'src / main / libs', include: '** / *. *')

Далі залишається додати в розділ dependencies рядок:

compile fileTree (dir: "$ buildDir / native-libs", include: 'native-libs.jar')

Під час складання ця команда включить вміст створеної програмно бібліотеки native-libs.jar в APK-файл.

Приклад build.gradle з даними кодом:

Якщо все зроблено правильно, то Android Studio в ході складання проекту автоматично створить в каталозі build правильну бібліотеку і включить її в готову програму. Таким чином, після кожної перекомпіляції нативного коду відпадає необхідність здійснення будь-яких додаткових дій і налаштувань, gradle зробить все сам.

коротке резюме
  1. Відкриваємо папку "<путь_к_проекту>/<имя_проекта>Project /<имя_проекта>/ Src / main "і створюємо там підпапку jni.
  2. Відкриваємо файл "<путь_к_проекту>/<имя_проекта>Project /<имя_проекта>/build.gradle ", модифікуємо розділ dependencies, після чого додаємо туди код:

Для включення також статичних бібліотек * .a (при їх наявності) міняємо рядок

from fileTree (dir: 'src / main / libs', include: '** / *. so')

from fileTree (dir: 'src / main / libs', include: '** / *. *')

  • В папці jni розміщуємо файли * .mk, * .h, * .c, пишемо нативний код.
  • Відкриваємо папку "<путь_к_проекту>/<имя_проекта>Project /<имя_проекта>/ Src / main "в терміналі.
  • Вводимо в терміналі команду <путь_к_ndk>/ Ndk-build
  • Запускаємо проект.
  • Важливо!
    Дана інструкція призначена для операційних систем Unix (в моєму випадку - MacOSX). Для операційної системи MS Windows пункти 4 і 5 не актуальні, так як для компіляції нативних бібліотек потрібні додаткові утиліти. Також, швидше за все, буде доцільним змінити шляху зберігання бібліотек на більш зручні і врахувати це в скрипті збірки.

    Вдалого Вам нативного програмування, головне - кожен раз не забувайте себе запитувати, чи варто використовувати нативний код. Цілком можуть бути java-аналоги, використання яких простіше, а в більшості випадків - краще, так як скорочується час розробки, поліпшується понимаемость коду іншими, знижується складність архітектури додатку, а потужності сучасних пристроїв вистачає на виконання більшості завдань навіть в Dalvik VM.

    Схожі статті