Android平台上的JNI开发之Hello JNI

先有个概念

JNI,洋名全称是Java Native Interface,翻译过来就是Java本地接口。我们都知道所谓接口就是一套规范,可以用来定义接入方法。那么JNI定义了什么接入方法呢?答案是:它定义了一套Java接入C/C++的方法。

我们知道C/C++语言编译后的文件是可以直接在本地系统中运行的,而Java文件编译后生成的是字节码文件,需要依赖Java虚拟机(JVM)来运行,显然在效率上C/C++更高效;另一方面,我们知道字节码文件是可以很容易反编译的,存在不安全性;再者,硬件相关的驱动、许多知名的音视频解码库也是C/C++编写的。Java程序想要执行高性能的代码、想要高安全性或者需要调用系统驱动和重用已有的音视频解码库,那么就不得不使用JNI了。

看下我当初简陋的笔记:

JNI笔记-概念

原理

大致的过程是这样的:

  1. 我们把C/C++源程序编译打包成动态库(dll或so,具体看运行的环境),存放在我们程序的lib目录下;
  2. 在java程序中我们调用带native修饰的代理方法,它的实现在C/C++程序中,系统会调用动态库中相应的实现方法,如果需要的话,还会返回处理结果。
JNI笔记-原理

JNI开发环境搭建

听你这么一说,原理似乎懂了些了,那现在是不是该开工了??别急,“工欲善其事,必先利其器”,让我们先搭建起开发环境。

1. 下载NDK

What ? NDK是什么鬼?别急嘛,这就说。NDK洋名全称是Native Development Kit,就是Google给咱们开发者提供的一套方便地进行JNI开发的工具集,可以到官网下载,也可以用我准备好的资源(里面还包含了适用于eclipse开发环境的NDK插件,这个在ADT-Bundle中是集成的)。

2. 解压NDK并查看参考文档

将下载后的压缩包解压到任意分区,注意文件路径不能包含中文和空白字符(比如空格、tab),否则会导致NDK不可用。注:可以通过查看参考文档和导入示例代码到工程学习。

NDK目录

3. 安装NDK插件和绑定NDK路径

对Eclipse IDE:放到eclipse安装目录下的plugins目录就对了,然后依次点击Window-->Preference-->Android-->NDK 进行NDK路径的绑定

绑定步骤1
绑定步骤2
JNI笔记-搭建开发环境

Hello JNI

如果上面的步骤顺利,下面我们就要正式跟JNI打个招呼了。Hello JNI项目源码:下载

1. 新建一个普通的Android项目

简单,略。

2. 添加本地动态库支持

右击Android项目-->Android Tools-->Add Native Support...,然后输入动态库的名称(系统会自动加上前缀"lib"和后缀".so",以jni为例)

添加本地动态库支持1
添加本地动态库支持2

完成后会发现,工程会转到C/C++视图,并且项目中多了一些文件/文件夹

多了的文件

3. 创建两个对应的文件

一个是Java源文件,代理动态库中方法;另一个是C/C++源文件,是对代理方法的实现。
最佳实践是:先创建Java代理文件,静态导入动态库并编写好代理方法,然后利用javah命令生成C语言的方法签名;复制方法签名到C/C++源文件中,补充参数名和方法体就OK了。

3.1 创建Java源文件--Jni.java
package com.example.hello_jni;
public class Jni {
    //静态加载我们即将生成的so库
    static{
        System.loadLibrary("jni");
    }
    //代理函数,具体实现在C
    public native String getMsgFromC();
}
3.2 生成Jni.java的标头文件

复制工程的src目录的位置,当前为:D:\Android\jni\Hello-JNI\src
CMD进入

C:\Users\Lshare>cd /d D:\Android\jni\Hello-JNI\src
D:\Android\jni\Hello-JNI\src>

复制Jni.java的全路径名,当前为:com.example.hello_jni.Jni
javah生成标头文件

D:\Android\jni\Hello-JNI\src>javah com.example.hello_jni.Jni

刷一下工程,可以看到

标头文件
3.3 复制标头文件内容到jni.cpp,并删除标头文件(com_example_hello_jni_Jni.h)
jni-未绑定.png
3.4 绑定头文件路径
绑定头文件1
绑定头文件2
绑定头文件3
绑定头文件4
绑定头文件5

4.完成函数体

#include <jni.h>
/**
 * env:包含了jni.h提供了许多便捷的函数,查看头文件源码发现
 * #if defined(__cplusplus)
 * typedef _JNIEnv JNIEnv;
 * typedef _JavaVM JavaVM;
 * #else
 * typedef const struct JNINativeInterface* JNIEnv;
 * typedef const struct JNIInvokeInterface* JavaVM;
 * thiz:包含对应的代理函数的java类的对象
 */
#ifndef _Included_com_example_hello_jni_Jni
#define _Included_com_example_hello_jni_Jni
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_example_hello_1jni_Jni_getMsgFromC
  (JNIEnv * env, jobject thiz){
    //当为cpp文件时用“env->”,为c文件时用“(*env)->”
    // 该函数用于将“C-String”转换为jstring
    return env->NewStringUTF("Hello JNI!");
}
#ifdef __cplusplus
}
#endif
#endif

5. 完善逻辑实现

//activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:onClick="sayHello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="C快快请安" />
</RelativeLayout>
//MainActivity.java
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void sayHello(View view){
        String msg = new Jni().getMsgFromC();
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

6. 修改Android.mk和Application.mk适应需求

Android.mk的任务:描述源文件该如何编译

#包含Android.mk自身的路径
LOCAL_PATH := $(call my-dir)
#清除LOCAL_XXX变量,除了LOCAL_PATH
include $(CLEAR_VARS)
#要生成的动态库名称,系统会自动加上“lib”前缀和“.so”后缀
LOCAL_MODULE    := jni
#要编译的C/C++文件
LOCAL_SRC_FILES := jni.cpp
#指向构建脚本,搜集定义的LOCAL_XXX变量信息,生成lib$(LOCAL_MODULE).so
include $(BUILD_SHARED_LIBRARY)

Application.mk的任务:描述哪一个动态库是项目需要的
默认不生成这个文件,我们需要自己在/jni目录下新建一个。一般情况下,我们使用两个变量就够了

#定义要生成的CPU架构的so文件,all代表所有
APP_ABI := all
#目标安卓版本,18表示api level,即Android 4.3
APP_PLATFORM := android-8

7. run一下看看

运行结果

Java果真通过JNI技术调用了C代码,而C真的返回问候给Java了,nice!!!


最后不妨看看我当初的笔记(仅供参考,估计只有我自己看得懂,捂脸走。。。)

JNI笔记- Hello JNI
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容