1=====概要(android的native开发的重要性,那些方面会用到native开发;
提高某些关键模块的效率;
使用某个以C/C++等Native语言编写的程序库;
硬件操作时候,在与别的公司/团队合作的时候,如果别人提供的是c/c++实现的功能;
由于Native模块的使用,Java代码会丧失其原有的跨平台性和类型安全等特性;
Java代码与Native代码运行于同一个进程空间内;对于跨进程甚至跨宿主环境的Java与Native间通信的需求,可以考虑采用socket、Web Service等IPC通信机制来实现;
2=====,提供源码与提供第三方库(动态库/静态库)
A,B,C,三种mk方式
3=====,全源码编译和ndk编译,写mk文件喝写makefile文件;
在Android中,有两种方式可以调用JNI,一种是Google release的专门针对
Android Native开发的工具包,叫做NDK。去Android网站上下载该工具包后,就可以通过阅读里面的文档来
setup一个新的包含Native代码的工程,创建自己的Android.mk文件,编译等等;另一种是完整的源码编译环
境 ,也就是通过git从官方网站获取完全的Android源代码平台。这个平台中提供有基于make的编译系统。
更多细节请参考这里。不管选择以上两种方法的哪一个,都必须编写自己的Android.mk文件,有关该文件
接下来本应介绍test_getPrintStr。但在此之前,简单介绍Android.mk,也就是编译NDK所需要的
Makefile,从而完成JNI信息链的讲解。Android.mk可以基于模版修改,里面重要的变量包括:
a. LOCAL_C_INCLUDES:包含的头文件。这里需要包含JNI的头文件。
b. LOCAL_SRC_FILES: 包含的源文件。
c. LOCAL_MODULE:当前模块的名称,也就是第一步中我们提到的LIBNAME。注意这个
需要加上lib前缀,但不需要加.so后缀,也就是说应该是libLIBNAME。
d. LOCAL_SHARED_LIBRARIES:当前模块需要依赖的共享库。
e. LOCAL_PRELINK_MODULE:该模块是否被启动就加载。该项设置依具体程序的特性而
定。
4=====,jni方式 c/c++,文件后缀策略;
load与非load方式
private native String getPrintStr();
try {System.loadLibrary("LIBNAME" }
catch (UnsatisfiedLinkError ule)
{Log.e(TAG, "Could not load native library");}
注意JNI会自动补全lib和so给LIBNAME,你只需要提供LIBNAME给loadLibrary就行了。在最后执行的时候,
JNI会先找到这个动态库,然后找里面的OnLoad函数,具体注册流程由OnLoad函数接管
如果把上面的getPrintString函数申明比作原型,那么本地代码中的具体函数定义就应该和该原型匹
配,JNI才能知道具体在哪里执行代码。具体来说,应该有一个对应的Native函数,有和Java中定义
的函数同样的参数列表以及返回值。另外,还需要有某种机制让JNI将两者相互映射,方便参数和
返回值的传递。在老版的JNI中,这是通过丑陋的命名匹配实现的,比如说在Java中定义的函数名
是getPrintStr, 该函数属于package java.come.android.xxx,那么中对应Native代码中的函数名就应该是
Java_com_android_xxx_getPrintStr。这样给开发人员带来了很多不便。可以用javah命令来生成对应
Java code中定义函数的Native code版本header文件,从中得知传统的匹配方法是如何做的。具体过
程如下:
a. 通过SDK的方式编译Java代码。
b. 找到Eclipse的工程目录,进入bin目录下。这里是编译出的java文件所对应的class文件所
在。
c.
假设包括Native函数调用的java文件属于com.android.xxx package,名字叫test.java,那么在
bin下执行javah -jni com.android.xxx.test
执行完后,可以看到一个新生成的header文件,名字为com_android_xxx_test.h。打开后会发现已经
有一个函数申明,函数名为java_com_android_xxx_test_getPrintStr。这个名字就包括了该函数所对应
Java版本所在的包,文件以及名称。这就是JNI传统的确定名字的方法。
值得注意的是,header文件中不仅包含了基于函数名的映射信息,还包含了另一个重要信息,就是
signature。一个函数的signature是一个字符串,描述了这个函数的参数和返回值。其中"()" 中的字符
表示参数,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int); 数组
则以"["开始,用两个字符表示。
具体的每一个字符的对应关系如下:
字符 Java类型 C类型
V void void
I jint int
Z jboolean boolean
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S
jshort
short
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的
包及类名。而其对应的C函数名的参数则为jobject。 一个例外是String类,其对应的类为jstring。举
例:
Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。例如 "(Ljava/lang/String;Landroid/os/
FileUtils$FileStatus;)Z"
这个signature非常重要,是下面要介绍的新版命名匹配方法的关键点之一。所以,即使传统的命名
匹配已经不再使用,javah这一步操作还是必须的,因为可以从中得到Java代码中需要Native执行的
函数的签名,以供后面使用。
3. 在新版(版本号大于1.4)的JNI中,Android提供了另一个机制来解决命名匹配问题,那就是
JNI_OnLoad。正如前面所述,每一次JNI执行Native代码,都是通过调用JNI_OnLoad实现的。下面
的代码是针对本例的OnLoad代码:
/* Returns the JNI version on success, -1 on failure.
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
goto bail;
}
assert(env != NULL);
if (!register_Test(env)) {
LOGE("ERROR: Test native registration failed");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}
仔细分析这个函数。首先,OnLoad通过GetEnv函数获取JNI的环境对象,然后通过register_Test来
注册Native函数。register_Test的实现如下:
int register_Test(JNIEnv *env) {
const char* const ClassPathName = "com/android/xxx/test";
return registerNativeMethods(env, ClassPathName, TestMethods,
sizeof(TestMethods) / sizeof(TestMethods[0]));
}
在这里,ClassPathName是Java类的全名,包括package的全名。只是用 “/” 代替 ”.” 。然后我们把
类名以及TestMethods这个参数一同送到registerNativeMethods这个函数中注册。这个函数是基于
JNI_OnLoad的命名匹配方式的重点。
在JNI中,代码编写者通过函数signature名和映射表的配合,来告诉JNI_OnLoad,你要找的函数在
Native代码中是如何定义的(signature),以及在哪定义的(映射表)。关于signature的生成和含
义,在上面已经介绍。而映射表,是Android使用的一种用于映射Java和C/C++函数的数组,这个数
组的类型是JNINativeMethod,定义为:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
其中,第一个变量是Java代码中的函数名称。第二个变量是该函数对应的Native signature。第三
个变量是该函数对应的Native函数的函数指针。例如,在上面register_Test的函数实现中,传给
registerNativeMethods的参数TestMethods就是映射表,定义如下:
static JNINativeMethod TestMethods[] = {
{"getPrintStr", "()Ljava/lang/String", (void*)test_getPrintStr}
};
其中getPrintStr是在Java代码中定义的函数的名称,()Ljava/lang/String是签名,因为该函数无参数
传入,并返回一个String。test_getPrintStr则是我们即将在Native code中定义的函数名称。该映射
表和前面定义的类名ClassPathName一起传入registerNativeMethods:
static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod*
Methods, int numMethods) {
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
在这里,先load目标类,然后注册Native函数,然后返回状态。
可以看出,通过映射表方式,Java code中的函数名不须再和Native code中的函数名呆板对应。只需
要将函数注册进映射表中,Native code的函数编写就有了很大的灵活性。虽说和前一种传统的匹配
方法比,这种方式并没有效率上的改进,因为两者本质上都是从JNI load开始做函数映射。但是这
一种register的方法极大降低了两边的耦合性,所以实际使用中会受欢迎得多。比如说,由于映射表
是一个<名称,函数指针>对照表,在程序执行时,可多次调用registerNativeMethods()函数来更换本
地函数指针,而达到弹性抽换本地函数的目的。
//
Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针,指向C函数。
其中比较难以理解的是第二个参数,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下
字符 Java类型 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
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring
Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
<5=====>,jni多中方法包装策略,static方法,普通方法,参数传递,global机制;
用于最后测试的test_getPrintStr函数实现如下:
const jstring testStr = env->NewStringUTF("hello, world");
return testStr;
<6>,c调用java,java调用c;
JNI是一个双向的接口:开发者不仅可以通过JNI在Java代码中访问Native模块,还可以在 Native代码中嵌入一个JVM,并通过JNI访问运行于其中的Java模块。可见,JNI担任了一个桥梁的角色,它将JVM与Native模块联系起来,从而实现了Java代码与Native代码的互访;
<7>,多线程中的C调用java,以及很重要的jvm/dvm机制。剖析java和native的实现机制。
<8>,GNU makefile
<9>,架构,分层实现(c/c++ --> jni->java)。
<10>,java的多线程与native多线程的同步与互斥。
<11>,Android/Ndk对c/c++的支持,cpu架构不同时的。
在Android中,仅有以下类库是允许在JNI中使用的:
● libc (C library) headers
● libm (math library) headers
● JNI interface headers
● libz (Zlib compression) headers
● liblog (Android logging) header
● OpenGL ES 1.1 (3D graphics library) headers (since 1.6)
● A Minimal set of headers for C++ support
附:
关于测试时使用Log。调用JNI进行Native Code的开发有两种环境,完整源码环境以及NDK。两种
环境对应的Log输出方式也并不相同,差异则主要体现在需要包含的头文件中。如果是在完整源
码编译环境下,只要include <utils/Log.h>头文件(位于Android-src/system/core/include/cutils)
,就可以使用对应的LOGI、LOGD等方法了,当然LOG_TAG,LOG_NDEBUG等宏值需要自定义。
如果是在NDK环境下编译,则需要include <android/log.h>头文件(位于ndk/android-ndk-r4/
platforms/android-8/arch-arm/usr/include/android/),另外自己定义宏映射,例如:
#include <android/log.h>
#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__)
另外,在Android.mk文件中对类库的应用在两种环境下也不相同。如果是NDK环境下,需要包括
LOCAL_LDLIBS := -llog
而在完整源码环境下,则需要包括
LOCAL_SHARED_LIBRARIES := libutils libcutils
- 浏览: 1030880 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (675)
- ios (214)
- android-course (5)
- unity3d (7)
- cocos2d (36)
- html5 (3)
- game (5)
- android (42)
- java (57)
- php (12)
- 创业 (10)
- SEO (3)
- 架构 (2)
- 数据库 (3)
- 产品设计 (9)
- 操作系统 (10)
- Web前端 (11)
- 其他 (50)
- GAE (1)
- mac os (8)
- Open Source (2)
- 序列号 (10)
- C (2)
- database (2)
- 算法 (6)
- 设计模式 (1)
- photoshop (1)
- 3dmax (1)
- maya (1)
- opengl (3)
- 游戏设计 (1)
- 趋势 (1)
- cocos2d-x (4)
- shell (3)
- c++ (30)
- lua (5)
- flash (1)
- spring (3)
- mysql (4)
- Git (6)
- xmpp (1)
- cocos2dx (14)
- mac (2)
- 编程规范 (2)
- windows (1)
- linux (5)
- coocs2dx (1)
- ubuntu (2)
- aws (1)
- OPENGLES (1)
- 原画 (1)
最新评论
-
jlees:
Best mobile app testing tool pc ...
iOS + XCode 4 + GHUnit = Mobile TDD+Continuous testing -
ipanda:
楼主,能否给一个Micro CloudFoundry的虚机或者 ...
Cloud Foundry使用及开发向导 -
love_zongming:
谢谢分享。。
visio2007序列号 -
雨花台舞水:
你这才是枪文把
套在 360 黑匣子外面的黑盒子:你被技术型枪稿吓到了么? -
hugh.wang:
改天试试
Mac版魔兽争霸3 1.24e下载
发表评论
-
cocos2dx3.x lua binding
2014-10-14 16:47 988ios的lua binding 详见参考:http:// ... -
android 打开 cclog 输出
2014-08-12 14:49 1365找到原因了,不能打印的原因在于我使用了CCLOG的宏而在And ... -
使用Cocos2d-x 3.0rc,一条命令打包Android
2014-06-13 17:56 14661、目录改动 每个版本都会有目录变动,这次的版本也不例外。 ... -
cocos2dx 提高 build_native.sh 编译速度
2014-05-14 15:57 1576进入生成的obj文件夹看看 proj.android\obj\ ... -
cocos2dx学习笔记
2013-11-26 10:02 7501.Constructors are protected ... -
[cocos2d-x]File文件的IO读写处理
2013-11-15 16:16 1540为了保存自定义数据文 ... -
iPhone: libpng error: CgBI: unknown critical chunk
2013-10-30 17:29 1507If you get this error when tryi ... -
cocos2d-x发生undefined reference to `XX'错误
2013-10-22 17:36 1201eclipse cocos2dx项目,出现错误 E:/Ac ... -
cocos2dx3.0 build_native.sh 需要这些环境变量
2013-10-22 15:25 1568#android SDK 位置 export ANDROI ... -
NDK build编译的解析
2013-10-22 14:51 3029从ndk-build命令开始解析:1,ndk-build.sh ... -
mac 不识别 android samsung手机
2013-10-21 16:30 1889使用Mac开发Android时,有可能无法识别手机,可以通过 ... -
cocos2d-x 3.0版本,eclipse编译时,识别不了std::thread
2013-10-21 15:08 1720在mac环境下,使用create-multi-platfor ... -
使用tolua++编译pk创建自定义类
2013-10-17 10:47 924在Lua第三篇中介绍了,如何在cocos2dx中使用Lua创 ...
相关推荐
在移动应用开发领域,Android和React Native的混合开发已经成为一种常见的技术栈选择,它结合了原生应用的优势和Web开发的便利性。本教程通过"Android和React Native混合开发Demo",将详细介绍如何在Android应用中...
### Android Native Exception详解 #### 一、概览 在Android系统中,应用程序主要基于Java运行时环境进行开发,但也有不少部分是...此外,掌握Native Exception的处理流程也是提高应用稳定性和性能的重要手段之一。
在移动应用开发领域,Android以其开源性和丰富的生态系统占据了一席之地。"Android Native Goodies PRO 1.3.0"是一款专为Android开发者设计的工具集,它包含了多种实用功能,旨在提升用户在Android设备上的体验,...
由于Unity和Android环境的不同,可能会出现兼容性问题或者运行时错误。学会使用日志输出、调试工具以及Unity的异常处理机制是必要的。 6. 性能优化:由于涉及到跨进程通信,性能可能成为关注点。合理设计API接口,...
DPF Android Native.Components v2.8.1是这个库的一个重要版本,它包含了一系列关键组件,提升了Android应用的开发体验。 首先,我们关注到"DPF.Android.JSpinner.pas",这代表了JSpinner组件。JSpinner是Android ...
总的来说,掌握有效的内存泄漏调试方法对于提高Android应用的性能和稳定性至关重要。通过利用Node.js和LeakTracer这样的工具,开发者可以在Native层代码的内存管理上达到更高的精度,避免因内存泄漏导致的问题,从而...
在Android开发中,"android native+h5"是一个常见的技术结合,它涉及到原生Android应用与Web(HTML5)内容的交互。本项目“android native+h5 demo”提供了一个实例,展示了如何在Android应用中集成H5页面并实现两者...
在Android平台上,有时候我们需要在原生层(Native)进行TCP通信,这通常涉及到C...理解并掌握这些知识,对于开发高效、安全的Android应用至关重要。在实践过程中,需要不断调试和优化,以适应各种网络环境和性能需求。
Android驱动开发是Android系统开发的重要组成部分,涉及到硬件与软件的交互,对系统的性能、稳定性和效率有着直接影响。本书首先会引导读者理解Android系统的架构,特别是硬件抽象层(HAL)和Linux内核之间的关系,...
我们可以看到“swift-ReactNative开发嘎嘎商城客户端”项目涵盖了移动应用开发的多个核心领域,包括跨平台开发、原生与JavaScript的融合、状态管理、网络通信和UI设计等,这些都是现代移动应用开发中的重要技术栈。
在Android开发中,有时我们需要在原生代码(Native)与HTML中的JavaScript之间进行交互,以实现混合式应用的开发。这种交互方式可以让开发者利用Web技术的便利性与原生平台的强大功能,达到最佳的用户体验。本文将...
标题中的“android_app_NativeActivity.rar_NativeActivity_android”表明我们正在探讨与Android应用开发相关的主题,特别是...理解和掌握NativeActivity的使用,对于进行高性能、低延迟的Android应用开发至关重要。
ReactNative是一种由Facebook开发的开源框架,它允许开发者使用JavaScript和React库来构建原生的Android和iOS应用程序。ReactNative的核心理念是“Learn once, write anywhere”,即学习一次,到处编写,这使得...
4. Native Service与Binder:通过Camera服务的实例,学员将学习如何创建和管理Native Service,并理解Binder作为Android系统间通信的关键角色,以及其在系统架构中的重要地位。 5. App开发:课程不仅覆盖Android...
在Android应用开发中,JavaScript(通常通过HTML5)与原生代码(Native)的交互是构建混合应用程序的关键技术。这种交互使得开发者可以利用Web技术来实现界面,同时利用Android原生功能,提升用户体验和性能。本文将...
总之,《DPF.Android.Native.Components.v2.8.6XE7控件及修正实例》是Delphi开发者在构建Android应用时的重要参考资料,通过深入理解和实践,开发者能够提升Android开发技能,打造出高质量的原生应用。
在这个“React Native开发的食谱应用程序”项目中,我们可以看到如何利用React Native构建一个功能丰富的移动端应用。 1. **React基础**:React Native基于React,这是一个用于构建用户界面的JavaScript库。React...
在Android平台上进行游戏开发是一项富有挑战性和创新性的任务,它涉及到多个技术和工具的融合。"Android游戏开发大全"这个资源可能包含了一份全面的指南,帮助开发者深入理解和掌握Android游戏开发的核心概念和技术...
《Android系统开发与实践》这本书深入探讨了Android操作系统的核心技术和实际应用开发,是广大Android开发者和爱好者提升技术能力的重要参考资料。书中的内容涵盖了从系统架构到应用层开发的多个方面,旨在帮助读者...