Решил не так давно написать что-то родное для своего андроида. Но проблема в том, что на Java я программировать не умею, да и не хочу.
Вспомнил, что существует набор компиляторов Android NDK, который, со слов Гугла, позволяет писать приложения на C/С++ или даже на других языках. На самом деле это немного не так, но об этом позже. Обрадовался и решил написать небольшой скрипт на своём любимом Си + скомпилировать некоторые полезности.
Скачал этот NDK (а он в 7zip SFX, фу и весит гига 2 в распакованном виде), попробовал что-то скомпилировать под arm.
Готовый скрипт для тех, кому надо быстро что-нибудь собрать. Можно адаптировать под Makefile для сборки всяких свободных библиотек.
#!/bin/bash NDK_DIR=/mnt/android-ndk-r10e/ CC_PATH=$NDK_DIR/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86/bin/ # для GCC 4.9, доступен также 4.8 и clang CC=$CC_PATH/arm-linux-androideabi-gcc # есть и другие утилиты из списка SYSROOT=$NDK_DIR/platforms/android-21/arch-arm/ # android-21 - это версия 5.0, есть и более старые $CC -I $SYSROOT/usr/include/ --sysroot=$SYSROOT ваша_прога.c -o output
Собрал lua, свой клиент для ii вместе с libcurl (писал об этом в ii.14), ещё один небольшой скрипт под 200 строк.
Захотелось большего =) Решил сделать apk, чтобы можно было просто тыкнуть пальцем и запускать свои скрипты, а не заходить в терминал.
Тут и начались проблемы. Документация у Гугла по NDK слишком скудная, поэтому разбираться пришлось самому.
GUI андроид-приложения называется Activity. В каталоге samples из NDK нашёл пример native-activity.
Компилируется оно уже по всем правилам и стандартам. Сначала надо зайти в сам каталог проекта, а потом уже запустить $NDK_DIR/ndk-build. Данный скрипт сам найдёт нужные параметры и соберёт библиотеку для всех архитектур.
Для того, чтобы упаковать программу в apk-файл, потребуется Android SDK и apache-ant. SDK в распакованном виде тоже весит несколько гигабайт, к сожалению.
Самой упаковкой apk и прочими рутинными вещами занимается ant. Но просто так он работать не может, ему нужен специальный файл под названием build.xml. И чтобы не мучаться с его ручным составлением, можно прибегнуть к небольшой хитрости с использованием SDK.
#!/bin/bash SDK_DIR=/mnt/android-sdk-linux TOOLS=$SDK_DIR/tools $TOOLS/android create project --activity APP_Name --path ./каталог_проекта -n HelloWorld -t android-21 -k vit01.helloworld # в -k должно быть внутреннее системное имя пакета; точка в названии обязательна
Вот мы создали обычный java-проект. Теперь копируем файлы build.xml, local.properties и project.properties в каталог с проектом ndk.
Содержимое build.xml у меня оказалось вот такое:
<?xml version="1.0" encoding="UTF-8"?> <project name="HelloWorld" default="help"> <property file="local.properties" /> <property environment="env" /> <condition property="sdk.dir" value="${env.ANDROID_HOME}"> <isset property="env.ANDROID_HOME" /> </condition> <loadproperties srcFile="project.properties" /> <fail message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable." unless="sdk.dir" /> <import file="custom_rules.xml" optional="true" /> <import file="${sdk.dir}/tools/ant/build.xml" /> </project>
Теперь идём в нужный каталог, набираем ant debug, ждём сборки пакета и находим apk в bin/
Устанавливаем, запускаем, радуемся.
А, стоп, надо посмотреть исходники (в jni/main.c)! Нет, не радуемся. Приложение почти полностью построено на EGL. Значит оно работает с видеокартой на низком уровне, а activity в хэдерах NDK - это единственная небольшая обёртка для него.
Если посмотреть в заголовочные файлы NDK, то видно, что для него есть только та самая обёртка, поддержка сенсоров, карты памяти, TTS, логов и прочее незначительное. A GUI-контролов нет!
Без Java, увы, здесь не обойтись. И это главный минус NDK. Пробуем пойти длинным путём. Идём в каталог java-проекта того самого хеллоуворлда, создаём там каталог jni, внутрь jni суём наш сишных хеллоуворлд и файл Android.mk с вот таким содержанием:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := modulename LOCAL_SRC_FILES := helloworld.c LOCAL_LDLIBS := -llog -landroid LOCAL_STATIC_LIBRARIES := android_native_app_glue include $(BUILD_SHARED_LIBRARY) $(call import-module,android/native_app_glue)
Ещё не помешает Application.mk с подобным:
APP_ABI := all APP_PLATFORM := android-21
И что же нам написать на этот раз, чтобы хоть что-то получилось?
Для связывания Java кода и Си используется вещь под названием Java Native Interface, или JNI. Она позволяет пробрасывать сишные функции, чтобы их можно было вызывать из java-кода. И наоборот, но так гораздо сложнее. Проще говоря, это такой костыль.
Для демонстрации его работы напишем в java файл (а находится он в каталоге src/vit01/helloworld/APP_Name.java для моего проекта) вот такой код:
package vit01.helloworld; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class APP_Name extends Activity { native public String return_helloworld(); // объявляем функцию, находящуюся в сишной библиотеке; ключевое слово native обязательно String mytext; static { System.loadLibrary("modulename"); // загружаем наше скомпилированное } TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); textView = new TextView(this); mytext=return_helloworld(); // вызываем сишную функцию textView.setText(mytext); setContentView(textView); } }
В jni/helloworld.c пишем
#include <jni.h> JNIEXPORT jstring JNICALL Java_vit01_helloworld_APP_1Name_return_1helloworld(JNIEnv * env, jobject jObj) { // много букв, да? И не говорите. Название для этой функции jni ищет по особым правилам, и если в джаве она называется return_helloworld(), то здесь вот такой ужас // приходится нагромождать в сишном коде кучу костылей и обёрток char str[]="Hello World 123456!"; return (*env)->NewStringUTF(env, str); }
Потом в каталоге проекта запускаем $NDK_DIR/ndk-build и ant debug, на выходе получаем готовый apk в bin/, который при запуске выдаст наш хеллоуворлд. Кстати, material design из коробки я в нём так и не увидел. Видимо, надо ещё стилевые библиотеки подключать.
Вывод: программирование на Андроид без IDE и джавы довольно сложно, много минусов у NDK, но так или иначе приноровиться можно. Через любимую консоль будет довольно тяжело писать под эту платформу.