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 類的方法。
結束。