Android中JNI的使用方法
Android通过Jni实现Java对C/C++函数的调用示例说明:
A.第一步
使用Java编写Android应用程序,以HelloWorld为例:
<pre name="code" class="objc">package com.lucy;import android.app.Activity;import android.os.Bundle;import android.util.Log;public class HelloWorld extends Activity { @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.v("dufresne", printJNI("I am HelloWorld Activity")); } static{ //加载库文件 System.loadLibrary("HelloWorldJni"); } //声明原生函数 参数为String类型 返回类型为String private native String printJNI(String inputStr);}编写完成之后,用eclipse编译Java应用程序。
B.第二步
生成共享库的头文件:
进入到eclipse对应的AndroidProject中 :/HelloWorld/bin/classes/com/lucy/下:
可以看到里面有很多后缀为.class的文件,就是eclipse为我们自动编译好了的java文件,其中就有:HelloWorld.class文件。
退回到classes一级目录:/HelloWorld/bin/classes/执行如下命令:
javah com.lucy.HelloWorld
生成文件:com_lucy_HelloWorld.h
#include <jni.h>#ifndef _Included_com_lucy_HelloWorld#define _Included_com_lucy_HelloWorld#ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_com_lucy_HelloWorld_printJNI(JNIEnv *, jobject, jstring);#ifdef __cplusplus}#endif#endif
可以看到自动生成对应的函数:Java_com_lucy_HelloWorld_printJNI
Java_ + 包名(com.lucy) + 类名(HelloWorld) + 接口名(printJNI):必须要按此JNI规范来操作。
当然函数名太长,可以在.c文件中通过函数名映射表来实现简化。
C.第三步
实现JNI原生函数源文件:
新建com_lucy_HelloWorld.c文件:
#include <jni.h>#define LOG_TAG "HelloWorld"#include <utils/Log.h>/* Native interface, it will be call in java code */JNIEXPORT jstring JNICALL Java_com_lucy_HelloWorld_printJNI(JNIEnv *env, jobject obj,jstring inputStr){ LOGI("dufresne Hello World From libhelloworld.so!"); const char *str = (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE ); LOGI("dufresne--->%s",(const char *)str); (*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str ); return (*env)->NewStringUTF(env, "Hello World! I am Native interface");}jint JNI_OnLoad(JavaVM* vm, void* reserved){ void *venv; LOGI("dufresne----->JNI_OnLoad!"); if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) { LOGE("dufresne--->ERROR: GetEnv failed"); return -1; } return JNI_VERSION_1_4;}OnLoad Java_com_lucy_HelloWorld_printJNI
函数里面做一些log输出,注意JNI中的log输出的不同。
JNI_OnLoad函数JNI规范定义的,当共享库第一次被加载的时候会被回调,
这个函数里面可以进行一些初始化工作,比如注册函数映射表,缓存一些变量等,
最后返回当前环境所支持的JNI环境。本例只是简单的返回当前JNI环境。
D.第四步
编译生成so库
编译com_lucy_HelloWorld.c生成so库可以和app一起编译,也可以都单独编译。
在当前目录下建立jni文件夹:HelloWorld/jni/下建立Android.mk ,并将com_lucy_HelloWorld.c和 com_lucy_HelloWorld.h 拷贝到进去
编写编译生成so库的Android.mk文件:
LOCAL_PATH:= $(call my-dir)# 一个完整模块编译include $(CLEAR_VARS)LOCAL_SRC_FILES:=com_lucy_HelloWorld.cLOCAL_C_INCLUDES := $(JNI_H_INCLUDE)LOCAL_MODULE := libHelloWorldJniLOCAL_SHARED_LIBRARIES := libutilsLOCAL_PRELINK_MODULE := falseLOCAL_MODULE_TAGS :=optionalinclude $(BUILD_SHARED_LIBRARY)
系统变量解析:
LOCAL_PATH - 编译时的目录
$(call目录,目录….)目录引入操作符
include$(CLEAR_VARS) -清除之前的一些系统变量
LOCAL_MODULE-编译生成的目标对象
LOCAL_SRC_FILES-编译的源文件
LOCAL_C_INCLUDES-需要包含的头文件目录
LOCAL_SHARED_LIBRARIES-链接时需要的外部库
LOCAL_PRELINK_MODULE-是否需要prelink处理
include$(BUILD_SHARED_LIBRARY)-指明要编译成动态库
android.mk编译模块添加具体方法参考:http://blog.csdn.net/yili_xie/article/details/4906865
编译此模块:输入编译命令
./make Mtkmm packages/apps/HelloWorld/jni/
上面是我的工程根目录编译命令。具体编译方式根据自己系统要求执行。
编译输出:libHelloWorldJni.so (system/lib中视具体而定)
此时库文件编译好了可以使用,如果在eclipse中模拟器上使用,需要将libHelloWorldJni.so导入到system/lib下,
或者对应app的data/data/com.lucy/lib/下;
直接在真机上弄,看一下HelloWorld中Android.mk文件的配置:
其中存在:
include$(LOCAL_PATH)/jni/Android.mk 表示编译库文件
LOCAL_JNI_SHARED_LIBRARIES:= libHelloWorldJni 表示app依赖库,打包的时候会一起打包。
E.第五步
验证执行
将编译好的apk安装到手机上
使用adbpush到手机上去需要自己去导入库文件libHelloWorldJni.so到data/data/com.lucy/lib/
使用adbinstall方式安装则会自动导入。
更多相关文章
- C语言函数以及函数的使用
- Android 文件夹命名规范 国际化资源
- android读取文件权限
- Android文件存储
- Android 播放资源文件视频
- android 日志文件输出
- gradle 配置文件 build.gradle 属性详解
- android文件缓存,并SD卡创建目录未能解决和bitmap内存溢出解决