13 Март 2009

JNI для андроида


Необходимые материалы

Для лучшего понимания рекомендую прочитать мой предыдущий пост про Hello World на Си для анроода.

Если вы используете не линукс, то вам нужна папка include из линуксового JDK, поскольку из-за незначительных различий виндовая не подходит(

Описание нативного метода в java-классе и получение хидера для Си

Нативный код в Java описывается с помощью ключевого слова native. В своем коде я заведу такую функцию:

public static native int hello(int val1, String val2);

Теперь необходимо собрать проект(я не нашел в эклипсе кнопки “Build”, поэтому я просто запускал проект), а затем перейти в папку /bin и выполнить там комманду:

javah %ClassName%

где %ClassName% - полное название класса, в котором мы написали этот метод. В моем случае - это hotheart.JNITest.JNITestActivity в результате мы получили заголовочный файл

 

К сожалению, мы не сможем воспользоваться вторым параметром и в нативном коде мы не сможем ничего о нем узнать. Поскольку в заголовочных файлах не описаны методы для работы с объектами. В частности String перейдет в jstring, который на самом деле jobject, который является просто пустой структурой. Но, возможно, это дело может исправить использование заголовочных файлов из самого андроида.

Изучение заголовочного файла и реализация метода

Теперь взглянем на сгенерированный заголовочный файл. Вот что получилось в моем случае:
(файл myjni.h)

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class hotheart_JNITest_JNITestActivity */

#ifndef _Included_hotheart_JNITest_JNITestActivity
#define _Included_hotheart_JNITest_JNITestActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     hotheart_JNITest_JNITestActivity
 * Method:    hello
 * Signature: (ILjava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_hotheart_JNITest_JNITestActivity_hello
 (JNIEnv *, jclass, jint, jstring);

#ifdef __cplusplus
}
#endif
#endif

Рассмотрим комментарий перед описанием метода. Мы должны запомнить что здесь написано - мы будем это использовать в последствии.
В поле Class мы получили название метода, в Metod - название метода, Signature - Описание параметров и выходных значений метода.

Теперь реализуем этот метод. Я в своей реализации буду просто возвращать первый аргумент:
(файл myjni.c)

#include
#include "myjni.h"

#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_hotheart_JNITest_JNITestActivity_hello
 (JNIEnv *a, jclass b, jint c, jstring d)
{
 return c;
}

#ifdef __cplusplus
}
#endif

Но! этого не достаточно для функционирования JNI. Необходимо при инициализации JNI регистрировать методы в джава-машине.

Делается это так:

static JNINativeMethod sMethods[] = {
 /* name, signature, funcPtr */

 {"hello", "(ILjava/lang/String;)I", (void*)Java_hotheart_JNITest_JNITestActivity_hello},
};

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
 JNIEnv* env = NULL;
 jint result = -1;

 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
 return result;
 }

 jniRegisterNativeMethods(env, "hotheart/JNITest/JNITestActivity", sMethods, 1);
 return JNI_VERSION_1_4;
}

Этот код приписываем в конец файла myjni.c
Массив sMethods представляет из себя список всех регистрируемых функций, а в методе JNI_OnLoad мы регистрируем сами функции.

 

Компиляция и загрузка библиотеки

Что бы теперь скомпилировать наш проект нужно немного видоизменить код для компиляции в случае с HelloWorld’ом - при компиляции нужно добавить параметры -i с путями к заголовкам (не забываем про пробелы), которые вы скачали, когда начали читать статью, а при линковке добавить ключ -shared.

В результате в моем случае получаем:

arm-none-linux-gnueabi-gcc -ID:\Android\AndroidJNI\jdk_include\ -ID:\Android\AndroidJNI\jdk_include\linux -fpic -c myjni.c -o D:\CompileTemp\myjni.o
arm-none-linux-gnueabi-ld -TD:\GCC\arm-none-linux-gnueabi\lib\ldscripts\armelf_linux_eabi.xsc -shared -o D:\CompileTemp\myjni.so D:\CompileTemp\myjni.o

Теперь с нативным кодом покончено и мы можем загрузить библиотеку в андроид.
Я положил в папку /data/data/hotheart.JNITest/, поскольку в эту папку мое приложение имеет гарантированный полный доступ. Теперь остался последний штрих - добавление кода для загрузки библиотеки в память:

System.load("/data/data/hotheart.JNITest/myjni.so");

В итоге мой java-код стал таким:

package hotheart.JNITest;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class JNITestActivity extends Activity {
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 TextView tv =  (TextView)findViewById(R.layout.hello);
 System.load("/data/data/hotheart.JNITest/myjni.so");
 tv.setText("Result =" + hello(10, "test"));
 }

 public static native int hello(int val1, String val2);
}

Все! теперь мы можем запустить наше приложение:

Hello World на Си для андроида

Что требуется для компиляции

  • Компилятор GCC для ARM. Далее буду описывать работу под виндами
  • Модифицированный скрипт, необходимый для адаптации компилятора для компилирования под андроид - armelf_linux_eabi.xsc

Отмазка

Я плохо разбираюсь в GCC и многого не понимаю еще и этот мануал - выдержка из слежующих блогов:

Компиляция HelloWorld’а со статичной линковкой

После установки компилятора мы можем сразу скомпилировать свой HelloWorld с таким кодом файла main.c:
#include <stdio.h>;
int main()
{
printf("Hello Kitty!");
    return 0;
}
Выполнив такой код в папке с этим файлом:
arm-none-linux-gnueabi-gcc -static main.c -o main
В результате мы получили бинарник теперь его необходимо загрузить на G1 или эмулятор, прописать права на выполнение и запустить:
adb push  main /data/myjni
adb shell
#cd /data
#chmod 777 myjni
#./myjni
В результате получим надпись “Hello Kitty!” на экране) Если не верите, то вот пруф - скриншот с экрана G1, но у меня myjni немного не тот бинарник. Результат подобных действий у меня в native:
Но есть огромный минус - статическая линковка. и таким образом мы подходим к следующему пункту.

Динамическая компиляция

В андроиде таблица импортов/экспортов немного не такая как сделана в этом компиляторе и поэтому необходимо вмешиваться в работу компилятора.
Первое, что нам нужно сделать - это скачать файл armelf_linux_eabi.xsc и заменить им такой же файл в дистрибе компилятора - %CompilerPath%\arm-none-linux-gnueabi\lib\ldscripts\
Затем нам нужно сделать так, что бы в пути к компилятору не было пробелов. Это необходимо, поскольку как-то некорректно работает этот GCC с парамертрами, которые содержат пробелы(
Я решил дело просто - сделал символьную ссылку на папку компилятора и назвал её D:\GCC
Теперь непосредственно компилируем и линкуем:
arm-none-linux-gnueabi-gcc -fpic -c myjni.c -o D:\CompileTemp\myjni.o
arm-none-linux-gnueabi-ld -TD:\GCC\arm-none-linux-gnueabi\lib\ldscripts\armelf_linux_eabi.xsc -shared -o D:\CompileTemp\myjni.so D:\CompileTemp\myjni.o 
Тут еще проблема была - выходной файл из компилятора без указания абсолютного пути писался непонятно куда… пришлось вручную писать во временную папку(
Теперь все с динамическим кодом и в результате мы получили полноценный HelloWorld

8 Март 2009

Ура! Удалось загрузить изображение карты!

Сегодня я смог добится загрузки карты старкрафта! Это еще один шажок на пути к реализации идеи)

Все идет пока что даже слишком легко… надеюсь у меня не будет проблем с AI. Насколько мне известно там простые скрипты, которые описывают каждое действие и не будет проблем и с этим)

разработка идет полным ходом, надеюсь, что в скором времени у меня уже будет пре-пре-пре-альфа версия и доведу я её до адекватного состояния и затем выложу на гуглокод.

На всякий случай вот пруфлинк(в мой блог не захотело аплоадится):

6 Март 2009

Эмулятор DroidGear теперь выложен на Code.Google

Ну вот теперь я решил выложить свои нынешние наработки на Code.Google) Начну с DroidGear!

Страница приложения на Google Code.

Небольшое описание.

DroidGear - это порт единственного(насколько мне известно) открытого Java-эмулятора Sega - JavaGear.

Основные возсожности/особенности и тпх:

  • Тормозной( дело в том, что либо я что-то не понимаю в андроиде, либо андроид еще слишком сырой и следует немного подождать до его оптимизаций. Но насколько мне извесно Java-машина Андроида вообще не предназначалась к таким ресурсоемким приложениям. Выход тут есть - написать нативный код на C++, чем я и планирую заняться через годик(а может и раньше) после окончания разработки Старкрафта
  • Поддержка Sega Master System, Sega Game Gear
  • Загрузка ROM’ов с карты памяти
  • Управление с клавиатуры… не представляю как без мультитача играть получится в приципе(

Несколько скриншотов:

3 Февраль 2009

Начата разработка игры StarCraft для Андроида

Сегодня с утра я проснулся от того, что мне позвонил друг… позвонил рано) даже очень) Но день, тем не менее не испортился. Поскольку у меня было много времени (появилось часа три-четыре) я вспомнил про мою любимую игру детства - StarCraft. Ну и соответственно, вспомнив как там все было сделано, я пришел к выводу, что это все вполне реализуемо.

И так. сейчас опишу то, чего я добился в эти три-четыре часа.

  • Загрузка одного юнита, в принципе можно и других, но пока нужно формализовать движок
  • Некоторая анимация - выравнивание изображения юнита по направлению движения
  • Найдены соответствующие утилиты для распаковки архивов и т.д.

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

28 Январь 2009

Парсинг XML в Google Android

Введение

Более подробно можете почитать о Document Object Model(DOM) в википедии. Лучше в английской.

В крадце опишу.

Есть некий документ, который представим в виде дерева. Пусть будет HTML-код:

<paragraph align="left">
The <it>Italicized</it> portion.
</paragraph>

В результате он будет преобразован к вот такому дереву:

Как видно(это верно и для Android Java) текст внутри тега помещается в отдельный узел(лист) дерева.
Это нужно не забывать, когда будем работать с этим деревом!

» Читать полностью …

Android и NetBeans

1) Скачайте и установите NetBeans

Тут ведется на основе NetBeans 6.5 All (система Windows 7 7000 build)
NetBeans

2) Скачайте и установите Android SDK

Тут ведется на основе Android SDK 1.0 R2
Android SDK

3) Установка плагина

а) Идем в Tools->Plugins


» Читать полностью …