430 likes | 689 Views
Android 应用程序开发进阶. Android 的 Native 开发 陈水德. NDK 简介 NDK 开发环境搭建 在应用程序中使用 JNI Native 与 Java 之间的沟通 MIPS 的 NDK 及开发环境. NDK 简介. NDK 简介. 关于 NDK Android 提供的工具,方便应用程序嵌入 native 代码 . 目前基本上支持 c, c++ 和 assembly 的 native 开发 目标以动态链接库 so 的形式提供给应用程序 要求系统有 SDK , NDK 不能开发应用程序,附着于 SDK
E N D
Android应用程序开发进阶 Android 的Native开发 陈水德
NDK简介 • NDK开发环境搭建 • 在应用程序中使用JNI • Native与Java之间的沟通 • MIPS的NDK及开发环境
NDK简介 • 关于NDK • Android提供的工具,方便应用程序嵌入native代码. • 目前基本上支持c, c++和assembly的native开发 • 目标以动态链接库so的形式提供给应用程序 • 要求系统有SDK,NDK不能开发应用程序,附着于SDK • 包括交叉编译环境、文档和sample • NDK可简单看作Android源码编译环境的精简版 • 官方仅支持ARM指令系统 • ARMv5TE (including Thumb-1 instructions) • ARMv7-A (including Thumb-2 and VFPv3-D16 instructions, with optional support for NEON/VFPv3-D32 instructions)
NDK简介 • 关于Android应用的Native开发 • 不管有没有NDK,由于dalvik支持jni,native开发一直被支持。 • jni不可开发Android可执行程序 • 不能扩展Android服务/功能等 • 不能扩展编/解码功能 • 不能扩展驱动 • 只是是应用程序的补充(运算方面及系统资源访问) • 对界面控制能力相当弱(弱到忽略不计) • 没有glibc,posix支持不完善,移植要注意. No SysV IPC
NDK简介 • NDK提供的接口 • Bionic C库, openGL库和linux头文件 • Android-8(froyo)之前 • 仅能使用android的log (liblog) • Android-8 (froyo) • 增加了bitmap接口,jni可控制java bitmap类,在native端render界面。 • Android-9 增加了native_window • 增加了Native window接口 • 除NDK提供的接口外,不要用到其他任何android源码提供的系统接口 • Native的系统接口不保证版本一致性 • Native的系统接口没有强制要求厂商保持一致
NDK开发环境搭建 • Software Requirement • 完全安装的SDK开发环境 • 至少要 android SDK 1.5版本 • Make 3.81版本之后
NDK开发环境搭建 • Android NDK开发环境 • Windows • Windows XP (32-bit) or Vista (32- or 64-bit) • Cygwin 1.7(一定要安装devel包), 1.5版本不工作 • NDK 开发包(绿色,解压即可) • Linux • Linux (32- or 64-bit, tested on Linux Ubuntu Dapper Drake) • NDK开发包 • Mac OS • Mac OS X 10.4.8 or later (x86 only)
什么是JNI • JNI 是java native interface的简称 • JNI是定义java和java虚拟机之外的native代码的交流标准 • JNI可以让java直接访问系统,java通过解释器之后访问,非常慢! • JNI中,native代码的声明在java程序中,实现是放在C中。native实现的也是java类的方法 • 提供用另外一种语言实现java语言方法(method)的功能。
在应用程序中使用JNI Native线程
在应用程序中使用JNI • 在应用程序中使用JNI • 使用eclipse创建android应用程序 • 构建应用程序类,申明native方法 • 创建jni目录 • 编写native代码
在应用程序中使用JNI • JNIEnv JNI运行时环境 • JavaVM Java VM的handle
在应用程序中使用JNI • 创建JNI的androidapp • 1. 实现正常的android工程 • 2. 在java中声明native方法 • 3. 在C/c++中编写native方法的实现代码 • 4. 编译native代码,生成so库 • 5. java代码中加载 so库 • 6. 在java代码中调用native方法 • 7. 运行、调试
在应用程序中使用JNI • 编写java代码,声明native方法 publicclass HelloJni extends Activity { @Override publicvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); setContentView(tv); } publicnative String stringFromJNI(); }
在应用程序中使用JNI • Java方法和Native函数之间的对应关系 // Java code JniDemo.java package com.example.jnidemo; …… public native String stringFromJNI() /* native code */ Java_com_example_jnidemo_JniDemo_stringFromJNI( JNIEnv* env, jobject thiz ) { …… }
在应用程序中使用JNI • 编写native代码 • C的方式: #include <string.h> #include <jni.h> jstring Java_com_example_jnidemo_JniDemo_stringFromJNI( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from JNI !"); }
在应用程序中使用JNI • 编写native代码 • C++的方式: #include <string.h> #include <jni.h> extern "C" jstring Java_com_example_jnidemo_JniDemo_stringFromJNI( JNIEnv* env, jobject thiz ) { return env->NewStringUTF("Hello from JNI !"); }
在应用程序中使用JNI • 编写native代码 • Jni Onload方式 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { goto bail; } if (registerMethods(env) != 0) { goto bail; } result = JNI_VERSION_1_4; bail: return result; }
在应用程序中使用JNI • Java类型/C++类型 • Char为2字节 • Long为8字节 • 其他非内置数据类型的,都是object类型 • Java都是大端表示
在应用程序中使用JNI • Java 方法的符号标识 native String funcA(int i, double j, String s) 标识(signature):(IDLjava/lang/String;)Ljava/lang/String; native void funcB(int[] k, String[] s) 标识(signature):([I[Ljava/lang/String;)V native void funcC() 标识(signature):()V
在应用程序中使用JNI • 编译 • Application.mk • make APP=<project directory name> • 生成的库放在 <project dir>/libs/<abi>/<lib>.so • Java加载动态库 static { System.loadLibrary("jni-demo"); } static { System.load(“<path>/libjni-demo.so"); }
在应用程序中使用JNI • 加载so,调用native方法 publicclass HelloJni extends Activity { @Override publicvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.setText( stringFromJNI() ); setContentView(tv); } publicnative String stringFromJNI(); static { System.loadLibrary("jni-demo"); } }
在应用程序中使用JNI • 使用Native的注意事项 • 没有glibc • 没有native的window system(如GTK) • 不包括全部的linux utilities • Bionic • 小而精炼(换句话说:健壮性不强) • 快速pthread实现(健壮性不强) • 内置了android的服务系统(如log,property) • 只支持posix的部分功能 • so在应用程序中扮演的是资源角色 • 应用程序不检验合法性 • Native和java之间的对应关系不作判断
Invocation API • Native端需要获取的信息 • Class (jclass) • 类型的定义。通常也就是class文件描述的信息。 • Object (jobject) • Class的实例,通常都是分配在堆上的。 • Filed (jfieldID) • Class的数据成员 • Method (jmethodID) • Class的方法(成员函数) • JNIEnv JNI运行时环境,提供invocation API
Invocation API • 访问java String /* DO NOT USE jstring THIS WAY !!! */ string Java_Test_getLine(JNIEnv *env, jobject obj, jstring jstr) { printf("%s", jstr); } jstring Java_Test_getLine(JNIEnv *env, jobject obj, jstring jstr) { char buf[128]; const char *str = (*env)->GetStringUTFChars(env, jstr, 0); printf("%s", str); (*env)->ReleaseStringUTFChars(env, prompt, str); scanf("%s", buf); return (*env)->NewStringUTF(env, buf); }
Invocation API • 访问java Array /* This program is illegal! */ jint Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) { int i, sum = 0; for (i=0; i<10; i++) sum += arr[i]; } jint Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) { int i, sum = 0; jsize len = (*env)->GetArrayLength(env, arr); jint *body = (*env)->GetIntArrayElements(env, arr, 0); for (i=0; i<len; i++) sum += body[i]; (*env)->ReleaseIntArrayElements(env, arr, body, 0); return sum; }
Invocation API • JNI调用java方法 public class CallBacks { ……… public void callback() {……} } JNIEXPORT void JNICALL Java_Callbacks_nativeMethod(JNIEnv *env,jobject obj,jint depth) { jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); if (mid == 0) return; printf("In C, depth = %d, about to enter Java\n", depth); (*env)->CallVoidMethod(env, obj, mid, depth); printf("In C, depth = %d, back from Java\n", depth); }
Invocation API • JNI访问java数据成员(field) // java code ……… int si = …… String s = “………”; ……… /* native code */ ……… jint si; jstring jstr; fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;"); si = (*env)->GetStaticIntField(env, cls, fid); jstr = (*env)->GetObjectField(env, obj, fid); ………
JNI的异常处理 ……… jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V"); jthrowable exc; if (mid == 0) return; (*env)->CallVoidMethod(env, obj, mid); exc = (*env)->ExceptionOccurred(env); if (exc) { jclass newExcCls; (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); if (newExcCls == 0) /* Unable to find the new exception class, give up. */ return; (*env)->ThrowNew(env, newExcCls, "thrown from C code"); } 捕获异常 处理异常 Create Exception Throw new exception
Invocation API • 局部和全局引用 /* This code is illegal */ static jclass cls = 0; static jfieldID fld; JNIEXPORT void JNICALL Java_FieldAccess_accessFields(JNIEnv *env, jobject obj) { ... if (cls == 0) { cls = (*env)->GetObjectClass(env, obj); if (cls == 0) ... /* error */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); } ... /* access the field using cls and fid */ }
Invocation API • 局部和全局引用 /* This code is OK */ static jclass cls = 0; static jfieldID fld; JNIEXPORT void JNICALL Java_FieldAccess_accessFields(JNIEnv *env, jobject obj) { ... if (cls == 0) { jclass cls1 = (*env)->GetObjectClass(env, obj); if (cls1 == 0) ... /* error */ cls = (*env)->NewGlobalRef(env, cls1); if (cls == 0) ... /* error */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); } ... /* access the field using cls and fid */ }
Invocation API • 线程同步 • sychronize(obj) { // critical section } • (*env)->MonitorEnter(env, obj); // critical section (*env)->MonitorExit(env, obj);
Mips NDK 下载 • http://mipsandroid.org/projects/show/mips-android-public
MIPS NDK与Android NDK的差异 • 1. mips写了一个java gui界面,配置运行更简单 • 2. 使用mips的交叉编译环境
1. 安装Mips NDK • 和android NDK一样 • 需要安装JDK
源码开发环境 • 下载源码 • 编译源码 • 添加模块 • 启动模拟器
Mips Android 环境编译 export TARGET_ARCH=mips source build/envsetup.sh export TARGET_ARCH_VERSION=mips32r2 export TARGET_CPU_ENDIAN=EL