转自:http://www.android123.com.cn/androidkaifa/678.html
一:
平时我们开发Android应用时可能部分要求由于性能问题需要使用NDK来实现,比如OpenGL、OpenCore这样的多媒体应用,使用本地C/C++语言可以处理一些性能敏感或复杂的算法,Android123就我们日常NDK开发中遇到的问题以及技巧逐一总结方便网友查阅。
1. Android NDK编译的应用可以运行在Android 1.5或更高的操作系统上,在Android平台中本地调用主要通过JNI方式处理,JNI调用开销不小如果很简单的操作没有必要画蛇添足。
2. 一般NDK生成的so文件如何打包到apk中去呢?一般工程目录下创建一个libs文件夹,当然了手动释放也可以比如说 data/data/cn.com.android123.cwj/files/rmvb-decoder.so
3. 在JNI下面,我们常用的命名方式:
static JavaVM *g_VM; //全局环境指针
static jclass jNativesClass;
(*env)->GetJavaVM(env, &g_VM); //获取env环境指针,这样以后用g_VM就可以了。
4. jobjectArray jargv // java数组
int getArrayLen(JNIEnv * env, jobjectArray jarray): //获取一个Java数组长度,返回为jsize类型
jstring jele = (jstring)(*env)->GetObjectArrayElement(env, jargv, n); //从将Java数组元素n转为本地的jstring
const char *word = (*env)->GetStringUTFChars(env, jele, 0); //将java的utf字符转为c下面的char*
5. 记住释放内存,这不是java
(*env)->ReleaseStringUTFChars(env, jele, word);
6. 有关Java类命名方式
(Ljava/lang/String;I)V 其中本句
I = Integer
B = Byte
S = Short
C = Char
LXXX; = L加上类名
7. JNI本身没有什么技术含量,Android123提醒大家,会C语言的网友只需要知道Java和C/C++中如何通过JNI相互传输数据即可。
8. JNI和Java如何传递大型数据呢,其实变通的方法可以通过共享一个文件方式处理一些大型数据。
有关Android NDK和JNI的相关内容,我们在下次告诉大家各种数据类型的相互传输和通讯,如果你有什么疑问可以来函至 Android开发网
二:
Android NDK相关的开发技巧我们将在今后经常总结,方便网友参考,有关JNI的使用可以查看 Android JNI开发入门篇 、Android JNI开发提高篇、Android JNI开发进阶篇 、Android JNI开发高级篇 和 Android JNI开发终极篇。
Android NDK对于我们的作用和基本常识在 Android NDK开发技巧一 中已经讲明了,今天谈论下实战的技巧吧
1. 在JNI中打印Logcat,首先我们需要在cpp文件中加入 #include <android/log.h> 这个头文件,NDK有关android自己的就给我们这个唯一的文件log.h,其他的需要我们自己hack diy来解决。
jstring jlog; //从Java传来需要打印的字符
jboolean isCopy;
const char * szLog = (*env)->GetStringUTFChars(env, jlog, &isCopy); //将java的unicode字符转化为utf8字符
__android_log_print(ANDROID_LOG_WARN, “android123-cwj”, "from ndk = %s", szLog); //打印logcat
(*env)->ReleaseStringUTFChars(env, jlog, szLog); // 释放内存
}
上面这段比较简单,其中使用__android_log_print函数打印Logcat,第一个参数为log的level,在log.h头文件中定义了 ANDROID_LOG_UNKNOWN = 0、 ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
ANDROID_LOG_VERBOSE,
ANDROID_LOG_DEBUG,
ANDROID_LOG_INFO,
ANDROID_LOG_WARN,
ANDROID_LOG_ERROR,
ANDROID_LOG_FATAL,
ANDROID_LOG_SILENT
等类型,第二个参数为tag标签,第三个为需要打印的字符。整个例子比较简单,但方便了很多调试。
2. Android NDK给我们提供了zlib库的支持,可以通过本地的方法解压缩zip文件。
3. 有关C语言运行库的一些方法,在string.h文件中描述的比较清楚,可以方便的操作字符串 ,比如
extern void* memccpy(void *, const void *, int, size_t);
extern void* memchr(const void *, int, size_t);
extern void* memrchr(const void *, int, size_t);
extern int memcmp(const void *, const void *, size_t);
extern void* memcpy(void *, const void *, size_t);
extern void* memmove(void *, const void *, size_t);
extern void* memset(void *, int, size_t);
extern void* memmem(const void *, size_t, const void *, size_t);
extern void memswap(void *, void *, size_t);
extern char* strchr(const char *, int);
extern char* strrchr(const char *, int);
extern size_t strlen(const char *);
extern int strcmp(const char *, const char *);
extern char* strcpy(char *, const char *);
extern char* strcat(char *, const char *);
extern int strcasecmp(const char *, const char *);
extern int strncasecmp(const char *, const char *, size_t);
extern char* strdup(const char *);
extern char* strstr(const char *, const char *);
extern char* strcasestr(const char *haystack, const char *needle);
extern char* strtok(char *, const char *);
extern char* strtok_r(char *, const char *, char**);
extern char* strerror(int);
extern int strerror_r(int errnum, char *buf, size_t n);
extern size_t strnlen(const char *, size_t);
extern char* strncat(char *, const char *, size_t);
extern char* strndup(const char *, size_t);
extern int strncmp(const char *, const char *, size_t);
extern char* strncpy(char *, const char *, size_t);
相信这些肯定比Java效率快上不少,至少有指针用,在处理字符串等方面效率可能是几百倍几千倍的提升。
4. NDK在I/O处理上会更有效率,比如提供了Socket和File的本地读写,在socket.h文件中包含了标准Socket的各种方法,可以处理TCP和UDP报文,这样和C++服务器的互通,通过NDK解决,不用再为Java的类型字节对齐以及编码而烦恼。
__socketcall int socket(int, int, int);
__socketcall int bind(int, const struct sockaddr *, int);
__socketcall int connect(int, const struct sockaddr *, socklen_t);
__socketcall int listen(int, int);
__socketcall int accept(int, struct sockaddr *, socklen_t *);
__socketcall int getsockname(int, struct sockaddr *, socklen_t *);
__socketcall int getpeername(int, struct sockaddr *, socklen_t *);
__socketcall int socketpair(int, int, int, int *);
__socketcall int shutdown(int, int);
__socketcall int setsockopt(int, int, int, const void *, socklen_t);
__socketcall int getsockopt(int, int, int, void *, socklen_t *);
__socketcall int sendmsg(int, const struct msghdr *, unsigned int);
__socketcall int recvmsg(int, struct msghdr *, unsigned int);
extern ssize_t send(int, const void *, size_t, unsigned int);
extern ssize_t recv(int, void *, size_t, unsigned int);
__socketcall ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
__socketcall ssize_t recvfrom(int, void *, size_t, unsigned int, const struct sockaddr *, socklen_t *);
5. 当然了,对于我们开发最爽的还要属OpenGL ES了,在NDK中所有GL的函数,可以在gl.h和glext.h中查找到,最新版本NDK支持最新的OpenGL ES版本,可以方便移植iPhone上的3D游戏了。Android123已经成功将Cube例子用NDK改造运行,确实比Java来的更方便和亲切。
最后还是一句话,对于Java这种解释型语言不爽的Android开发者NDK才是你最明智的选择,如果你有C/C++的基础,可以解决很多开源项目的移植工作,实现高级的功能,将Java程序员甩在脑后面,开发出色的UI,完全可以拍拖View的舒服,让OpenGL来写常规应用相信更有竞争力。