在Android编程过程中,有时会出于程序效率或代码复用的考虑,使用到JNI编程,即用Java调用C/C++的内容,本文将纪录一个简单的JNI调用实例,用以备忘。
1.新建一Android工程,命名HelloJNI,目录如下图:
建立JNI调用的类Hello.java,如下
- package com.hellojni;
- public class Hello {
- static {
- try{
- System.loadLibrary("hellojni");
- }catch(UnsatisfiedLinkError e){
- System.err.println( "Cannot load hello library:\n " +
- e.toString() );
- }
- }
- public Hello(){
- }
- public native void SayHello(String strName);
- }
主要用上上述代码中的native本地方法调用。
2.Java和C的链接主要是使用命名空间的一致性,为了保证命名不会出现错误,我们采取直接编译java文件的形式直接生成C的头文件,方法如下:
1)将Hello.java编译成Hello.class
- simmatoMacBook-Pro:~ me$ javac Documents/workspace/HelloJNI/src/com/hellojni/Hello.java
- simmatoMacBook-Pro:~ me$ javah -classpath . -jni com.hellojni.Hello
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- #include"common.h"
- /* Header for class com_hellojni_Hello */
- #ifndef _Included_com_hellojni_Hello
- #define _Included_com_hellojni_Hello
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_hellojni_Hello
- * Method: SayHello
- * Signature: (Ljava/lang/String;)V
- */
- JNIEXPORT void JNICALL Java_com_hellojni_Hello_SayHello
- (JNIEnv *, jobject, jstring);
- #ifdef __cplusplus
- }
- #endif
- #endif
3.实现JNI部分
1)在android工程中新建jni目录,将com_hellojni_Hello.h拷入其中,并实现,如下实现com_hellojni_Hello.cpp
- #include "com_hellojni_Hello.h"
- #include <stdlib.h>
- JNIEXPORT void JNICALL Java_com_hellojni_Hello_SayHello
- (JNIEnv * env, jobject obj, jstring msg){
- LOGI("JNI - hello:\n");
- if (msg == NULL) {
- //该方法为打印的方法
- LOGI( "Your params is null");
- }
- char* rtn = NULL;
- jclass clsstring = env->FindClass("java/lang/String");
- jstring strencode = env->NewStringUTF("utf-8");
- jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
- jbyteArray barr = (jbyteArray)env->CallObjectMethod(msg, mid, strencode);
- jsize alen = env->GetArrayLength(barr);
- jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
- if (alen > 0)
- {
- rtn = (char*)malloc(alen + 1);
- memcpy(rtn, ba, alen);
- rtn[alen] = 0;
- }
- env->ReleaseByteArrayElements(barr, ba, 0);
- //该方法为打印的方法
- LOGI("Get Param: %s From Java", rtn);
- }
- #ifndef COMMON_HEADER
- #define COMMON_HEADER
- //enable re-enter code
- #define _REENTRANT
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netinet/tcp.h>
- #include <fcntl.h>
- #include <time.h>
- #include <fstream>
- #include <pthread.h>
- #include <string.h>
- #include <memory>
- #include <string>
- #include <errno.h>
- #include <semaphore.h>
- // for android debug
- #include <android/log.h>
- #define debug
- #ifdef debug
- #define LOG_TAG "hello_jni"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
- #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, 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__)
- #else
- #define LOG_TAG
- #define LOGI(...)
- #define LOGD(...)
- #define LOGW(...)
- #define LOGE(...)
- #endif //debug
- typedef void (*Notify)(int);
- using namespace std;
- #define SINGLETON(CLASS) \
- friend class auto_ptr<CLASS>; \
- public: \
- static CLASS* GetInstance(){ \
- if (_mInstance.get() == NULL){ \
- _mInstance.reset(new CLASS()); \
- } \
- return _mInstance.get(); \
- } \
- private: \
- CLASS(); \
- static auto_ptr<CLASS> _mInstance; \
- public: \
- virtual ~CLASS();
- #define INITINSTANCE(CLASS) \
- auto_ptr<CLASS> CLASS::_mInstance(NULL);
- #endif //COMMON_HEADER
2)编写Android.mk和Application.mk
Android.mk是一个makefile,用来告诉NDK需要编译哪些文件,生成哪些模块。我们创建<jni>/Android.mk文件:
- # =============================
- # lib hello
- # =============================
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := hellojni
- LOCAL_C_INCLUDES := $(LOCAL_PATH)/
- LOCAL_SRC_FILES := \
- com_hellojni_Hello.cpp \
- #LOCAL_CFLAGS += -DENABLE_TRACE
- #LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
- LOCAL_LDLIBS := -llog -lm -ldl
- include $(BUILD_SHARED_LIBRARY)
其中LOCAL_PATH表示c源代码文件的位置;LOCAL_MODULE表示生成的共享库的名称;LOCAL_SRC_FILES代表c代码的文件。不需要把头文件列在里面;头文件的依赖关系是ndk自动计算的。
Application.mk的作用是告诉Android SDK需要哪些库文件。有了它,NDK就可以把库放在正确的位置。我们创建<project>/Application.mk:
- APP_STL := stlport_static
- STLPORT_FORCE_REBUILD := true
- APP_MODULES := hellojni
4.编译hellojni.so
使用eclipse工具runConfigurations,如下:
其中,Location选ndk-build, Working Directory选为本android工程目录下的jni目录,然后“Apply”-》“Run”。
即可在本工程的Libs/armeabi下生成libhellojni.so,取出使用即可。