# Первая проба Android NDK или лень против костылей :)
vit01(mira, 1) — All
2015-10-19 04:05:58


Решил не так давно написать что-то родное для своего андроида. Но проблема в том, что на 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, но так или иначе приноровиться можно. Через любимую консоль будет довольно тяжело писать под эту платформу.

# Re: Первая проба Android NDK или лень против костылей :)
Difrex(mira, 14) — vit01
2015-10-19 12:54:30


А IDE не пробовал?
Может, в нем проще будет NDK пользовать

# Re: Первая проба Android NDK или лень против костылей :)
vit01(mira, 1) — Difrex
2015-10-19 13:38:33


> А IDE не пробовал?
Не, ни одну не пробовал. Они все такие жирные :[
Предпочитаю писать код в vim.

> Может, в нем проще будет NDK пользовать
NDK и там, и там одинаково используется, я же писал, в чём главные минусы этой штуки =)

Неудобно сборку пакета воедино делать, отлаживать и на джаве писать.

Думаю, что IDE всё-таки придётся ставить.

# Re: Первая проба Android NDK или лень против костылей :)
vit01(mira, 1) — vit01
2015-10-20 04:01:49


>Думаю, что IDE всё-таки придётся ставить.
Попробовал android studio, так она виснет и вылетает через 5 минут работы. Ещё и терминал, в котором запущена, в сегфолт загоняет.

# Re: Первая проба Android NDK или лень против костылей :)
Andrew Lobanov(station13, 1) — vit01
2015-10-20 05:50:09


>>Думаю, что IDE всё-таки придётся ставить.
>Попробовал android studio, так она виснет и вылетает через 5 минут работы. Ещё и терминал, в котором запущена, в сегфолт загоняет.

У нас таки будет андроид-клиент?

Оффтопик: у меня, скорее всего, остались последние дни сессии. В iing чего договорились то? Оставляем 20 знаков в msgid и обязательную точку в имене эхи, но без обязательного числа в конце (ii.talk)?

# Re: Первая проба Android NDK или лень против костылей :)
vit01(mira, 1) — Andrew Lobanov
2015-10-20 11:16:46


> У нас таки будет андроид-клиент?
А разве его до сих пор нет? =)
Java-клиент от ntkrnlmp.exe и так вполне работает. Сишный клиент тоже работает (с доустановкой терминала и рута).

Пока "андроид-клиента от vit01" в планах не намечалось, но в перспективе хотел бы и на андроид свои грязные^W руки просунуть ;)

Пробовал ещё на Qt что-то сделать опять же на сабж, но ничего не вышло. И снова из-за ужасной IDE =(

# Re: Первая проба Android NDK или лень против костылей :)
vit01(mira, 1) — Andrew Lobanov
2015-10-20 11:16:46


> В iing чего договорились то? Оставляем 20 знаков в msgid и обязательную точку в имене эхи, но без обязательного числа в конце (ii.talk)?
Пока ещё не договорились, но готов остановиться на этом варианте. msgid пока оставляем.

# Re: Первая проба Android NDK или лень против костылей :)
Difrex(mira, 14) — vit01
2015-10-20 11:54:06


>Оставляем 20 знаков в msgid и обязательную точку в имене эхи, но без обязательного числа в конце (ii.talk)

Я за такой вариант

# Re: написание софта под Андроид
vit01(mira, 1) — vit01
2015-10-28 11:52:48


IDE мне всё-таки завести удалось. Но жрать гигабайт с чем-то ОЗУ, извините, просто нагло с её стороны.
Здесь завести NDK пришлось немного по-другому: тут используется система сборки gradle вместо чего-то более простого. Для начала в файле gradle.properties надо прописать: android.useDeprecatedNdk=true
Затем в local.properties указываем: ndk.dir=/путь/к/ndk
Потом либо создаём в проекте каталог jni и туда сишные исходники, либо делаем симлинк для libs/, сгенерированного ndk-build на jniLibs/ и собираем библиотеки вручную через консоль, чтобы IDE их подхватывала.

А так скажу, что со средой разработки гораздо проще, чем без неё. Настолько просто, что даже интерес особый пропадает в ней писать.