Android中JNI编程的那些事儿
2011年06月21日
Android中JNI编程的那些事儿
首先说明,Android系统不允许一个纯粹使用C/C++的程序出现,它要求必须是通过Java代码嵌入Native C/C++--即通过JNI的方式来使用本地(Native)代码。因此JNI对Android底层开发人员非常重要。
如何将.so文件打包到.APK
让我们 先 从最简单的情况开始,假如已有一个JNI实现--libxxx.so文件,那么如何在APK中使用它呢?
在我最初写类似程序的时候,我会将libxxx.so文件push到/system/lib/目录下,然后在Java代码中执行System.loadLibrary(xxx),这是个可行的做法,但需要取得/system/lib 目录 的写权限(模拟器通过adb remount取得该权限)。但模拟器 重启之 后libxxx.so文件会消失。现在 我找到了更好的方法,把.so文件打包到apk中分发给最终用户,不管是模拟器 或者 真机 ,都不再需要system分区的写权限。实现步骤如下:
1、在你的项目根目录下建立libs/armeabi目录;
2、将libxxx.so文件copy到 libs/armeabi/下;
3、此时ADT插件自动编译输出的.apk文件中已经包括.so文件了;
4、安装APK文件,即可直接使用JNI中的方法;
我想还需要简单说明一下libxxx.so的命名规则,沿袭Linux传统,lib.so是类库文件名称的格式,但在Java的System.loadLibrary(" something ")方法中指定库名称时,不能包括 前缀-- lib,以及后缀--.so。
准备编写自己的JNI模块
你一定想知道如何编写自己的xxx.so,不过这涉及了太多有关JNI的知识。简单的说:JNI是Java平台定义的用于和宿主平台上的本地代码进行交互的"Java标准",它通常有两个使用场景:1.使用(之前使用c/c++、delphi开发的)遗留代码;2.为了更好、更直接地与硬件交互并 获得更高性能 。你可以通过以下链接了解JNI的更多资料: Java Native Interface Developer Guides
Java Native Interface Specification
Java本地接口(JNI)基本功能
Book:JNI Programmer's Guide and Specification
JNI之Hello World 1、首先创建含有native方法的Java类: Java代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=package%20com.okwap.testjni%3 B%0A%0Apublic%20final%20class%20MyJNI%20%7B%0A%09%2 F%2Fnative%E6%96%B9%E6%B3%95%2C%0A%09public%20stati c%20native%20String%20sayHello(String%20name)%3B%0A %7D">
package com.okwap.testjni;
public final class MyJNI {
//native方法,
public static native String sayHello(String name);
}
2、通过javah命令生成.h文件,内容如下(com_okwap_testjni.h文件): C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%2F*%20DO%20NOT%20EDIT%20THIS %20FILE%20-%20it%20is%20machine%20generated%20*%2F% 0A%23include%20%3Cjni.h%3E%0A%2F*%20Header%20for%20 class%20com_okwap_testjni_MyJNI%20*%2F%0A%0A%23ifnd ef%20_Included_com_okwap_testjni_MyJNI%0A%23define% 20_Included_com_okwap_testjni_MyJNI%0A%23ifdef%20__ cplusplus%0Aextern%20%22C%22%20%7B%0A%23endif%0A%2F *%0A%20*%20Class%3A%20%20%20%20%20com_okwap_testjni _MyJNI%0A%20*%20Method%3A%20%20%20%20sayHello%0A%20 *%20Signature%3A%20(Ljava%2Flang%2FString%3B)Ljava% 2Flang%2FString%3B%0A%20*%2F%0AJNIEXPORT%20jstring% 20JNICALL%20Java_com_okwap_testjni_MyJNI_sayHello%0 A%20%20(JNIEnv%20*%2C%20jclass%2C%20jstring)%3B%0A% 0A%23ifdef%20__cplusplus%0A%7D%0A%23endif%0A%23endi f">
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_okwap_testjni_MyJNI */
#ifndef _Included_com_okwap_testjni_MyJNI
#define _Included_com_okwap_testjni_MyJNI
#ifdef __cplusplus
extern "C" { #endif /*
* Class: com_okwap_testjni_MyJNI
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_okwap_testjni_MyJNI_sayHello
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus } #endif #endif
这是一个标准的C语言头文件,其中的JNIEXPORT、JNICALL是JNI关键字(事实上它是没有任何内容的宏,仅用于指示性说明),而jint、jstring是JNI环境下对int及java.lang.String类型的映射。这些关键字的定义都可以在jni.h中看到。
3、在 com_okwap_testjni.c文件中实现以上方法: Java代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%23include%20%3Cstring.h%3E%0 A%23include%20%3Cjni.h%3E%0A%23include%20%22com_okw ap_testjni.h%22%0A%0AJNIEXPORT%20jstring%20JNICALL% 20Java_com_okwap_testjni_MyJNI_sayHello(JNIEnv*%20e nv%2C%20jclass%2C%20jstring%20str)%7B%0A%20%20%20%2 0%2F%2F%E4%BB%8Ejstring%E7%B1%BB%E5%9E%8B%E5%8F%96% E5%BE%97c%E8%AF%AD%E8%A8%80%E7%8E%AF%E5%A2%83%E4%B8 %8B%E7%9A%84char*%E7%B1%BB%E5%9E%8B%0A%20%20%20%20c onst%20char*%20name%20%3D%20(*env)-%3EGetStringUTFC hars(env%2C%20str%2C%200)%3B%0A%20%20%20%20%2F%2F%E 6%9C%AC%E5%9C%B0%E5%B8%B8%E9%87%8F%E5%AD%97%E7%AC%A 6%E4%B8%B2%0A%20%20%20%20char*%20hello%20%3D%20%22% E4%BD%A0%E5%A5%BD%EF%BC%8C%22%3B%0A%20%20%20%20%2F% 2F%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D%E7%9B%AE%E6% A0%87%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%A9%BA%E9%97%B4% 0A%20%20%20%20char*%20result%20%3D%20malloc((strlen (name)%20%2B%20strlen(hello)%20%2B%201)*sizeof(char ))%3B%0A%20%20%20%20memset(result%2C0%2Csizeof(resu lt))%3B%0A%20%20%20%20%2F%2F%E5%AD%97%E7%AC%A6%E4%B 8%B2%E9%93%BE%E6%8E%A5%0A%20%20%20%20strcat(result% 2Chello)%3B%0A%20%20%20%20strcat(result%2Cname)%3B% 0A%20%20%20%20%2F%2F%E9%87%8A%E6%94%BEjni%E5%88%86% E9%85%8D%E7%9A%84%E5%86%85%E5%AD%98%0A%20%20%20%20( *env)-%3EReleaseStringUTFChars(env%2Cstr%2Cname)%3B %0A%20%20%20%20%2F%2F%E7%94%9F%E6%88%90%E8%BF%94%E5 %9B%9E%E5%80%BC%E5%AF%B9%E8%B1%A1%0A%20%20%20%20str %20%3D%20(*env)-%3ENewStringUTF(env%2C%20%22%E4%BD% A0%E5%A5%BD%20JNI~!%22)%3B%0A%20%20%20%20%2F%2F%E9% 87%8A%E6%94%BE%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D% E7%9A%84%E5%86%85%E5%AD%98%0A%20%20%20%20free(resul t)%3B%0A%0A%20%20%20%20%2F%2F%0A%20%20%20%20return% 20str%3B%0A%7D">
#include
#include
#include "com_okwap_testjni.h"
JNIEXPORT jstring JNICALL Java_com_okwap_testjni_MyJNI_sayHello(JNIEnv* env, jclass, jstring str){
//从jstring类型取得c语言环境下的char*类型
const char* name = (*env)->GetStringUTFChars(env, str, 0);
//本地常量字符串
char* hello = "你好,";
//动态分配目标字符串空间
char* result = malloc((strlen(name) + strlen(hello) + 1)*sizeof(char));
memset(result,0,sizeof(result));
//字符串链接
strcat(result,hello);
strcat(result,name);
//释放jni分配的内存
(*env)->ReleaseStringUTFChars(env,str,name);
//生成返回值对象
str = (*env)->NewStringUTF(env, "你好 JNI~!");
//释放动态分配的内存
free(result);
//
return str; }
4、编译--两种不同的编译环境
以上的C语言代码要编译成最终.so动态库文件,有两种途径: Android NDK :全称是Native Developer Kit,是用于编译本地JNI源码的工具,为开发人员将本地方法整合到Android应用中提供了方便。事实上NDK和完整源码编译环境一样,都使用Android的编译系统--即通过Android.mk文件控制编译。NDK可以运行在Linux、Mac、Window(+cygwin)三个平台上。有关NDK的使用方法及更多细节请参考以下资料:
eoe特刊第七期《NDK总结》http://blog.eoemobile.com/?p=27
http://androidappdocs.appspot.com/sdk/ndk/index.ht ml ;
完整源码编译环境 :Android平台提供有基于make的编译系统,为App编写正确的Android.mk文件就可使用该编译系统。该环境需要通过git从官方网站获取完整源码副本并成功编译,更多细节请参考:http://source.android.com/index.html
不管你选择以上两种方法的哪一个,都必须编写自己的Android.mk文件,有关该文件的编写请参考相关文档。
JNI组件的入口函数--JNI_OnLoad()、JNI_OnUnload()
JNI组件被成功加载和卸载时,会进行函数回调,当VM执行到System.loadLibrary(xxx)函数时,首先会去执行JNI组件中的JNI_OnLoad()函数,而当VM释放该组件时会呼叫JNI_OnUnload()函数。先看示例代码: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%2F%2FonLoad%E6%96%B9%E6%B3%9 5%EF%BC%8C%E5%9C%A8System.loadLibrary()%E6%89%A7%E8 %A1%8C%E6%97%B6%E8%A2%AB%E8%B0%83%E7%94%A8%0Ajint%2 0JNI_OnLoad(JavaVM*%20vm%2C%20void*%20reserved)%7B% 0A%09LOGI(%22JNI_OnLoad%20startup~~!%22)%3B%0A%20%2 0%20%20%20%20%20%20return%20JNI_VERSION_1_4%3B%0A%7 D%09%0A%0A%2F%2FonUnLoad%E6%96%B9%E6%B3%95%EF%BC%8C %E5%9C%A8JNI%E7%BB%84%E4%BB%B6%E8%A2%AB%E9%87%8A%E6 %94%BE%E6%97%B6%E8%B0%83%E7%94%A8%0Avoid%20JNI_OnUn load(JavaVM*%20vm%2C%20void*%20reserved)%7B%0A%09LO GE(%22call%20JNI_OnUnload%20~~!!%22)%3B%0A%7D%0A">
//onLoad方法,在System.loadLibrary()执行时被调用
jint JNI_OnLoad(JavaVM* vm, void* reserved){
LOGI("JNI_OnLoad startup~~!");
return JNI_VERSION_1_4;
}
//onUnLoad方法,在JNI组件被释放时调用
void JNI_OnUnload(JavaVM* vm, void* reserved){
LOGE("call JNI_OnUnload ~~!!"); }
JNI_OnLoad()有两个重要的作用: 指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当。
JNI_OnUnload()的作用与JNI_OnLoad()对应,当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。
使用registerNativeMethods方法
对Java程序员来说,可能我们总是会遵循:1.编写带有native方法的Java类;--->2.使用javah命令生成.h头文件;--->3.编写代码实现头文件中的方法,这样的"官方" 流程,但也许有人无法忍受那"丑陋"的方法名称,RegisterNatives方法能帮助你把c/c++中的方法隐射到Java中的native方法,而无需遵循特定的方法命名格式。来看一段示例代码吧: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%2F%2F%E5%AE%9A%E4%B9%89%E7%9 B%AE%E6%A0%87%E7%B1%BB%E5%90%8D%E7%A7%B0%0Astatic%2 0const%20char%20*className%20%3D%20%22com%2Fokwap%2 Ftestjni%2FMyJNI%22%3B%0A%0A%2F%2F%E5%AE%9A%E4%B9%8 9%E6%96%B9%E6%B3%95%E9%9A%90%E5%B0%84%E5%85%B3%E7%B 3%BB%0Astatic%20JNINativeMethod%20methods%5B%5D%20% 3D%20%7B%0A%20%20%7B%22sayHello%22%2C%20%22(Ljava%2 Flang%2FString%3B)Ljava%2Flang%2FString%3B%22%2C%20 (void*)sayHello%7D%2C%0A%7D%3B%0A%0Ajint%20JNI_OnLo ad(JavaVM*%20vm%2C%20void*%20reserved)%7B%0A%20%20% 2F%2F%E5%A3%B0%E6%98%8E%E5%8F%98%E9%87%8F%0A%20%20j int%20result%20%3D%20JNI_ERR%3B%0A%20%20JNIEnv*%20e nv%20%3D%20NULL%3B%0A%20%20jclass%20clazz%3B%0A%20% 20int%20methodsLenght%3B%0A%09%0A%20%20%2F%2F%E8%8E %B7%E5%8F%96JNI%E7%8E%AF%E5%A2%83%E5%AF%B9%E8%B1%A1 %0A%20%20if%20((*vm)-%3EGetEnv(vm%2C%20(void**)%20% 26env%2C%20JNI_VERSION_1_4)%20!%3D%20JNI_OK)%20%7B% 0A%20%20LOGE(%22ERROR%3A%20GetEnv%20failed%5Cn%22)% 3B%0A%20%20%20%20return%20JNI_ERR%3B%0A%20%20%7D%0A %20%20assert(env%20!%3D%20NULL)%3B%0A%09%0A%20%20%2 F%2F%E6%B3%A8%E5%86%8C%E6%9C%AC%E5%9C%B0%E6%96%B9%E 6%B3%95.Load%20%E7%9B%AE%E6%A0%87%E7%B1%BB%0A%20%20 clazz%20%3D%20(*env)-%3EFindClass(env%2CclassName)% 3B%0A%20%20if%20(clazz%20%3D%3D%20NULL)%20%7B%0A%20 %20%20%20LOGE(%22Native%20registration%20unable%20t o%20find%20class%20'%25s'%22%2C%20className)%3B%0A% 20%20%20%20return%20JNI_ERR%3B%0A%20%20%7D%0A%0A%20 %20%2F%2F%E5%BB%BA%E7%AB%8B%E6%96%B9%E6%B3%95%E9%9A %90%E5%B0%84%E5%85%B3%E7%B3%BB%0A%20%20%2F%2F%E5%8F %96%E5%BE%97%E6%96%B9%E6%B3%95%E9%95%BF%E5%BA%A6%0A %20%20methodsLenght%20%3D%20sizeof(methods)%20%2F%2 0sizeof(methods%5B0%5D)%3B%0A%20%20if%20((*env)-%3E RegisterNatives(env%2Cclazz%2C%20methods%2C%20metho dsLenght)%20%3C%200)%20%7B%0A%20%20%20%20LOGE(%22Re gisterNatives%20failed%20for%20'%25s'%22%2C%20class Name)%3B%0A%20%20%20%20return%20JNI_ERR%3B%0A%20%20 %7D%0A%0A%20%20%2F%2F%0A%20%20result%20%3D%20JNI_VE RSION_1_4%3B%0A%20%20return%20result%3B%20%20%0A%7D %0A">
//定义目标类名称
static const char *className = "com/okwap/testjni/MyJNI";
//定义方法隐射关系
static JNINativeMethod methods[] = {
{"sayHello", "(Ljava/lang/String;)Ljava/lang/String;", (void*)sayHello},
};
jint JNI_OnLoad(JavaVM* vm, void* reserved){
//声明变量
jint result = JNI_ERR;
JNIEnv* env = NULL;
jclass clazz;
int methodsLenght;
//获取JNI环境对象
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
return JNI_ERR;
}
assert(env != NULL);
//注册本地方法.Load 目标类
clazz = (*env)->FindClass(env,className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_ERR;
}
//建立方法隐射关系
//取得方法长度
methodsLenght = sizeof(methods) / sizeof(methods[0]);
if ((*env)->RegisterNatives(env,clazz, methods, methodsLenght)
typedef struct {
const char* name;//java方法名称
const char* signature; //java方法签名
void* fnPtr;//c/c++的函数指针
} JNINativeMethod;
参照上文示例中初始化该结构的代码: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%2F%2F%E5%AE%9A%E4%B9%89%E6%9 6%B9%E6%B3%95%E9%9A%90%E5%B0%84%E5%85%B3%E7%B3%BB%0 Astatic%20JNINativeMethod%20methods%5B%5D%20%3D%20% 7B%0A%20%20%7B%22sayHello%22%2C%20%22(Ljava%2Flang% 2FString%3B)Ljava%2Flang%2FString%3B%22%2C%20(void* )sayHello%7D%2C%0A%7D%3B">
//定义方法隐射关系
static JNINativeMethod methods[] = {
{"sayHello", "(Ljava/lang/String;)Ljava/lang/String;", (void*)sayHello},
};
其中比较难以理解的是第二个参数--signature字段的取值,实际上这些字符与函数的参数类型/返回类型一一对应,其中"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void func(),"(II)V" 表示 void func(int, int),具体的每一个字符的对应关系如下:
字符 Java类型 C/C++类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示:
字符 java类型 c/c++类型
[Z jbooleanArray boolean[]
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
上面的都是基本类型,如果参数是Java类,则以"L"开头,以";"结尾,中间是用"/"隔开包及类名,而其对应的C函数的参数则为jobject,一个例外是String类,它对应C类型jstring,例如:Ljava/lang /String; 、Ljava/net/Socket; 等,如果JAVA函数位于一个嵌入类(也被称为内部类),则用$作为类名间的分隔符,例如:"Landroid/os/FileUtils$FileStatus;"。
使用registerNativeMethods方法不仅仅是为了改变那丑陋的长方法名,最重要的是可以提高效率,因为当Java类别透过VM呼叫到本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),如果某方法需要连续呼叫很多次,则每次都要寻找一遍,所以使用RegisterNatives将本地函数向VM进行登记,可以让其更有效率的找到函数。
registerNativeMethods方法的另一个重要用途是,运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次调用registerNativeMethods()方法,并传入不同的映射表参数即可。
JNI中的日志输出
你一定非常熟悉在Java代码中使用Log.x(TAG,"message")系列方法,在c/c++代码中也一样,不过首先你要include相关头文件。遗憾的是你使用不同的编译环境( 请参考上文中两种编译环境的介绍) ,对应的头文件略有不同。。
如果是在完整源码编译环境下,只要include 头文件,就可以使用对应的LOGI、LOGD等方法了,同时请定义LOG_TAG,LOG_NDEBUG等宏值,示例代码如下: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%23define%20LOG_TAG%20%22Hell oJni%22%0A%23define%20LOG_NDEBUG%200%0A%23define%20 LOG_NIDEBUG%200%0A%23define%20LOG_NDDEBUG%200%0A%0A %23include%20%3Cstring.h%3E%0A%23include%20%3Cjni.h %3E%0A%23include%20%3Cutils%2FLog.h%3E%0Ajstring%20 Java_com_inc_android_ime_HelloJni_stringFromJNI(JNI Env*%20env%2Cjobject%20thiz)%7B%0A%20%20%20%20LOGI( %22Call%20stringFromJNI!%5Cn%22)%3B%0A%20%20%20%20r eturn%20(*env)-%3ENewStringUTF(env%2C%20%22Hello%20 from%20JNI%20%EF%BC%88%E4%B8%AD%E6%96%87%EF%BC%89!% 22)%3B%0A%7D">
#define LOG_TAG "HelloJni"
#define LOG_NDEBUG 0
#define LOG_NIDEBUG 0
#define LOG_NDDEBUG 0
#include
#include
#include
jstring Java_com_inc_android_ime_HelloJni_stringFromJNI(JN IEnv* env,jobject thiz){
LOGI("Call stringFromJNI!\n");
return (*env)->NewStringUTF(env, "Hello from JNI (中文)!");
}
与日志相关的.h头文件,在以下源码路径:
myeclair\frameworks\base\include\utils\Log.h
myeclair\system\core\include\cutils\log.h
如果你是在NDK环境下编译,则需要#include ,示例代码如下: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%23define%20LOG_TAG%20%22Hell oJni%22%0A%0A%23include%20%3Cstring.h%3E%0A%23inclu de%20%3Cjni.h%3E%0A%23include%20%3Cutils%2FLog.h%3E %0Ajstring%20Java_com_inc_android_ime_HelloJni_stri ngFromJNI(JNIEnv*%20env%2Cjobject%20thiz)%7B%0A%20% 20%20%20__android_log_print(ANDROID_LOG_INFO%2CLOG_ TAG%2C%22Call%20stringFromJNI!%5Cn%22)%3B%0A%20%20% 20%20return%20(*env)-%3ENewStringUTF(env%2C%20%22He llo%20from%20JNI%20%EF%BC%88%E4%B8%AD%E6%96%87%EF%B C%89!%22)%3B%0A%7D">
#define LOG_TAG "HelloJni"
#include
#include
#include
jstring Java_com_inc_android_ime_HelloJni_stringFromJNI(JN IEnv* env,jobject thiz){
__android_log_print(ANDROID_LOG_INFO,LOG_TAG,"Call stringFromJNI!\n");
return (*env)->NewStringUTF(env, "Hello from JNI (中文)!");
}
很可惜,其中用于日志输出的方法是: __android_log_print(....) , 并不是我们熟悉的LOG.x(...)系列方法。不过好的一点是android/log.h文件在完整源码环境下也是可用的,因此,可以用一下的头文件来统两种环境下的差异: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%2F*%0A%20*%20jnilogger.h%0A% 20*%0A%20*%20%20Created%20on%3A%202010-11-15%0A%20* %20%20%20%20%20%20Author%3A%20INC062805%0A%20*%2F%0 A%0A%23ifndef%20__JNILOGGER_H_%0A%23define%20__JNIL OGGER_H_%0A%0A%23include%20%3Candroid%2Flog.h%3E%0A %0A%23ifdef%20_cplusplus%0Aextern%20%22C%22%20%7B%0 A%23endif%0A%0A%23ifndef%20LOG_TAG%0A%23define%20LO G_TAG%20%20%20%20%22MY_LOG_TAG%22%0A%23endif%0A%0A% 23define%20LOGD(...)%20%20__android_log_print(ANDRO ID_LOG_DEBUG%2CLOG_TAG%2C__VA_ARGS__)%0A%23define%2 0LOGI(...)%20%20__android_log_print(ANDROID_LOG_INF O%2CLOG_TAG%2C__VA_ARGS__)%0A%23define%20LOGW(...)% 20%20__android_log_print(ANDROID_LOG_WARN%2CLOG_TAG %2C__VA_ARGS__)%0A%23define%20LOGE(...)%20%20__andr oid_log_print(ANDROID_LOG_ERROR%2CLOG_TAG%2C__VA_AR GS__)%0A%23define%20LOGF(...)%20%20__android_log_pr int(ANDROID_LOG_FATAL%2CLOG_TAG%2C__VA_ARGS__)%0A%0 A%0A%23ifdef%20__cplusplus%0A%7D%0A%23endif%0A%0A%2 3endif%20%2F*%20__JNILOGGER_H_%20*%2F%0A">
/*
* jnilogger.h *
* Created on: 2010-11-15
* Author: INC062805 */
#ifndef __JNILOGGER_H_
#define __JNILOGGER_H_
#include
#ifdef _cplusplus
extern "C" { #endif
#ifndef LOG_TAG
#define LOG_TAG "MY_LOG_TAG" #endif
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA _ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA _ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA _ARGS__)
#ifdef __cplusplus } #endif
#endif /* __JNILOGGER_H_ */
你可以下载以上头文件,来统一两种不同环境下的使用差异。另外,不要忘了在你的Android.mk文件中加入对类库的应用,两种环境下分别是: Java代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=ifeq%20(%24(HOST_OS)%2Cwindow s)%0A%23NDK%E7%8E%AF%E5%A2%83%E4%B8%8B%0A%09LOCAL_L DLIBS%20%3A%3D%20-llog%0Aelse%0A%23%E5%AE%8C%E6%95% B4%E6%BA%90%E7%A0%81%E7%8E%AF%E5%A2%83%E4%B8%8B%0A% 09LOCAL_SHARED_LIBRARIES%20%3A%3D%20libutils%0Aendi f">
ifeq ($(HOST_OS),windows)
#NDK环境下
LOCAL_LDLIBS := -llog
else
#完整源码环境下
LOCAL_SHARED_LIBRARIES := libutils
endif
。未完待续》。。。。。。。。。。。。。。
Android为JNI提供的助手方法
myeclair\dalvik\libnativehelper\include\nativehelp er
在完整源码编译环境下,Android在myeclair\dalvik\libnativehelper\include\nativehelp er\JNIHelp.h头文件中 提供了助手函数 ,用于本地方法注册、异常处理等任务,还有一个用于计算方法隐射表长度的宏定义: C代码 type="application/x-shockwave-flash" width="14" height="15" src="http://cnetwei.iteye.com/javascripts/syntaxhi ghlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflash player" allowscriptaccess="always" quality="high" flashvars="clipboard=%23ifndef%20NELEM%0A%23%20def ine%20NELEM(x)%20((int)%20(sizeof(x)%20%2F%20sizeof ((x)%5B0%5D)))%0A%23endif%0A%0A%2F%2F%E6%9C%89%E4%B A%86%E4%BB%A5%E4%B8%8A%E5%AE%8F%E5%AE%9A%E4%B9%89%E 5%90%8E%EF%BC%8C%E6%B3%A8%E5%86%8C%E6%96%B9%E6%B3%9 5%E5%8F%AF%E4%BB%A5%E6%8C%89%E5%A6%82%E4%B8%8B%E5%8 6%99%EF%BC%8C%E8%AF%A5%E5%AE%8F%E5%AE%9A%E4%B9%89%E 5%8F%AF%E4%BB%A5%E7%9B%B4%E6%8E%A5copy%E5%88%B0NDK% E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BD%BF%E7%94%A8%EF%BC% 9A%0A(*env)-%3ERegisterNatives(env%2Cclazz%2C%20met hods%2CNELEM(methods))%3B">
#ifndef NELEM
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
//有了以上宏定义后,注册方法可以按如下写,该宏定义可以直接copy到NDK环境下使用:
(*env)->RegisterNatives(env,clazz, methods,NELEM(methods));
更多关于JNI在Android中的信息:
JNI系列(1):基础篇
JNI系列(2):jstring操作
JN系列(3):如何得到JavaVM,JNIEnv接口
JNI系列(4):如何访问自定义类对象
。。。。。。。。。。
http://cnetwei.iteye.com/blog/825306
发表评论
-
Android入门--写一个最简单的计算器
2012-01-20 02:51 791Android入门--写一个最简单的计算器 2011年04月 ... -
Android编译,打包、签名详细教程
2012-01-20 02:51 601Android编译,打包、签名详细教程 2011年05月25 ... -
Android命令行手动编译打包详解
2012-01-20 02:51 825Android命令行手动编译打 ... -
As3.0 decompiler反编译器
2012-01-20 02:51 581As3.0 decompiler反编译器 2 ... -
神马都是浮云
2012-01-19 10:43 456神马都是浮云 2011年06月02日 心情突然莫名的伤感 ... -
汪汪,记住神马都是浮云、
2012-01-19 10:43 533汪汪,记住神马都是浮云、 2012年01月08日 神 ... -
神马都是浮云
2012-01-19 10:43 569神马都是浮云 2011年09月14日 2011 ... -
神马都是浮云
2012-01-19 10:43 540神马都是浮云 2011年12月30日 坎坷的2011即将 ... -
想开了,神马都是浮云
2012-01-19 10:43 503想开了,神马都是浮云 2012年01月09日 最近几日的 ... -
常见的硬盘内存问题的解决方案,ADSL错误代码
2012-01-17 03:14 475常见的硬盘内存问题的解决方案,ADSL错误代码 2011年0 ... -
折腾我N久的flash 8软件终于可以打开了
2012-01-17 03:14 757折腾我N久的flash 8软件 ... -
Erase flash,Erase NVRAM命令与
2012-01-17 03:14 2621Erase flash,Erase NVRAM命令与 201 ... -
电脑知识
2012-01-17 03:14 540电脑知识 2010年06月23日 ... -
掌握这些电脑知识,你可以玩的很无耻
2012-01-17 03:14 594掌握这些电脑知识,你可以玩的很无耻 2011年09月05日 ... -
东北话4-8级考试~东北人试试!
2012-01-16 01:48 669东北话4-8级考试~东北人试试! 2009年06月01日 ... -
看看你是个合格的东北人不
2012-01-16 01:48 934看看你是个合格的东北人不 2009年10月16日 安 ... -
够格东北人测试
2012-01-16 01:48 598够格东北人测试 2009年10月03日 [转] 看看你是 ... -
浅谈医院执行力不佳的原因
2012-01-16 01:48 597浅谈医院执行力不佳的 ... -
《阳光家族核心会议精神概要》
2012-01-16 01:48 667《阳光家族核心会议精神概要》 2011年12月06日 ... -
两个 AIR SQLite 例子
2012-01-11 01:41 627两个 AIR SQLite 例子 2011年09月01日 ...
相关推荐
Android中JNI编程的那些事儿
这是一个Android Studio的jni编程入门实例,配置方式简单易懂,本人有相应的博客说明,欢迎交流。
android studio 中 进行 JNI 开发的一个简单实例,入门demo
这是一个简单的android studio中jni编程实例,包括生成.so和.jar方法,并且用test进行验证。
使用NDK完成Android之JNI编程实例
android精通JNI编程(牛人高焕堂写的)
自写AndroidStudio JNI编程静态注册 编译调用so,雷电模拟器3可正常运行。Android Studio直接导入项目即可使用
JNI和NDK的关系:在Android开发环境中,JNI是实现的目的,NDK是实现JNI的手段,即通过NDK实现JNI功能
Android Studio JNI/NDK 编程; 具体见博客: http://blog.csdn.net/q610098308/article/details/51313341
Android中实现JNI的AES加解密源代码,绝对可以运行!
android中jni的简单使用示例代码
Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例
Android JNI编程demo
JNI编程指南JNI编程指南JNI编程指南JNI编程指南JNI编程指南JNI编程指南
Android中的JNI测试程序 最简单的实现JNI的测试代码,配合我的博客的博文学习
Android使用JNI调用Python so解释器
JNI编程指南.pdf
android通过JNI访问硬件LED,包含应用程序APP,JNI代码,和LED驱动程序。
教你如何生成.h文件,使用window通过NDK编译出.so文件,如何通过JNI调用so文件