一、前言
● NDK
Native Development Kit(NDK)是一系列工具的汇合。它提供了一系列的工具,帮忙开发者疾速开发C/C++的动静库,并能主动将so和java一起打包成apk。
● JNI
Java Native Interface(JNI)规范是java平台的一部分,JNI是Java语言提供的Java和C/C++互相沟通的机制,Java能够通过JNI调用C/C++代码,C/C++的代码也能够调用java代码。
● JNI与NDK的关系
NDK能够为咱们生成了C/C++的动态链接库,JNI是java和C/C++沟通的接口,两者与android没有半毛钱关系,只因为安卓是java程序语言开发,而后通过JNI又能与C/C++沟通,所以咱们能够应用NDK+JNI来实现“Java+C”的开发方式。
● 为什么要NDK开发
NDK开发具备以下长处:
我的项目须要调用底层的一些C/C++的一些货色(java无奈间接拜访到操作系统底层(如零碎硬件等)),或者曾经在C/C++环境下实现了性能代码(大部分现存的开源库都是用C/C++代码编写的。),间接应用即可。NDK开发罕用于驱动开发、无线热点共享、数学运算、实时渲染的游戏、音视频解决、文件压缩、人脸识别、图片解决等。
为了效率更加高效些。将要求高性能的应用逻辑应用C/C++开发,从而进步应用程序的执行效率。然而C/C++代码尽管是高效的,在java与C/C++互相调用时却增大了开销;
基于安全性的思考。避免代码被反编译,为了平安起见,应用C/C++语言来编写重要的局部以增大零碎的安全性,最初生成so库(用过第三方库的应该都不生疏)便于给人提供方便。(任何无效的代码混同对于会smail语法反编译你apk是分分钟的事,即便你加壳也不能幸免高手的攻打)
便于移植。用C/C++写得库能够不便在其余的嵌入式平台上再次应用。
二、装置与配置
首先咱们在Android Studio下新建一个安卓我的项目。而后关上Project Structure界面,如下:
在SDK Location目录下,有SDK和NDK的门路,而这里咱们临时还未下载配置过NDK,故咱们须要点击Download Android NDK来进行下载(Android Studio还是很弱小的,相比Eclipse能省不少事)。这里Android Studio会下载最新版本的NDK进行装置,默认会下载保留在SDK的门路下。咱们在上图中还能看到有一段介绍文字,说SDK以及NDK的门路配置会保留在local.properties文件内,装置实现后咱们刷新Project,进local.properties文件查看也能看到SDK与NDK的门路。
NDK下载配置实现之后,须要在gradle.properties文件中加上一行:
android.useDeprecatedNdk=true
1
接下来,咱们借助弱小的Android Studio的插件性能,在External Tools下配置两个十分有用的插件。进入Settings–>Tools–>ExternalTools,点击+号减少。
javah -jni命令,是依据java文件生成.h头文件的,会主动依据java文件中的类名(蕴含包名)与办法名生成对应的C/C++外面的办法名。上面是参数配置及其含意:
Program: JDKPath\bin\javah.exe 这里配置的是JDK目录下的javah.exe的门路。
Parametes: -classpath . -jni -d ModuleFileDir/src/main/jni FileClass 这里FileClass指的是要执行操作的类名(即咱们操作的文件),ModuleFileDir/src/main/jni示意生成的文件保留在这个module目录的src/main/jni目录下。
Working: ModuleFileDir\src\main\java module目录下的src\main\java目录(不是很了解)。
应用形式:选中java文件—>右键—>External Tools—>javah-jni,将生成jni文件夹以及文件夹下的 包名.类名的.h头文件 (名字过长,咱们能够本人重命名)。
ndk -build命令,是依据C/C++文件生成so文件的。上面是参数配置及其含意:
Program: F:\apk\sdk\ndk-bundle\ndk-build.cmd 这里配置的是ndk下的ndk-build.cmd的门路(依据理论状况填写)。
Working: ModuleFileDir\src\main
应用形式:选中C/C++文件—>右键—>ExternalTools—>ndk-build,将在main文件夹下生成libs文件夹以及多个so文件,咱们能够挪动至jniLibs目录上来。
三、简略实例
接下来咱们创立一个拜访本地C/C++办法的java类。
public class JniTest {
/** * 将用C++代码实现,在android代码中调用的办法:获取以后app的包名 * @param o * @return */public static native String getPackname(Object o);/** * 加载so库或jni库,在应用到该库之前加载就行,不肯定非要写在这个类内 * 零碎本人会判断扩展名是dll还是so,这里加载libJNI_ANDROID_TEST.so */static { System.loadLibrary("JNI_ANDROID_TEST");}
}
留神JNI_ANDROID_TEST这个Library名字,之后还会须要用到,要保持一致。该类提供了一个static的native办法,该办法将用来获取app的包名。而后对该文件执行javah -jni操作,生成对应的.h头文件。
如图,曾经依据咱们的java类生成了对应的.h文件,文件名为包名类名.h,咱们能够手动改名为jnitest.h,外面只有一个办法,返回值为String(jstring),办法名为Java类的包名类名办法名(包名中的分级不是用.而是_),后面两个参数是C++外面必须有的(JNIEnv代表指向JVM的指针,jclass是调用该办法的java对象),第三个就是咱们java类的办法外面的参数Object。留神,这是java函数与C++函数对应的动态注册办法,即通过特定的规定来写,此处办法名能够随便起名字,而后还能够用动静注册的形式关联两个办法(显然,动态注册要简略一些)。
而后咱们新建一个C++文件,取名为jnitest.cpp,写上须要include的文件,从.h文件中复制办法过去(办法名、参数类型、返回值等必须统一!血与泪的教训)。
至此,.h文件和c++文件均已实现,接下来还须要在这个jni目录下减少两个文件,Android.mk和Application.mk。
Android.mk,留神LOCAL_MODULE的值与之前的名字绝对应,LOCAL_SRC_FILES的值写c++文件的名字,这两个值成对设置,可设置多组。(:=是赋值的意思,$是援用某变量的值。)
外面的符号正确的应该是:=,代码中已更正,图片外面的更换麻烦就没改了。很奇怪,我当初写的时候编译运行如同是没出错是失常的…(Tips.20170519)
LOCAL_PATH := $(call my-dir) // 设置以后的编译目录(Android.mk所在的目录) include $(CLEAR_VARS) // 革除LOCAL_XX变量(LOCAL_PATH除外)LOCAL_MODULE := JNI_ANDROID_TEST // 指定以后编译模块的名称 LOCAL_SRC_FILES := jnitest.cpp // 编译模块须要的源文件include $(BUILD_SHARED_LIBRARY) // 指定编译出的库类型,BUILD_SHARED_LIBRARY:动静库;BUILD_STATIC_LIBRARY:动态库, BUILD_EXECUTEABLE指:可执行文件在一个Android.mk文件中配置多个Module的形式如下(include$(CLEAR_VARS)、include $(BUILD_SHARED_LIBRARY)两个语句也须要加上):LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := JNI_STATIC_ANDROID_TESTLOCAL_SRC_FILES := jnistaticutils.cppinclude $(BUILD_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := JNI_DYNAMIC_ANDROID_TESTLOCAL_SRC_FILES := jnidynamicutils.cppinclude $(BUILD_SHARED_LIBRARY)
Application.mk,APP_ABI有四种类型(默认armeabi),armeabi、armeabi-v7a、x86、mips,设置时以空格隔开,all示意所有。该文件中有个可选配置的APP_MODULES,相似于下面Android.mk文件中的LOCAL_MODULE,以空格隔开,且会笼罩掉Android.mk文件中的LOCAL_MODULE设置(比方Android.mk文件中的写了两个jni库的配置,LOCAL_MODULE := JNI1、LOCAL_MODULE := JNI2,而Application.mk中设置的APP_MODULES := JNI1,则只能生成JNI1的so文件,要生成JNI2的so文件的时候会报错,除非写成APP_MODULES := JNI1 JNI2,这里咱们间接省略默认应用Android.mk中的)。
APP_ABI := all
1
接下来咱们须要对C++文件执行ndk-build操作,生成相应的so文件。
如图,在main/libs目录下生成了多个so文件,名字为lib+咱们指定的库名(同时还生成了obj文件夹,不知是什么货色)。
这时候咱们能够在main目录下新建jniLibs文件夹,把生成的libs文件夹内的货色均复制过来,删除新生成的jni、libs、obj三个文件夹。而后在Activity中测试调用,在TextView上显示咱们通过C++代码实现的办法getPackname获取app的包名了。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.tv_app_package_name); tv.setText("packageName: " + JniTest.getPackname(MainActivity.this)); }}
测试能正确失去包名,阐明调用胜利了。咱们能够把JniTest类以及so文件给他人去应用,这样他人是看不到咱们的代码实现的,能很好的爱护咱们的源码。
B站学习链接:https://www.bilibili.com/vide...
欢送大家一起交流学习,能够给博主点个关注哦,及时看到音讯。