附:相关代码路径

/frameworks/base/media/java/android/media/MediaScanner.java

/frameworks/base/media/jni/android_media_MediaScanner.cpp

/frameworks/base/media/jni/android_media_MediaPlayer.cpp

/franmeworks/base/core/jni/AndroidRunTime.cpp

/dalvik/libnativehelper/JNIHelp.cpp

一、什么是JNI

JavaNativeInterface(JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI是本地编程接口,它使得在Java虚拟机(VM)内部运行的Java代码能够与用其它编程语言(CC++和汇编语言)编写的应用程序和库进行交互操作。

二、为什么推出JNI

1)Java世界虚拟机使用Native语言编写的,虚拟机运行在具体的平台上,虚拟机本身无法做到与平台无关,jni可以对java层屏蔽不同平台操作的差异,这样就能实现java本身平台无关特性

2)、适应已经用Native语言实现的技术。

3)、一些效率的问题需要Native语言实现。

AndroidJNI就是一座将Native层和java层联系在一起的桥梁。(本文将参考《深入理解Android卷一》以MediaScanner为例)

三、什么时候使用JNI

1)、JavaAPI可能不支某些平台相关的功能。比如,应用程序执行中要使用JavaAPI

支持的文件类型,而如果使用跨进程操作方式,即繁琐又低效

2)、避免进程间低效的数据拷贝操作

3)、多进程的派生:耗时、耗资源(内存)

4)、用本地代码或汇编代码重写Java中低效方法

注意:

Java程序集成了本地代码,它将丢掉Java的一些好处。

首先,脱离Java,可移植性问题你要自己解决,且需重新在其他平台编译链接本地库。

第二,要小心处理JNI编程中各方面问题和来自C/C++语言本身的细节性问题,处理不当,应用将崩溃。

一般性原则:做好应用程序架构,使nativemethods定义在尽可能少的几个类里。

四、AndroidJNI使用流程解析

MediaScanner为例

1)、载入*.so库文件

VM去载入Android/system/lib/libmedia_jni.so档案。载入*.so之后,Java类与*.so档案就汇合起来,一起执行了。

-->MediaScanner.java

......

static{

System.loadLibrary("media_jni");

native_init();

}

......

privatestaticnativefinalvoidnative_init();

......

<--

2)、如何撰写*.so的入口函数

JNI_OnLoad()JNI_OnUnload()函数的用途,当AndroidVM(VirtualMachine)执行到System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。它的用途有二:

(1)告诉VMC组件使用那一个JNI版本。如果你的*.so档没有提供JNI_OnLoad()函数,VM会默认该*.so档是使用最老的JNI1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI1.4java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM

(2)由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以

C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization)

例如,Android/system/lib/libmedia_jni.so档案里,就提供了JNI_OnLoad()函数。

由于VM通常是多执行绪(Multi-threading)的执行环境。每一个执行绪在呼叫JNI_OnLoad(),所传递进来的JNIEnv指标值都是不同的。为了配合这种多执行绪的环境,C组件开发者在撰写本地函数时,可藉由JNIEnv指标值之不同而避免执行绪的资料冲突问题,才能确保所写的本地函数能安全地在Android的多执行绪VM里安全地执行。基于这个理由,当在呼叫C组件的函数时,都会将JNIEnv指标值传递给它。

-->android_media_MediaPlayer.cpp

......

externintregister_android_media_MediaScanner(JNIEnv*env);

......

jintJNI_OnLoad(JavaVM*vm,void*reserved)

{

JNIEnv*env=NULL;

jintresult=-1;

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

ALOGE("ERROR:GetEnvfailed\n");

gotobail;

}

assert(env!=NULL);

......

if(register_android_media_MediaScanner(env)<0){

ALOGE("ERROR:MediaScannernativeregistrationfailed\n");

gotobail;

}

......

<--

3)JNI注册,使javanative函数和JNI函数一一对应(动态注册)

JNI中记录javanative函数和JNI函数一一对应的结构

typedefstruct{

//javanative函数的名字如native_init,不带函数路径

constchar*name;

//java函数的签名信息,含参数和返回值

constchar*signature;

//JNI层对应函数的指针,void类型

void*fnPtr;

}JNINativeMethod

如(2)中所述,在调用*.so入口函数JNI_OnLoad时有调用register_android_media_MediaScanner(env)等方法。

-->andorid_media_MediaScanner.cpp

......

staticJNINativeMethodgMethods[]={

......

{

"native_init",

"()V",

(void*)android_media_MediaScanner_native_init

},

......

}

......

intregister_android_media_MediaScanner(JNIEnv*env)

{

returnAndroidRuntime::registerNativeMethods(env,

kClassMediaScanner,gMethods,NELEM(gMethods));

}

......

<--

-->AndroidRunTime.cpp

......

/*

*RegisternativemethodsusingJNI.

*/

/*static*/intAndroidRuntime::registerNativeMethods(JNIEnv*env,

constchar*className,constJNINativeMethod*gMethods,intnumMethods)

{

returnjniRegisterNativeMethods(env,className,gMethods,numMethods);

}

......

<--

VM(AndroidRuntime)登记gMethods[]表格所含的本地函数了。简而言之,registerNativeMethods()函数的用途有二:(1)更有效率去找到函数。(2)可在执行期间进行抽换。由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,可多次呼叫registerNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。

--->JNIHelp.cpp

......

staticjclassfindClass(C_JNIEnv*env,constchar*className){

JNIEnv*e=reinterpret_cast<JNIEnv*>(env);

return(*env)->FindClass(e,className);

}

extern"C"intjniRegisterNativeMethods(C_JNIEnv*env,constchar*className,

constJNINativeMethod*gMethods,intnumMethods)

{

JNIEnv*e=reinterpret_cast<JNIEnv*>(env);

LOGV("Registering%snatives",className);

scoped_local_ref<jclass>c(env,findClass(env,className));//没有看明白

if(c.get()==NULL){

LOGE("Nativeregistrationunabletofindclass'%s',aborting",className);

abort();

}

if((*env)->RegisterNatives(e,c.get(),gMethods,numMethods)<0){

LOGE("RegisterNativesfailedfor'%s',aborting",className);

abort();

}

return0;

}

......

<---

看似很繁琐,其实动态注册只用了两个函数完成。

(*env)->FindClass(e,className);

(*env)->RegisterNatives(e,c.get(),gMethods,numMethods)

如果想要完成动态注册,就必须实现JNI_OnLoad函数

JNI_OnLoad()呼叫register_android_media_MeidaScnaner(env)函数时,就将env指标值传递过去。如此,register_android_media_MeidaScnaner()函数就能藉由该指标值而区

别不同的执行绪,,以便化解资料冲突的问题。

例如,register_android_media_MeidaScnaner()函数里,可撰写下述指令:

if((*env)->MonitorEnter(env,obj)!=JNI_OK){

}

查看是否已经有其他执行绪进入此物件,如果没有,此执行绪就进入该物件里执行了。

还有,也可撰写下述指令:

if((*env)->MonitorExit(env,obj)!=JNI_OK){

}

查看是否此执行绪正在此物件内执行,如果是,此执行绪就会立即离开。

五、JNI内容简单介绍

1)JNIEnv是一个与线程相关的变量

2)native函数转换成JNI函数时,虚拟机会传入JNIEnv变量,如果后台线程主动回调java层函数是在JNI_OnLoad函数中JavaVM会产生JNIEnv变量,在线程退出时,会释放对应的资源

插入内容:JNI签名

Java中有函数重载,区别在于函数的参数,JNI则是通过函数返回值和函数参数合成一个签名信息与java中的函数对应。

如下:签名标示表

类型标识

Java类型

类型标识

Java类型

Z

boolean

F

float

B

byte

D

double

C

char

L/java/lang/String;

String

S

short

[I

Int[]

I

int

[L/java/lang/Object;

Object[]

J

long

注意:Java类型是一个数组,则标识中会有一个[,引用类型最后都有一个;

例如:

函数签名

Java函数

()Ljava/lang/String;

Stringf()

(ILjava/lang/Class)J

longf(inti,Classclass)

([B)V

voidf(byte[]byte)

附:用javap-s-pxxx(编译生成的.class文件)可以查看xxx中函数对应的签名。

1)jfieldIDjmethodID介绍和使用

我们知道,成员变量和成员函数都是由类定义的,他们都是类的属性,所以在JNI规则中用jfieldIDjmethodID来标示java类成员和函数。JNI中可以通过如下方式操作。

jfieldIDfid=(*env)->GetFieldID(env,cls,"s","Ljava/lang/String;");

cls:代表java

"s":成员变量的名字

"Ljava/lang/String;":变量的签名信息

这时,通过在对象上调用下述方法获得成员的值:

jstr=(*env)->GetObjectField(env,obj,fid);

通过在对象上调用下述方法改变成员的值:

(*env)->SetObjectField(env,obj,fid,field);

此外JNI还提供Get/SetIntField,Get/SetFloatField等访问不同类型成员。

同样,也提供了static变量的访问方法,即Get/SetStatic<ReturnValueType>Field

jmethodIDfid=(*env)->GetMethodID(env,cls,"s","Ljava/lang/String;");

Java中有三类方法:实例方法、静态方法和构造方法。示例:

Java代码:

classInstanceMethodCall{

privatenativevoidnativeMethod();

privatevoidcallback(){

System.out.println("InJava");

}

publicstaticvoidmain(Stringargs[]){

InstanceMethodCallc=newInstanceMethodCall();

c.nativeMethod();

}

static{

System.loadLibrary("InstanceMethodCall");

}

}

JNI代码

JNIEXPORTvoidJNICALL

Java_InstanceMethodCall_nativeMethod(JNIEnv*env,jobjectobj)

{

jclasscls=(*env)->GetObjectClass(env,obj);

jmethodIDmid=(*env)->GetMethodID(env,cls,"callback","()V");

if(mid==NULL){

return;/*methodnotfound*/

}

printf("InC\n");

(*env)->CallVoidMethod(env,obj,mid);

}

输出:

InC

InJava

A)、回调Java方法分两步:

•首先通过GetMethodID在给定类中查询方法.查询基于方法名称和签名

•本地方法调用CallVoidMethod,该方法表明被调Java方法的返回值为void

JNI调用实例方法命名规则:Call<ReturnValueType>Method

-->android_media_MediaScanner.cpp::MyMediaScannerClient

......

staticconstchar*constkClassMediaScannerClient=

"android/media/MediaScannerClient";

......

//先找到android/media/MediaScannerClient类在JNI中对应的jclass实例

jclassmediaScannerClientInterface=

env->FindClass(kClassMediaScannerClient);

if(mediaScannerClientInterface==NULL){

ALOGE("Class%snotfound",kClassMediaScannerClient);

}else{

//获得MediaScannerClient类中方法scanFileID

mScanFileMethodID=env->GetMethodID(

mediaScannerClientInterface,

"scanFile",

"(Ljava/lang/String;JJZZ)V");

......

}

......

<--

如上在MyMediaScannerClient会缓存mScanFileMethodID,这是关于程序运行效率的一个问题。(避免每次都去做查询ID的操作)

B)、同实例方法,回调Java静态方法分两步:

•首先通过GetStaticMethodID在给定类中查找方法

•通过CallStatic<ReturnValueType>Method调用

静态方法与实例方法的不同,前者传入参数为jclass,后者为jobject

C)、调用被子类覆盖的父类方法:JNI支持用CallNonvirtual<Type>Method满足这类需求:

GetMethodID获得methodID

•调用CallNonvirtualVoidMethod,CallNonvirtualBooleanMethod

上述,等价于如下Java语言的方式:

super.f();

D)CallNonvirtualVoidMethod可以调用构造函数

六、JNI数据类型转换

1)、java基本类型和JNI基本类型转换

Java

Native类型

符号属性

字长

boolean

jboolean

无符号

8

byte

jbyte

无符号

8

char

jchar

无符号

16

short

jshort

有符号

16

int

jint

有符号

32

long

jlong

有符号

64

float

jfloat

有符号

32

double

jdouble

有符号

64

java基本类型和JNI基本类型转换关系表)

注意:转换成Native类型后对应类型的字长,如jcharNative语言中时16位,占两个字节,和普通的char占一个字节是不一样的。

2)、引用数据类型的转换

Java引用类型

Native类型

Java引用类型

Native类型

Allobjects

jobject

char[]

jcharArray

java.lang.Class实例

jclass

short[]

jshortArray

java.lang.String实例

jstring

int[]

jintArray

Object[]

jobjectArray

long[]

jlongArray

boolean[]

jbooleanArray

float[]

jfloatArray

byte[]

jbyteArray

double[]

jdoubleArray

java.lang.Throwable实例

jthrowable

MediaScanner中的processFile

//javaprocessFile中有3个参数

privatenativevoidprocessFile(Stringpath,StringmimeType,MediaScannerClientclient);

//JNI层对应的函数响应

staticvoidandroid_media_MediaScanner_processFile(JNIEnv*env,jobjectthiz,jstringpath,

jstringmimeType,jobjectclient){

......

}

如果对象都用jobject,就好比Native层的void*类型,对“码农”来讲,他们是透明的,既然是透明的,该如何操作呢?注意上述JNI对应函数中JNIEnv*env,jobjectthiz(调用processFile函数的对象)参数。

3)JNI局部变量,全局变量和垃圾回收

Java中有许多引用的概念,我们只关心GlobalRefLocalRef两种。JNI编程很复杂,

建议不要引入更多复杂的东西,正确、高效的实现功能就可以了。比如对引用来说,最好不要在JNI中考虑:虚引用和影子引用等复杂的东西。

GlobalRef:当你需要在JNI层维护一个Java对象的引用,而避免该对象被垃圾回收时,使用NewGlobalRef告诉VM不要回收此对象,当本地代码最终结束该对象的引用时,

DeleteGlobalRef释放之。

LocalRef:每个被创建的Java对象,首先会被加入一个LocalRefTable,这个Table

小是有限的,当超出限制,VM会报LocalRefOverflowException,然后崩溃.这个问题是JNI编程中经常碰到的问题,请引起高度警惕,JNI中及时通过DeleteLocalRef释放对象的LocalRef.,JNI中提供了一套函数:Push/PopLocalFrame,因为LocalRefTable大小是固定的,这套函数只是执行类似函数调用时,执行的压栈操作,LocalRefTable中预留一部分供当前函数使用,当你在JNI中产生大量对象时,虚拟机仍然会因LocalRefOverflowException崩溃,所以使用该套函数你要对LocalRef使用量有准确估计。

下面来看具体代码

-->android_media_MediaScanner.cpp

......

classMyMediaScannerClient:publicMediaScannerClient

{

public:

MyMediaScannerClient(JNIEnv*env,jobjectclient)

:mEnv(env),

//NewGlobalRef创建一个GlobalRefmClient,这样就避免mClient被回收

mClient(env->NewGlobalRef(client)),

mScanFileMethodID(0),

mHandleStringTagMethodID(0),

mSetMimeTypeMethodID(0)

......

virtual~MyMediaScannerClient()

{

ALOGV("MyMediaScannerClientdestructor");

//析构函数中调用DeleteGlobalRef释放全局变量

mEnv->DeleteGlobalRef(mClient);

}

......

<--

注意:

a)、当写nativemethod的实现时,要认真处理循环中产生的LocalRef.VM规范中规定每个本地方法至少要支持16LocalRef供自由使用并在本地方法返回后回收.本地方法绝对不能滥用GlobalRefWeakGlobalRef,因为此类型引用不会被自动回收。工具函数,LocalRef的使用更要提起警惕,因为该类函数调用上下文不确定,而且会被重复调用,每个代码路径都要保证不存在LocalRef泄露。

b)、Push/PopLocalFrame常被用来管理LocalRef.在进入本地方法时,调用一次PushLocalFrame,并在本地方法结束时调用PopLocalFrame.此对方法执行效率非常高,建议使用这对方法。

你只要对当前上下文内使用的对象数量有准确估计,建议使用这对方法,在这对方法间,不必调用DeleteLocalRef,只要该上下文结尾处调用PopLocalFrame会一次性释放所有LocalRef。一定保证该上下文出口只有一个,或每个return语句都做严格检查是否调用了PopLocalFrame忘记调用PopLocalFrame可能会使VM崩溃

4)JNI对象比较

有两个对象,用如下方法比较相容性:

(*env)->IsSameObject(env,obj1,obj2)

如果相容,返回JNI_TRUE,否则返回JNI_FALSE

NULL的比较,LocalRefGlobalRef语义显然,前提是释放了两个引用,程序员重新为相应变量做了NULL初始化。

但对于WeakGlobalRef来说,需要使用下述代码判定:

(*env)->IsSameObject(env,wobj,NULL)

因为,对于一个WeakGlobalRef来说可能指向已经被GC的无效对象。

七、数据类型的传递

注意:代码中如果env->C++语言,如果是(*env)->C语言

1)、基本类型的传递

Java的基本类型和对应的JNI类型传递时没有问题的。

2)String参数的传递

......

privatenativeStringgetLine(Stringprompt);//java定义的native方法

......

......

JNIEXPORTjstringJNICALLJava_Prompt_getLine(JNIEnv*env,jobjectthis,

jstringprompt);//JNI中与native方法对应的JNI方法

......

如上,在JNI函数中是不能直接使用jstringprompt,编译会报错,因为JNI都是用CC++编写的,这两种语言中没有jstring类型,所以使用的过程中必须要做一些处理。

......

char*str;

str=env->GetStringUTFChars(prompt,false);//jstring类型变成一个char*类型

......

(*env)->ReleaseStringUTFChars(env,prompt,str);//使用完后要记得释放内存

......

返回的时候,要生成一个jstring类型的对象,也必须通过如下命令

jstringrtstr=env->NewStringUTF(tmpstr);

3)、数组类型的传递

String一样,JNIJava基本类型的数组提供了j*Array类型,比如int[]对应的就是jintArray。来看一个传递int数组的例子,

JNIEXPORTjintJNICALL

Java_IntArray_sumArray(JNIEnv*env,jobjectobj,jintArrayarr){

jint*carr;

carr=env->GetIntArrayElements(arr,false);//分配内存空间

if(carr==NULL){

return0;

}

jintsum=0;

for(inti=0;i<10;i++){

sum+=carr[i];

}

env->ReleaseIntArrayElements(arr,carr,0);//释放内存空间

returnsum;

}

这个例子中的GetIntArrayElementsReleaseIntArrayElements函数就是JNI提供用

于处理int数组的函数。如果试图用arr的方式去访问jintArray类型,毫无疑问会出错。JNI

还提供了另一对函数GetIntArrayRegionReleaseIntArrayRegion访问int数组,就不介绍

,对于其他基本类型的数组,方法类似。

4)、二维数组和String数组

JNI,二维数组和String数组都被视为object数组,因为数组和String被视为object。用一个例子来说明,这次是一个二维int数组,作为返回值。

JNIEXPORTjobjectArrayJNICALL

Java_ObjectArrayTest_initInt2DArray(JNIEnv*env,jclasscls,intsize){

jobjectArrayresult;//因为要返回值,所以需要新建一个jobjectArray对象。

jclassintArrCls=env->FindClass("[I");

result=env->NewObjectArray(size,intArrCls,NULL);//result分配空间。

for(inti=0;i<size;i++){

jinttmp[256];

jintArrayiarr=env->NewIntArray(size);//是为一维int数组iarr分配空间。

for(intj=0;j<size;j++){

tmp[j]=i+j;

}

env->SetIntArrayRegion(iarr,0,size,tmp);//是为iarr赋值。

env->SetObjectArrayElement(result,i,iarr);//是为result的第i个元素赋值。

env->DeleteLocalRef(iarr);//释放局部对象的引用

}

returnresult;

}

jclassintArrCls=env->FindClass("[I");

是创建一个jclass的引用,因为result的元素是一维int数组的引用,所以intArrCls必须是一维int数组的引用,这一点是如何保证的呢?注意FindClass的参数"[I",JNI就是通

过它来确定引用的类型的,I表示是int类型,[标识是数组。对于其他的类型,都有相应的表

示方法,详细见JNI签名。

八、JNI异常处理

JNI中也有异常,不过它和C++java中的异常不一样。如果JNI层出现异常,它不会中断本地函数,直到返回java层,由java虚拟机抛出异常。虽然JNI层不会抛出异常,但是在异常产生的时候它会做一些资源清理的工作,所以,如果在JNI层的函数出现异常时,调用JNIEnv异常函数外的其他函数会导致程序死掉。

示例代码

-->android_media_MediaScanner.cpp

......

virtualstatus_tscanFile(constchar*path,longlonglastModified,

longlongfileSize,boolisDirectory,boolnoMedia)

{

ALOGV("scanFile:path(%s),time(%lld),size(%lld)andisDir(%d)",

path,lastModified,fileSize,isDirectory);

jstringpathStr;

//调用失败后直接返回,不要再让程序做任何事情

if((pathStr=mEnv->NewStringUTF(path))==NULL){

mEnv->ExceptionClear();

returnNO_MEMORY;

}

mEnv->CallVoidMethod(mClient,mScanFileMethodID,pathStr,lastModified,

fileSize,isDirectory,noMedia);

mEnv->DeleteLocalRef(pathStr);

returncheckAndClearExceptionFromCallback(mEnv,"scanFile");

}

......

<--

异常Demo

java

classCatchThrow{

privatenativevoiddoit()throwsIllegalArgumentException;

privatevoidcallback()throwsNullPointerException{

thrownewNullPointerException("CatchThrow.callback");

}

publicstaticvoidmain(Stringargs[]){

CatchThrowc=newCatchThrow();

try{

c.doit();

}catch(Exceptione){

System.out.println("InJava:\n\t"+e);

}

}

static{

System.loadLibrary("CatchThrow");

}

}

C

JNIEXPORTvoidJNICALL

Java_CatchThrow_doit(JNIEnv*env,jobjectobj)

{

jthrowableexc;

jclasscls=(*env)->GetObjectClass(env,obj);

jmethodIDmid=

(*env)->GetMethodID(env,cls,"callback","()V");

if(mid==NULL){

return;

}

(*env)->CallVoidMethod(env,obj,mid);

//判断是否有异常发生

exc=(*env)->ExceptionOccurred(env);

if(exc){

/*Wedon'tdomuchwiththeexception,exceptthat

weprintadebugmessageforit,clearit,and

throwanewexception.*/

jclassnewExcCls;

//描述异常

(*env)->ExceptionDescribe(env);

//清除异常

(*env)->ExceptionClear(env);

newExcCls=(*env)->FindClass(env,

"java/lang/IllegalArgumentException");

if(newExcCls==NULL){

/*Unabletofindtheexceptionclass,giveup.*/

return;

}

//java层抛出异常

(*env)->ThrowNew(env,newExcCls,"thrownfromCcode");

}

}

结果

java.lang.NullPointerException:

atCatchThrow.callback(CatchThrow.java)

atCatchThrow.doit(NativeMethod)

atCatchThrow.main(CatchThrow.java)

InJava:

java.lang.IllegalArgumentException:thrownfromCcode

九、文档描述

本文结合《深入理解Android》《jni详解》等文章对jni技术做了简单的剖析,这些对学习androidjni层会有不错的帮助,在后续还会对文档修改完善。

本地JNIDemo调试步骤(Linux)

@以下步骤都是在同一个目录下

1创建Hello.java

Hello.java

publicclassHello.java{

privatenativevoidprint();

static{

System.loadLibrary(Hello);

}

publicstaticvoidmain(String[]args){

newHello().print();

}

}

2)JNI静态注册

$javacHello.java(生成Hello.class)

$javah-jniHello(生成Hello.h)

Hello.h

/*DONOTEDITTHISFILE-itismachinegenerated*/

#include<jni.h>

/*HeaderforclassHello*/

#ifndef_Included_Hello

#define_Included_Hello

#ifdef__cplusplus

extern"C"{

#endif

/*

*Class:Hello

*Method:print

*Signature:()V

*/

JNIEXPORTvoidJNICALLJava_Hello_print

(JNIEnv*,jobject);

#ifdef__cplusplus

}

#endif

#endif

3)创建Hello.c

Hello.c

#include<stdio.h>

#include"Hello.h"/*注意要引入Hello.h头文件*/

JNIEXPORTvoidJNICALL//静态注册native方法

Java_Hello_print(JNIEnv*env,jobjectobj)

{

printf("HelloWorld!\n");

}

4)编译成*.so库文件

$gccHello.c-fPIC-shared-olibHello.so//注意这里(linux中库文件都是以lib开头的)

5)设置Lib库文件环境变量

运行前,必须保证连接器,能找到待装载的库,不然,将抛如下异常:

java.lang.UnsatisfiedLinkError:noHelloWorldinlibrarypath

atjava.lang.Runtime.loadLibrary(Runtime.java)

atjava.lang.System.loadLibrary(System.java)

$LD_LIBRARY_PATH=.

$exportLD_LIBRARY_PATH

6)运行

$javaHello

<!--EndFragment-->

更多相关文章

  1. C语言函数以及函数的使用
  2. Unity调用Android配置方法
  3. Android 的相关文件类型
  4. Android 滑动手势侦测方法介绍
  5. 安卓selector使用方法
  6. 应用界面主题Theme使用方法
  7. Android 使用html做UI的方法---js与java的相互调用
  8. Android关闭JIT的方法

随机推荐

  1. android http://code.google.com/p/robog
  2. Android之SurfaceView学习一
  3. 状态栏通知Notification用法
  4. 仿Iphone右划关闭Activity
  5. Android如何选择targetSDKVersion
  6. android 使用libmad 生成MP3左右声道的PC
  7. Android Sina Oauth use sina sdk and si
  8. Android MediaPlayer 播放音频文件
  9. 我的Android进阶之旅------>Android 设置
  10. android studio 使用专用的sdk和virtual