Android Studio下JNI调用与编译so包

  2个月前做一款IM软件的时候,手机录音的声音比较小,所以就找了一些声音的增益库,在网上找了一遍最后选择了webrtc的声音增益模块。webrtc源码是c,所以如果要在android中使用的话,就必须将该模块打包成so库文件然后用java调用这个库。
  这里就不得不说一下JNI是什么了。

  JNI是什么?

  JNI(Java Native Interface)意为JAVA本地调用,它允许Java代码和其他语言写的代码进行交互,简单的说,一种在Java虚拟机控制下执行代码的标准机制。

  NDK是什么?

  Android NDK(Native Development Kit )是一套工具集合,允许你用像C/C++语言那样实现应用程序的一部分。

  为什么要用NDK?

  1、安全性,java是半解释型语言,很容易被反汇编后拿到源代码文件,我们可以在重要的交互功能使用C语言代替。
  2、效率,C语言比起java来说效率要高出很多(该不该使用jni这个需要结合项目环境需求,并不一定使用jni效率就比java高)。

  JNI和NDK的区别?

  从工具上说,NDK其实多了一个把.so和.apk打包的工具,而JNI开发并没有打包,只是把.so文件放到文件系统的特定位置。从编译库说NDK开发C/C++只能能使用NDK自带的有限的头文件,而使用JNI则可以使用文件系统中带的头文件。从编写方式说,它们一样。

  JNI具体的介绍就不多说了,网上很多的,这里主要讲一下编译so库和调用。之前是在eclipse里边编译的so库,相对来说比较起android studio麻烦一些,因为编译过程中又使用到了cygwin。

  下面说说在android studio中编译so库,首先新建一个工程

Android Studio下JNI调用与编译so包_第1张图片

新建一个类,并写一个native方法,用来调用so库中的方法

Android Studio下JNI调用与编译so包_第2张图片

下一步就是清空工程,然后再编译工程

Android Studio下JNI调用与编译so包_第3张图片

直到build目录下出现classes目录

Android Studio下JNI调用与编译so包_第4张图片

接着使用命令生成c的头文件

Android Studio下JNI调用与编译so包_第5张图片

使用命令进入到项目build目录下的debug目录


然后再使用javah -jni 类的包名加类名


如果java环境没错的话就会在debug目录下生产一个头文件

Android Studio下JNI调用与编译so包_第6张图片

生产了头文件然后在src/main目录下新建一个jni的目录用来存放c的代码文件

Android Studio下JNI调用与编译so包_第7张图片

将刚刚生成的c的头文件拷贝到jni目录下,文件的名字可以改

Android Studio下JNI调用与编译so包_第8张图片

之后新建一个.c的文件,这个文件用来实现头文件中的方法,首先来看看生成的头文件的内容

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_caidongdong_contentprovider_util_JniUtil */

#ifndef _Included_com_caidongdong_contentprovider_util_JniUtil
#define _Included_com_caidongdong_contentprovider_util_JniUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_caidongdong_contentprovider_util_JniUtil
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_caidongdong_contentprovider_util_JniUtil_getStringFromC
  (JNIEnv * env, jobject obj);

#ifdef __cplusplus
}
#endif
#endif
这个头文件是不用改的,下面贴上.c文件的内容

#include "mystring.h"
#include 
JNIEXPORT jstring JNICALL Java_com_caidongdong_contentprovider_util_JniUtil_getStringFromC
        (JNIEnv * env, jobject obj){
    return (*env)->NewStringUTF(env,"Hello from JNI !");
}
这个c文件的内容需要自己写的,首先引入头文件,使用#include 引入需要的头文件,这个跟java的import类似,但是有区别。拷贝生成的头文件中的方法到c文件中,注意要把行参补全


Android Studio下JNI调用与编译so包_第9张图片

然后写上该方法的实现,当然这里只是简单的返回一个字符串,多说一句c和c++的方法有些是有区别的,所以写的时候注意选择。

c文件写好之后就可以配置编译环境,编译需要ndk我这里使用的ndk的版本是android-ndk-r11,下载好解压后在Android studio中配置ndk路径


Android Studio下JNI调用与编译so包_第10张图片

选择ndk的路径

Android Studio下JNI调用与编译so包_第11张图片

下一步,启用ndk编译,需要在gradle.properties中配置

android.useDeprecatedNdk=true

Android Studio下JNI调用与编译so包_第12张图片


Android Studio下JNI调用与编译so包_第13张图片

下一步,需要在app的build.gradle中的defaultConfig配置ndk生成的so库名称依据生成多个平台的包

Android Studio下JNI调用与编译so包_第14张图片

下一步,在含有native方法的类中加载so库

public class JniUtil {
    static {
        System.loadLibrary("my_lib");
    }
    public  native String getStringFromC();
}

然后在activity中使用该类并调用其中的方法

        JniUtil jniUtil = new JniUtil();
        content.setText(jniUtil.getStringFromC());

最后的运行效果图

Android Studio下JNI调用与编译so包_第15张图片

在工程目录下的lib并没有生成so库,因为gradle已经自动的帮我们生成好了,工程目录下就有,如果反编译apk的话,就会发现so库是存在于lib中的。

Android Studio下JNI调用与编译so包_第16张图片



你可能感兴趣的:(经验总结)