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);
}
Все! теперь мы можем запустить наше приложение:

