Android中JNI的使用方法

首先看一下Android平台的框架图:(网上盗用)

    


  可以看到Android上层的ApplicationApplicationFramework都是使用Java编写,

底层包括系统和使用众多的LIiraries都是C/C++编写的。

  所以上层Java要调用底层的C/C++函数库必须通过JavaJNI来实现。

下面将学习Android是如何通过Jni来实现JavaC/C++函数的调用。以HelloWorld程序为例:

第一步:

使用Java编写HelloWorldAndroid应用程序:

package com.lucyfyr; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class HelloWorld extends Activity { /** Called when the activity is first created. */ @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来生成一个App

因为eclipse会自动为我们编译此Java文件,后面要是用到。


第二步:

  生成共享库的头文件:

  进入到eclipse生成的Android Project中 :/HelloWorld/bin/classes/com/lucyfyr/下:

  可以看到里面后很多后缀为.class的文件,就是eclipse为我们自动编译好了的java文件,其中就有:

    HelloWorld.class文件。

  退回到classes一级目录:/HelloWorld/bin/classes/

执行如下命令:

  javah com.lucyfyr.HelloWorld

  生成文件:com_lucyfyr_HelloWorld.h

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h>/* Header for class com_lucyfyr_HelloWorld */ #ifndef _Included_com_lucyfyr_HelloWorld #define _Included_com_lucyfyr_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: com_lucyfyr_HelloWorld * Method: printJNI * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif

  可以看到自动生成对应的函数:Java_com_lucyfyr_HelloWorld_printJNI

    Java_ +包名(com.lucyfyr+类名(HelloWorld) +接口名(printJNI):必须要按此JNI规范来操作;

  java虚拟机就可以在com.simon.HelloWorld类调用printJNI接口的时候自动找到这个C实现的Native函数调用。

  当然函数名太长,可以在.c文件中通过函数名映射表来实现简化。


第三步:

实现JNI原生函数源文件:

新建com_lucyfyr_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_lucyfyr_HelloWorld_printJNI(JNIEnv *env, jobject obj,jstring inputStr) {   LOGI("dufresne Hello World From libhelloworld.so!");   // 从 instring 字符串取得指向字符串 UTF 编码的指针  const char *str =   (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE );   LOGI("dufresne--->%s",(const char *)str);   // 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。  (*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str );   return (*env)->NewStringUTF(env, "Hello World! I am Native interface"); }
/* This function will be call when the library first be load. * You can do some init in the libray. return which version jni it support. */ 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; }

  OnLoadJava_com_lucyfyr_HelloWorld_printJNI

函数里面做一些log输出 注意JNI中的log输出的不同。

  JNI_OnLoad函数JNI规范定义的,当共享库第一次被加载的时候会被回调,

这个函数里面可以进行一些初始化工作,比如注册函数映射表,缓存一些变量等,

最后返回当前环境所支持的JNI环境。本例只是简单的返回当前JNI环境。

第四步:

编译生成so

  编译com_lucyfyr_HelloWorld.cso库可以和app一起编译,也可以都单独编译。

在当前目录下建立jni文件夹:HelloWorld/jni/

  下建立Android.mk,并将com_lucyfyr_HelloWorld.ccom_lucyfyr_HelloWorld.h拷贝到进去

编写编译生成so库的Android.mk文件:

LOCAL_PATH:= $(call my-dir)# 一个完整模块编译include $(CLEAR_VARS)LOCAL_SRC_FILES:=com_lucyfyr_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
目录,目录….)目录引入操作符
    
如该目录下有个文件夹名称src,则可以这样写$(call src),那么就会得到src目录的完整路径

  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


编译此模块:输入编译命令

  ./makeMtk mm packages/apps/HelloWorld/jni/

  上面是我的工程根目录编译命令。具体编译方式根据自己系统要求执行。

编译输出:libHelloWorldJni.so (system/lib中视具体而定)

  此时库文件编译好了可以使用,如果在eclipse中模拟器上使用,需要将libHelloWorldJni.so导入到system/lib下,

  或者对应appdata/data/com.lucyfyr/lib/下;

但是在导入system/lib下的时候,却始终提示out of memory。于是网上说使用emulator去搞,

  但是此命令使用起来却有问题,始终没有解决。但是可以导入到data/data/com.lucyfyr/lib/下使用。但是我这里直接在真机上弄的。

看一下HelloWorldAndroid.mk文件的配置

  其中存在:

    include $(LOCAL_PATH)/jni/Android.mk表示编译库文件

    LOCAL_JNI_SHARED_LIBRARIES := libHelloWorldJni表示app依赖库,打包的时候会一起打包。


第五步:

验证执行

  将编译好的apk安装到手机上

  使用adb push到手机上去需要自己去导入库文件libHelloWorldJni.sodata/data/com.lucyfyr/lib/

  使用adb install方式安装则会自动导入。

启动HelloWorld:输入命令adb logcat |grep dufresne

输出log如下:

  I/HelloWorld(28500): dufresne Hello World From libhelloworld.so!

  I/HelloWorld(28500): dufresne--->I am HelloWorld Activity

  V/dufresne(28500): Hello World! I am Native interface

符合调用打印顺序正确。

以上通过一个简单的例子学习了Android如何编写编译C库文件,以及如何使用库文件。

对于JNI的使用其中还涉及到其他方面的一些知识:C++接口如何调用,函数名注册表怎么回事,

参数类型如何匹配,JNI如何调用Java中的方法java<--->JNI等。


原文地址:http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html

更多相关文章

  1. NPM 和webpack 的基础使用
  2. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
  3. 【Android(安卓)Training - 04】保存数据 [ Lesson 2 - 保存文件
  4. Android内核开发:理解和掌握repo工具
  5. Android开发实践:Android交叉编译工具链的使用
  6. Android的init过程详解(一)
  7. Android(安卓)mainfest文件 android属性 汇总
  8. Android逆向之旅---Android中的sharedUserId属性详解
  9. Android(安卓)项目在Eclipse中的目录结构

随机推荐

  1. Android 文件布局一些细节备忘
  2. android 虚拟按键遮挡布局
  3. Android学习 2 -建立模拟器
  4. 7.Android(安卓)自定义Loader
  5. Android(安卓)升级到android studio 2.2
  6. Android Support v4、v7、v13的区别和应
  7. Android4.4支持使用DevTools对原生Androi
  8. Android MediaStore数据之增删查改剖析
  9. 让Android中的webview支持页面中的文件上
  10. Android之——多线程下载示例