android JNI编程实例

     在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 
           2) 此时将com.hellojni包copy到bin目录(指android工程的bin目录)下,进入bin目录执行:            

      simmatoMacBook-Pro:~ me$ javah -classpath . -jni com.hellojni.Hello
                此时会在bin下生成com_hellojni_Hello.h,即我们想要的h头文件,如下:

/* 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);

}


               补充一个头文件:common.h

#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
          APP_PROJECT_PATH代表android工程所在目录,在本例中它被放在<project>中; APP_MODULES表示工程需要的库,如果有多个以空格分开。

       4.编译hellojni.so

          使用eclipse工具runConfigurations,如下:


       其中,Location选ndk-build, Working Directory选为本android工程目录下的jni目录,然后“Apply”-》“Run”。

       即可在本工程的Libs/armeabi下生成libhellojni.so,取出使用即可。

              

      
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值