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环境

Android NDK开发(一)——ndk-build编译生成so库文件并使用_第1张图片

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

Android NDK开发(一)——ndk-build编译生成so库文件并使用_第2张图片

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库文件

Android NDK开发(一)——ndk-build编译生成so库文件并使用_第3张图片

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

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

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

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

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

Android NDK开发(一)——ndk-build编译生成so库文件并使用_第4张图片

在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());    }}

效果如下:

Android NDK开发(一)——ndk-build编译生成so库文件并使用_第5张图片

 

更多相关文章

  1. Kivy A to Z -- Kivycatalog例子无法在Android平台上运行及异常
  2. Android 学习笔记4---签名Android的执行文件
  3. 使用Android Studio调试Android Framework代码
  4. [置顶] 史上最全selector和shape使用方法 Android ListView 列表
  5. Android 获取内外置存储卡方法

随机推荐

  1. android 音频系统java部分代码阅读
  2. 实现在一个界面里多个TextView的跑马灯效
  3. 访问Android硬件资源の管理网络和Wifi连
  4. Android实现侧拉DrawerLayout简单用法
  5. Android提高第十九篇之"多方向"抽屉
  6. 植物大战僵尸2 Android破解版,免费买道具
  7. 打开电话Android系统调用
  8. Android电源管理
  9. [置顶] Android如何使用Https
  10. Android(安卓)属性总结