AndroidStudio 配置NDK/JNI开发环境 AndroidStudio 配置NDK/JNI开发环境

1、新建你的 AS 工程

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

2、在 AndroidStudio 中配置 NDK 路径

  方法如下:

  (1)先下载NDK并安装(这句基本是废话);

  (2)点菜单栏的 File->ProjectStructure…-> 在打开的窗口中左侧选中 SDKLocation-> 在右侧 Android NDK Location 中填入 NDK 目录所在路径,如下图所示:

                             AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境     AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

  (3)首先写好我们的 java 文件

  如果有朋友不明白这里怎么写的,可以看我 JNI 栏第一、二篇文章


  JNI 最简单例程步骤详解

   

                             AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/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 参数释义不再描述,前面两篇博文讲的很明白了,不清楚的同学可以去看看前面两篇博客。

                           AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境              AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

  经过 javac 编译过后,我们看到了 JNITest.java 生成的 JNITest.class 文件

  我们使用 javah 生成 .h 文件

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

   生成的 .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

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

   这里我们选择 .c 文件(C跟C++也没什么区别)

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

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

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

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

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

  命令实行之后,我们可以在项目目录下看到 libs 中多了一个 so 库,如下:

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

  到这里,关于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() 方法,最终显示结果如下:

AndroidStudio 配置NDK/JNI开发环境
AndroidStudio 配置NDK/JNI开发环境

  最后,我们总结一下这几个步骤:

  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 类的方法。

结束。