JNI简介

JNI(Java Native Interface),是方便Java调用C/C++等Native代码封装的一层接口。

NDK简介

NDK(Native Development Kit),是Android提供的一套工具集合,通过NDK可以在Android中更加方便的通过JNI开访问本地代码。NDK提供了交叉编译,开发人员只需要简单的修改mk文件就可以生成特定CPU平台的动态库。使用NDK有如下好处:

1、提高代码安全性。由于apk的java层代码很容易被反编译,而so库反编译比较困难

2、可以很方便的使用目前已有的C/C++开源库

3、便于平台间的移植。通过C/C++实现的动态库可以很方便的在其他平台上使用

4、提高程序在某些特定情形下的执行效率,但是并不能明显提升Android程序的性能

NDK使用步骤

  • 第1步:配置Android NDK环境
  • 第2步:创建Android项目,并与NDK进行关联
  • 第3步:在Android项目中声明所需要调用的Native方法
  • 第4步:使用Android需要交互的本地代码实现在Android中声明的Native方法
  • 第5步:通过ndk-build命令编译产生.so库文件
  • 第6步:编译Android Studio工程,从而实现Android调动本地代码

1、配置NDK环境

2、关联Android Studio 项目与NDK

在local.properties文件中添加配置

ndk.dir=D\:\\Android\\sdk\\ndk-bundle

3、在Android项目中声明Native方法

public class JNIUtils {    //加载生成的so库文件    //注意要跟.so库文件名相同    static {        System.loadLibrary("native-lib");    }    //java调C中的方法都需要用native声明且方法名必须和c的方法名一样    public native static String stringFromJNI();}

4、实现在Android中声明的Native方法

重新Make Project一下工程,完成后会在工程目录app/build/intermediates/classes/debug/com/hsdi/jnidemo下看到编译后的classes文件JNIUtils.class文件

a、使用javah工具生成头文件

我们的NDK模块源代码由C/C++的头/源文件和make文件组成,这些文件必须放在jni目录下

理论上,jni目录可以放在任何地方,例如我们放在xx/jni/下,在xx/jni/下执行ndk-build之后会在xx/下(即jni同级目录下)产生编译结果(即libs和obj文件)在Android Studio项目里,我们一般把jni目录放在项目根目录下(即src同级目录),这样我们编译出来的libs和obj将会在项目根目录下。

打开终端,输入以下命令:

javah -d ./app/jni -classpath ./app/build/intermediates/classes/debug com.hsdi.jnidemo.JNIUtils

b、创建本地代码文件

在jni目录下创建com_hsdi_jnidemo_JNIUtils.cpp文件,内容如下:

#include "com_hsdi_jnidemo_JNIUtils.h"JNIEXPORT jstring JNICALL Java_com_hsdi_jnidemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject){        // 1. JNIEnv:代表了VM里面的环境,本地的代码可以通过该参数与Java代码进行操作        // 2. jobject:定义JNI方法的类的一个本地引用(this)        return env->NewStringUTF("Hello from C++");}

注:

  • JNIEXPORT jstring JNICALL中的 JNIEXPORTJNICALL不能省略,其中jstring表示该方法返回字符串
  • 方法名的格式:Java_包名_类名_Java需要调用的方法名,对于包名,包名中的 . 要改成 _ _ 要改成 _1

c、在Module的build.gradle添加ndk节点

android{    ...    defaultConfig{        ...        ndk{            //模块名称,即编译的.so文件名            moduleName "native-lib"                       //"log"表示加入Android的调试日志,只要再导入#include             //就可以使用__android_log_print方法打印日志到logcat中            ldLibs "log"        }        ...    }    ...}

 

5、创建Android.mk文件和Application.mk文件

Android.mk文件是一个负责向NDK构建系统描述NDK项目的GNU Makefile片段,是每一个NDK项目的必备组件,根据GNU Make的命名规则,变量名要大写

# Android.mk必须以LOCAL_PATH开头,注释#除外# 设置工作目录,而my-dir则会返回Android.mk文件所在的目录LOCAL_PATH := $(call my-dir)# 借助CLEAR_VARS变量清除除LOCAL_PATH外的所有LOCAL_变量include $(CLEAR_VARS)# 设置模块的名称,即编译出来.so文件名# 注,要和上述步骤中build.gradle中NDK节点设置的名字相同LOCAL_MODULE := native-lib# 指定参与模块编译的C/C++源文件列表,多文件用"\"隔开LOCAL_SRC_FILES := com_hsdi_jnidemo_JNIUtils.cpp# 必须在文件结尾定义编译类型,指定生成的静态库或者共享库在运行时依赖的共享库模块列表。# BUILD_SHARED_LIBRARY 共享库,供java或者其他共享库调用# BUILD_STATIC_LIBRARY 静态库,供共享库调用,不能直接被java调用include $(BUILD_SHARED_LIBRARY)

Application.mk文件配置编译平台相关的内容

# 最常用的APP_ABI字段:指定需要基于哪些CPU平台的.so文件# 常见的平台有armeabi x86 mips,其中移动设备主要是armeabi平台# 默认情况下,Android平台会生成所有平台的.so文件,即同APP_ABI := armeabi x86 mips# 指定CPU平台类型后,就只会生成该平台的.so文件,即上述语句只会生成armeabi平台的.so文件# APP_ABI := armeabi armeabi-v7a mips x86APP_ABI := allAPP_PLATFORM := android-23

5、编译上述文件,生成.so库文件

经过上述步骤,在app/jni文件夹中已经有3个文件(即test.cpp、Android.mk、Application.mk

打开终端,输入以下命令

$ cd ./app/jni$ ndk-build

可能会编译失败,提示如下:

D:/Android/sdk/ndk-bundle/build//../build/core/add-application.mk:178: *** Android NDK: APP_STL gnustl_static is no longer supported. Please switch to either c++_static or c++_shared. See https://developer.android.com/ndk/guides/cpp-support.html for more information.    .  Stop.
出现的原因可能是由于ndk版本过高导致的,使用低版本的ndk再编译一次:

$ cd ./app/jni$ D:\Android\android-ndk-r14b-windows-x86_64\android-ndk-r14b\ndk-build

编译成功后,会在jni的同级目录中生成libsobj两个文件夹,其中libs下存放的是.so库文件

6、在Android Studio项目中使用NDK实现JNI功能

此时,我们已经将本地代码文件编译成.so库文件,有两种方法可以供Java代码调用本地代码

方法一:把这些编译好的.so库打包到apk里面供java调用,我们还必须在Module的build.gradle里面添加下面的代码:

android {    ...    sourceSets {        main {            jniLibs.srcDirs = ['libs']        }    }    ...}

方法二:在src/main/中创建一个名为jniLibs的文件夹,将上述生成的so文件夹放到该目录下,

在Java代码中调用本地代码中的方法,具体代码如下:

 

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        TextView tv = findViewById(R.id.tv);        tv.setText(JNIUtils.stringFromJNI());    }}

效果如下:

 

更多相关文章

  1. Android(安卓)资源,国际化,自适应
  2. 在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程
  3. 【Android】windows下使用android studio和ndk-build编译c程序生
  4. Gradle in Android(安卓)Studio (1) - 构建系统概述
  5. 浅谈J2me游戏如何快速移植到Android
  6. Android(安卓)Studio 配置模拟器AVD存放路径(默认在c盘,解决c盘空
  7. Android调用系统关机与重启功能
  8. Android编译环境
  9. NPM 和webpack 的基础使用

随机推荐

  1. Android自定义Dialog、Dialog样式、Dialo
  2. 在s5pv210开发板的android平台上添加busy
  3. Android面试(9): Activity的四种启动模式
  4. Android(安卓)Studio发布到Jcenter
  5. Android:长按连续响应的Button
  6. Android(安卓)SDK环境配置
  7. Android数据存储方式之:ContentProvider
  8. Day2.3--Android常用布局之RelativeLayou
  9. 自定义Logcat,完全控制打印信息
  10. JNI 引用问题梳理