android编程进阶 - freshui

Download Report

Transcript android编程进阶 - freshui

Android应用程序开发进阶
Android 的Native开发
陈水德
•
•
•
•
•
NDK简介
NDK开发环境搭建
在应用程序中使用JNI
Native与Java之间的沟通
MIPS的NDK及开发环境
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 开发环境的搭建
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
– JNI 是java native interface的简称
– JNI是定义java和java虚拟机之外的native代码的交流标准
– JNI可以让java直接访问系统,java通过解释器之后访问,非
常慢!
– JNI中,native代码的声明在java程序中,实现是放在C中。
native实现的也是java类的方法
• 提供用另外一种语言实现java语言方法(method)的功能。
在应用程序中使用JNI
使用了JNI的java程序
普通Java程序
Java Apps
Java Apps
JNI
Java VM
System Resources
(CPU, Memory, Storage...)
Java VM
System Resources
(CPU, Memory, Storage...)
在应用程序中使用JNI
Class Loader
Class Files
RUNTIME DATA AREA
Method Area
HEAP
Execute Engine
Java Stack
Native Method
Stack
Program Counter Register
Native
Interface
Native
Libraies
Native线程
在应用程序中使用JNI
• 在应用程序中使用JNI
–
–
–
–
使用eclipse创建android应用程序
构建应用程序类,申明native方法
创建jni目录
编写native代码
在应用程序中使用JNI
• JNIEnv JNI运行时环境
• JavaVM Java VM的handle
在应用程序中使用JNI
• 创建JNI的android app
–
–
–
–
–
–
–
1. 实现正常的android工程
2. 在java中声明native方法
3. 在C/c++中编写native方法的实现代码
4. 编译native代码,生成so库
5. java代码中加载 so库
6. 在java代码中调用native方法
7. 运行、调试
在应用程序中使用JNI
• 编写java代码,声明native方法
public class HelloJni extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
setContentView(tv);
}
public native 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++类型
Java类
型
JNI类型
– Char为2字节
boolean jboolean
– Long为8字节
byte
jbyte
– 其他非内置数据类型的,都是
short
jshort
object类型
– Java都是大端表示
char
jchar
位宽
(Byte)
字母标识
1
Z
1
B
2
S
2
C
int
jint
4
I
long
jlong
8
J
float
jfloat
4
F
double
jdouble
8
D
object
jobject
ref
L+object
void
void
0
V
[array]
[array]
-
[
在应用程序中使用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方法
public class HelloJni extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
}
public native 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之间的对应关系不作判断
Native与java之间的沟通(Invocation API)
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);
Create Exception
(*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");
}
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及开发环境
• 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
• Thanks!