Android中JNI的使用方法

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

    

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

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

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

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

第一步:

使用Java编写HelloWorld 的Android应用程序:

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.c成so库可以和app一起编译,也可以都单独编译。

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

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

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

LOCAL_PATH:= $(call my-dir)
# 一个完整模块编译
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=com_lucyfyr_HelloWorld.c
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_MODULE := libHelloWorldJni
LOCAL_SHARED_LIBRARIES := libutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_TAGS :=optional
include $(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 下,

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

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

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

看一下HelloWorld中Android.mk文件的配置

  其中存在:

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

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

第五步:

验证执行

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

  使用adb push到手机上去需要自己去导入库文件libHelloWorldJni.so到data/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等。

from:http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html

【转】Android中JNI的使用方法的更多相关文章

  1. Android中JNI的使用方法(转载)

    Android中JNI的使用方法 首先看一下Android平台的框架图:(网上盗用) 可以看到Android上层的Application和ApplicationFramework都是使用Java编写, ...

  2. Android中JNI的使用方法

    可以看到Android上层的Application和ApplicationFramework都是使用Java编写,底层包括系统和使用众多的LIiraries都是C/C++编写的. 所以上层Java要调 ...

  3. Android中JNI编程的那些事儿(1)

    转:Android中JNI编程的那些事儿(1)http://mobile.51cto.com/android-267538.htm Android系统不允许一个纯粹使用C/C++的程序出现,它要求必须 ...

  4. android中的提示信息显示方法(toast应用)

    android中的提示信息显示方法(toast应用) (2011-10-17 11:02:06) 转载▼ 标签: android toast 杂谈 分类: Android android中toast的 ...

  5. Android多线程编程<一>Android中启动子线程的方法

          我们知道在Android中,要更新UI只能在UI主线程去更新,而不允许在子线程直接去操作UI,但是很多时候,很多耗时的工作都交给子线程去实现,当子线程执行完这些耗时的工作后,我们希望去修改 ...

  6. Android 中JNI创建实例

    参考文档: http://blog.sina.com.cn/s/blog_a11f64590101924l.html http://www.cnblogs.com/hoys/archive/2010/ ...

  7. Android中JNI编程详解

    前几天在参加腾讯模拟考的时候,腾讯出了一道关于JNI的题,具体如下: JNI本身是一个非常复杂的知识,但是其实对于腾讯的这道题而言,如果你懂JNI,那么你可能会觉得这道题非常简单,就相当于C语言中的h ...

  8. Android中锁定文件的方法

    androidSDK中并没有锁定文件相关的api. 但是android是基于linux操作系统的,linux比较底层,灵活性也更大,为了实现锁定文件的效果,大概有以下几种办法: 用chmod命令修改文 ...

  9. Android中的布局优化方法

    http://blog.csdn.net/rwecho/article/details/8951009 Android开发中的布局很重要吗?那是当然.一切的显示样式都是由这个布局决定的,你说能不重要吗 ...

随机推荐

  1. mac下U盘装机系统的制作(命令行)

    1,不插入U盘和插入U盘分别命令检测硬盘,确定要制作的U盘号:diskutil list 2,卸载usb盘,不推出,diskutil umountDisk /dev/disk1 3,将dmg写入U盘, ...

  2. IOS 多线程编程之Grand Central Dispatch(GCD)介绍和使用 多线程基础和练习

    介绍:前面内容源自网络 Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式 ...

  3. 【转】互联网全站HTTPS的时代已经到来

    原文地址:http://blog.csdn.net/luocn99/article/details/39777707 前言 我目前正在从事HTTPS方面的性能优化工作.在HTTPS项目的开展过程中明显 ...

  4. MINIX3 系统任务分析

    MINIX3 系统任务分析  7.1 MINIX3 系统任务概要  MINIX3 怎么来给用户提供丰富的服务呢?除了中断,异常处理,除了时钟服务. 程序员总是希望一个操作系统给他提供足够的服务,使得他 ...

  5. Linux--/tmp目录文件重启后自动删除

    源博客 http://blog.itpub.net/24996904/viewspace-769327/ 在/etc/default/目录下有个rcS文件,文件内容如下:## /etc/default ...

  6. 新款F系列虚拟机

    我们宣布,10款全新的优化版虚拟机今天正式面市.这款名为"F系列"的全新虚拟机,基于因特尔2.4 千兆赫Xeon® E5-2673 v3(Haswell)处理器:采用因特尔睿频加速 ...

  7. js中this和回调方法循环-我们到底能走多远系列(35)

    我们到底能走多远系列(35) 扯淡: 13年最后一个月了,你们在13年初的计划实现了吗?还来得及吗? 请加油~ 主题: 最近一直在写js,遇到了几个问题,可能初入门的时候都会遇到吧,总结下. 例子: ...

  8. Delphi第一个入门程序——鼠标点击计数 - imsoft.cnblogs

    实现的效果如下: 制作要点: 添加一个按钮Button1和一个标签Label1,并双击按钮进入编程界面在var  Form1: TForm1;下面一行加上  n:integer;//定义变量. 然后在 ...

  9. Wamp安装使用+Git for Windows

    相信很多朋友都曾在windows上做过web开发,我们常用的Web应用程序平台是:Apache+Mysql+Perl/PHP/Python,在windows下集成称为WAMP.web开发新手有时候由于 ...

  10. jquery商城购物车右侧悬浮加入购物车动画效果

    <script type="text/javascript" src="js/jquery-1.7.min.js"></script> ...