一.概述,目的
首先,在之前使用Eclipse的时候写过一个如何搭建ndk开发环境的文章,现在都在使用AndroidStudio了所以再写一下。再者准备研究一下热更新技术,其中要用到jni和c相关的知识所以打算重新学习一下这部分的内容。后面也会将我的学习过程记录下来。
本文涉及的点
1.1 AndroidStudio中的ndk开发环境的配置
1.2 简单jni调用,ndk开发的开发流程(so文件生成的流程),这里写了一个“Hello Word”
1.3 so文件的使用和注意事项
1.4 为方便使用so文件,将开发工具打包为jar包,或者以工程依赖的方式使用。
二.AndroidStudio中的ndk环境的配置和so文件的生成
2.1 AndroidStudio中的ndk环境的配置
首先说明一下,在AndroidStudio中配置ndk和使用jni来编译生成so文件相比eclipse中都简单了好多。这也顺应了潮流,不简单怎么体现AndroidStudio的好呢。
2.1.1 ndk的下载
这里推荐直接下载ndk包,然后到AndroidStudio中配置,因为我发现在很多情况下通过AndroidStudio中的sdkManager或者Project Structure中下载的ndk-bundle在配置之后都不能正常使用,这里原因就不深究了,知道对的就按照对的做就好了。下载到自己确定的目录下面,预备下面安装使用
推荐的ndk下载链接
下载界面如图:我根据我的操作系统下载Win10的64位版本。
2.1.2 ndk的安装以及其中要注意的地方
先在AndroidStudio中创建一个项目。
安装步骤如下:
a.在AndroidStudio中打开File –>Project Structure –>Sdk Location打开如下图:
其中D:\as\myndk\android-ndk-r14b就是我解压完成的ndk包路径换成自己的就好了。
b.在工程的local.properties中会增加如下图中ndk.dir=D:\as\myndk\android-ndk-r14b说明你的ndk安装成功了。
c.在工程的gradle.properties中增加android.useDeprecatedNdk=true这一句,如下图。
上面就将ndk的开发环境配置完成了,这里不用去我们手动配置ndk的环境变量,AndroidStudio已经帮我们完成了。
2.2 利用搭建好的ndk环境写一个c程序,生成.so文件,完成一个jni的调用过程。
2.2.1 先创建一个调用本地方法的工具类JniUtil,如下图:
其中getData就是我们要调用的本地方法,如下
public static native String getData();
而如下的代码,就是静态代码块中加载so文件。(jnitest就是下面我们要编写的c文件的名称你可以自己取名字,而系统会在生成so文件的时候生成libjnitest.so的so文件,lib是系统为我们加上的不用管它)
static {
System.loadLibrary("jnitest");
}
在module(app)中的build.gradle中加入下面的代码如图:
其中各行的作用在里面已经注释了。
2.2.2 根据2.2.1中的JniUtil生成“头文件”,如下
2.2.2.1 打开AndroidStudio中的Terminal控制台(为提供方便操作Windows系统中文件),如果没有的话就File—settings—plugins下面找到Termina,勾选上重启就可以了。
2.2.2.2 打开后如下进入到当前工程java目录中,调用如下命令生成头文件。其中javah -jni为命令,后面的com.jni.test.gong.JniUtil就是我们刚刚写的工具类JniUtil的完整路径(更换成你自己的就好了)。
javah -jni com.jni.test.gong.JniUtil
界面如下图,操作完成刷新工程会有com_jni_test_gong_JniUtil.h的头文件生成。它的作用就是为我们下面写c文件做“中间件”。
2.2.3根据头文件生成c文件
2.2.3.1在工程的main目录下面新建jni文件夹,如下图
2.2.3.2 将2.2.2中生成的头文件剪切到jni文件夹中
2.2.3.3创建c文件
这里有两种方法:
一种直接在jni目录下面新建jnitest.c或者jnitest.cpp,前者是c文件后者是c++文件。如下图,注意选择c文件和c++文件,因为里面的语法会有不同哦。
然后在jnitest.c中编写先关代码如下
另一种是简单的快捷方法,在JniUtil中的方法getData方法是红色的,此时光标定位上面按Alt + Enter快捷键,出现如下的对话框,点击第一个即可。会在jni文件夹中自动生成jnitest.c文件(可能里面格式会是凌乱的自己整理一下即可)。
2.2.4 解释一下头文件和c文件内容
首先里面提示的“添加;”的错误不要管他,正常。
首先头文件中
...代码省略...
/*自己理解头文件作用,相当于java中的import,必须要有的(jni.h中定义了一些变量等)*/
/* Header for class com_jni_test_gong_JniUtil */
...代码省略...
extern "C" {
...代码省略...
/*JNIEXPORT jstring JNICALL 是固定格式,后面的Java_com_jni_test_gong_JniUtil_getData也可以看出是调用java中的“完整的方法”*/
JNIEXPORT jstring JNICALL Java_com_jni_test_gong_JniUtil_getData
(JNIEnv *, jclass);
...代码省略...
}
...代码省略...
c文件中的内容
很简单,一个头文件导入,再有就是具体的方法实现了。
#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_jni_test_gong_JniUtil_getData(JNIEnv *env, jclass type){
return (*env)->NewStringUTF(env, "Hello Word From Jni");
}
2.2.5生成so文件
点击Build中的Make Project,在如下的目录中就会生成我们要的so文件。如下图,我们可以将他们拷出来备用。
三. so文件的使用
可以在打包so文件的工程中使用我们的so文件,也可以在其他的工程中使用我们打包好的so文件。但是要注意使用时候的工具类的名称,方法名称,工具类所在的包路径,方法名称要和2.2.1中的完全相同。(所以为了方便,我们实际应用中可以打包成jar包或者作为module依赖的方式,以提高我们so文件的通用性)
3.1使用例子
3.1.1 新建一个工程这里我起名为JniUse,包名为com.jni.use,我们在里面新建一个包,名为com.jni.test.gong,再在里面新建JniUtil类并在里面写getData函数(为了和so文件中的方法名称中的包名,方法名都相同),最方便的方法就是将2.2.1中的JniUtil类拷过来。
3.1.2 将刚刚生成的so文件都拷贝过来放在libs包中
3.1.3 在build.gradle中添加如下代码(注意位置在下面3.1.4截图中),用以标明so文件的存放目录。
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
3.1.4 这里用log进行打印
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String str = JniUtil.getData();
Log.e("JniUtil",str);
}
}
所有配置截图如下
3.1.5运行一下工程log如下:
01-08 10:21:46.379 25920-25920/com.jni.use E/JniUtil: Hello Word From Jni
通过上面的配置我们的Hello word就算是完成了。jni的简单使用比较简单,c/c++的文件编写是重点。
四 为方便so文件的使用,将工具类等打包为jar包或者以库依赖的方式引用
4.1 库依赖的方式
这里我们新建工程,为了熟悉一下上面so的打包流程(其实内容是一样的)
4.1.1 新建工程,在里面新建一个Android Library作为依赖库,在这个module中依照上面的操作制作so文件,我的如图:
4.1.2 将4.1.1中创建的library作为依赖库,然后使用里面写好的工具类,如下面的代码很简单。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String str = JniUtil.getData();
Log.e("JniUtil",str);
}
}
运行一下log如下,说明很成功。
01-08 14:23:24.569 16298-16298/com.jni.use.lib E/JniUtil: Hello Word From Jni
4.2 以jar包的方式
由于我使用的同一个app工程,这里先将上面4.1中的依赖关系去掉。
4.2.1 将4.1中生成的so文件全部复制到app下面的lib目录下。
4.2.2 在build.gradle中添加如下的代码
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
和buildTypes,defaultConfig同级括号中(重要)