AndroidStudio 配置NDK/JNI开发环境
1、新建你的 AS 工程
2、在 AndroidStudio 中配置 NDK 路径
方法如下:
(1)先下载NDK并安装(这句基本是废话);
(2)点菜单栏的 File->ProjectStructure…-> 在打开的窗口中左侧选中 SDKLocation-> 在右侧 Android NDK Location 中填入 NDK 目录所在路径,如下图所示:
(3)首先写好我们的 java 文件
如果有朋友不明白这里怎么写的,可以看我 JNI 栏第一、二篇文章
先创建好我们的 JNITest.java 文件,内容如下:
1 package com.clay.example; 2
3 public class JNITest { 4 public native String getJNIString(); 5 }
(4)使用 javah 命令行生成 jni 目录及对应的头文件:
这里我们使用 Android studio 的命令行终端就行,毕竟打开默认就是工程根目录,这里进行的 javac、javah 参数释义不再描述,前面两篇博文讲的很明白了,不清楚的同学可以去看看前面两篇博客。
经过 javac 编译过后,我们看到了 JNITest.java 生成的 JNITest.class 文件
我们使用 javah 生成 .h 文件
生成的 .h 文件内容如下:com_clay_example_JNITest.h
1 /* DO NOT EDIT THIS FILE - it is machine generated */
2 #include <jni.h>
3 /* Header for class com_clay_example_JNITest */
4
5 #ifndef _Included_com_clay_example_JNITest 6 #define _Included_com_clay_example_JNITest
7 #ifdef __cplusplus 8 extern "C" { 9 #endif
10 /*
11 * Class: com_clay_example_JNITest 12 * Method: getJNIString 13 * Signature: ()Ljava/lang/String; 14 */
15 JNIEXPORT jstring JNICALL Java_com_clay_example_JNITest_getJNIString 16 (JNIEnv *, jobject); 17
18 #ifdef __cplusplus 19 } 20 #endif
21 #endif
创建我们所需的 C/C++ Source File
这里我们选择 .c 文件(C跟C++也没什么区别)
1 //
2 // Created by zhengchuanyu on 20-12-9. 3 // 4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <jni.h>
7
8 JNIEXPORT jstring JNICALL Java_com_clay_example_JNITest_getJNIString 9 (JNIEnv* env, jobject obj) 10 { 11 return (*env)->NewStringUTF(env, "Hello From JNITest Function(getJNIString)"); 12 }
如果是 .cpp 文件
1 //
2 // Created by zhengchuanyu on 20-12-9. 3 // 4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <jni.h>
7
8 JNIEXPORT jstring JNICALL Java_com_clay_example_JNITest_getJNIString 9 (JNIEnv* env, jobject obj) 10 { 11 return env->NewStringUTF(env, "Hello From JNITest Function(getJNIString)"); 12 }
在 jni 目录下创建 Android.mk 文件:
1 LOCAL_PATH := $(call my-dir) 2
3 include $(CLEAR_VARS) 4
5 LOCAL_MODULE := JNITest 6 LOCAL_SRC_FILES := \ 7 JNITest.c \ 8
9 include $(BUILD_SHARED_LIBRARY)
在 jni 目录下创建 Application.mk 文件(事实上这个可以不创建,但是还是讲一下吧)这一步的配置我嫌改 AndroidManifest.xml 跟 build.gradle 麻烦就没添加,如果你有兴趣可以去捣鼓一下,我自己也没有捣鼓明白,后面捣鼓明白了再补充上来。
1 APP_STL := gnustl_static 2 APP_CPPFLAGS := -frtti -fexceptions 3 # APP_ABI:是将要生成哪些 cpu 类型的 so, all 代表全部 4 APP_ABI := all 5 #APP——PLATFORM:生成的 so 的最低 android 版本 6 APP_PLATFORM := android-8
在这里,我们会定义几个变量:
LOCAL_PATH: 其值是 call my-dir,而 my-dir 是个宏函数,会返回 Android.mk 所在的路径,在这里,就是 jni 文件夹。
include $(CLEAR_VARS), 这个命令会清除掉所有 LOCAL 开头的变量,比如 LOCAL_MODULE 之类的,但有一个例外,就是其上面的 LOCAL_PATH 。
LOCAL_MODULE: 要生成的 so 包名,也是 Android 中 Java 代码加载时的名称。
LOCAL_SRC_FILES: 要进行编译的源文件,如在这里,有JNITest.c。
include $(BUILD_SHARED_LIBRARY):表明生成一个动态链接库。
定义后这样一个Android.mk文件之后,在命令行中调用 ndk-build 命令,如下:
ndk-build:command not found 请点击此链接 ndk-build:command not found
命令实行之后,我们可以在项目目录下看到 libs 中多了一个 so 库,如下:
到这里,关于Jni实现的就结束了,接下来就是如何在Android中使用这个本地方法了。
我们创建了一个 Activity,在它里面只放置一个 TextView 控件,它的布局如下:
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 tools:context=".MainActivity">
7
8 <TextView 9 android:id="@+id/text"
10 android:layout_width="match_parent"
11 android:layout_height="wrap_content"
12 android:text="test"
13 android:textSize="20dp"/>
14 </LinearLayout>
然后在 Activity 中,我们要加载这个 so 库,如下:
1 package com.clay.example; 2
3 import androidx.appcompat.app.AppCompatActivity; 4
5 import android.os.Bundle; 6 import android.widget.TextView; 7
8 import org.w3c.dom.Text; 9
10 public class MainActivity extends AppCompatActivity { 11
12 static { 13 System.loadLibrary("JNITest"); 14 } 15
16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_main); 20 TextView textView = (TextView) findViewById(R.id.text); 21 JNITest jniTest = new JNITest(); 22 textView.setText(jniTest.getJNIString()); 23 } 24 }
最后还有一步要做:接下来就是使用so了,首先先与.so关联起来,有两种方法
(1)第一种方法,app/src/main目录下面创建一个jniLibs文件夹,将刚才生成的libs下的所有文件夹拷贝或者剪切到这里面,然后在 build.gradle中加入:
1 android { 2 ...... 3 sourceSets { 4 main() { 5 jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程
6 } 7 } 8 }
(2)第二种方法,直接在 gradle 中加入
1 sourceSets.main { 2 jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程
3 jniLibs.srcDir 'src/main/libs'
4 }
1 android { 2 ...... 3 sourceSets { 4 main() { 5 jniLibs.srcDirs = ['src/main/libs'] 6 jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程 7 } 8 } 9 }
1)利用 static 静态代码块,加载 so 库文件,可以看到在这里,这个名称就是 Anrdoid.mk 中定义的 LOCAL_MODULE 值。
2)创建 JNITest 对象,调用其 getJNIString() 方法,最终显示结果如下:
最后,我们总结一下这几个步骤:
1)创建 Java 类文件,并定义 Native 方法,如 JNITest 类。
2)利用 javac 生成 class 文件,利用 javah 生成 C/C++ 头文件,在这里要注意 (javah -d <path>(要生成的.h文件的目录) -classpath <path>(类所在的根目录) -jni -v(输出详细信息)) 的使用方法
3)编写对应的C文件,如 JNITest.c,在里面实现C/C++的方法,记得要放在 jni 文件夹下面。
4)编写 Android.mk 文件,利用 ndk-build 命令生成 so 文件。
5)在 Android 中利用 static 静态代码块,调用 system.loadLibrary 方法来加载 so 库文件。
6)在 build.gradle 中关联 ndk-build 编译出来的 so 库文件。
7)在 Java 逻辑中调用之前定义的 JNITest 类的方法。
结束。