JNI學習筆記之ndk-build手動編譯并集成流程

參考

一天掌握Android JNI本地編程 快速入門
Android開發實踐:常用NDK命令行參數
Secrets of Android.mk

JNI

JNI是啥?

JNI(Java Native Interface):Java本地開發接口,JNI是一個協議,用來溝通Java代碼和外部的本地代碼(c/c++),外部的c/c++代碼也可以調用Java代碼

為什么使用JNI

  • 效率上C/C++是本地語言,比Java更高效
  • 代碼移植,如果之前用C語言開發過模塊,可以復用已經存在的C代碼
  • Java反編譯比C語言更容易,一般加密算法都是用C語言編寫,不容易被反編譯

Java基本數據類型與C語言基本數據類型的對應

Java基本數據類型與C語言基本數據類型的對應

Java引用類型對應

Java引用類型對應

堆內存和棧內存的概念

棧內存

系統自動分配和釋放,保存全局,靜態,局部變量,在棧上分配內存叫今天內存,大小一般是固定的。

堆內存

程序員手動分配(mallc/new)和釋放(free/java不用手動釋放,有GC回收),在堆上分配內存叫動態分配,一般硬件內存有多大內存就有多大。

交叉編譯

交叉編譯的概念

交叉編譯記載一個平臺,編譯出另一個平臺能夠執行的二進制代碼
主流平臺:Windows,Mac os,Linux
主流處理器:X86,arm,mips

交叉編譯的原理

即在一個平臺上,模擬其它平臺的特性
編譯的流程:源代碼》編譯》鏈接》可執行程序

交叉編譯的工具鏈

多個工具的集合,一個工具使用完后接著調用下一個工具

常見的交叉編譯工具

NDK(Native Development Kit):開發JNI必備工具,就是模擬其它平臺特性類編譯代碼的工具
CDP(C/C++ Development Tools):是Eclipse開發C語言的一個插件,高亮顯示C語言的語法
Cygwin:一個Windows平臺的Unix模擬器

NDK的目錄結構

這里我下的ndk版本是15.2.4203891 比較新了,配置NDK環境變量大家應該都會,這里我就不提了


目前NDK最新的目錄

ndk-build方式手動編譯出so庫文件

一個簡單的例子

1.編寫java代碼

這個直接在工程目錄下正常編輯你的代碼,比如我的這個JNIUtils是在com.newtrekwang.ndkpractice包下的,這個類聲明了一個方法,功能就是獲取從C層傳來的字符串。方法的具體實現當然是在C層實現啦。所以這個就跟java的接口定義差不多。

package com.newtrekwang.ndkpractice;

public class JNIUtils {
    public static native String getStringFromC();
}

2.編寫c層代碼

這里如果想快速寫一個與之關聯的頭文件的話,(java7)可以使用javah命令生成,不過命令要在你的類的所在包的根路徑執行
比如我這個:

javah

可以看下頭文件的代碼:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_newtrekwang_ndkpractice_JNIUtils */

#ifndef _Included_com_newtrekwang_ndkpractice_JNIUtils
#define _Included_com_newtrekwang_ndkpractice_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_newtrekwang_ndkpractice_JNIUtils
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_newtrekwang_ndkpractice_JNIUtils_getStringFromC
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

可以看到里面已經幫我們寫好了一個與Java類getStringFromC對應的Native函數Java_com_newtrekwang_ndkpractice_JNIUtils_getStringFromC,命名格式就是Java加完整類名加方法名

然后我把這個頭文件剪切到另一個新建的文件夾prac1里,這個文件夾專門存放c/c++真個編譯過程的產物,比如最后我要得到so文件,就從這里面拿

然后我們新建一個hello.c實現頭文件里面的函數

#include<com_newtrekwang_ndkpractice_JNIUtils.h>
#include<stdio.h>
#include<stdlib.h>

JNIEXPORT jstring JNICALL Java_com_newtrekwang_ndkpractice_JNIUtils_getStringFromC
  (JNIEnv* env, jclass obj){
      char* str="Hello from C!";
      jstring result=(*env)->NewStringUTF(env,str);
      return result;
  }

(1)JNIEXPORT :在Jni編程中所有本地語言實現Jni接口的方法前面都有一個"JNIEXPORT",這個可以看做是Jni的一個標志,至今為止沒發現它有什么特殊的用處。

(2)void :這個學過編程的人都知道,當然是方法的返回值了。

(3)JNICALL :這個可以理解為Jni 和Call兩個部分,和起來的意思就是 Jni調用XXX(后面的XXX就是JAVA的方法名)。

(4)Java_com_test01_Test_firstTest:這個就是被上一步中被調用的部分,也就是Java中的native 方法名,這里起名字的方式比較特別,是:包名+類名+方法名。

(5)JNIEnv * env:這個env可以看做是Jni接口本身的一個對象,在上一篇中提到的jni.h頭文件中存在著大量被封裝好的函數,這些函數也是Jni編程中經常被使用到的,要想調用這些函數就需要使用JNIEnv這個對象。例如:env->GetObjectClass()。(詳情請查看jni.h)

3.編寫Android.mk

Android.mk 里面就是些設置編譯配置的腳本
例如我的這個:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=hello
LOCAL_SRC_FILES := hello.c com_newtrekwang_ndkpractice_JNIUtils.h
include $(BUILD_SHARED_LIBRARY)

常見Android.mk語句解釋

  • LOCAL_PATH := $(call my-dir)
    一個Android.mk文件必須以LOCAL_PATH變量的定義開始,它用于在開發樹中查找源文件。在這個例子中,構建系統提供的宏函數'my-dir'被用來返回 當前目錄的路徑(即包含Android.mk文件本身的目錄)。
  • include $(CLEAR_VARS)
    CLEAR_VARS變量由構建系統提供,并指向一個特殊的GNU Makefile,它將清除許多LOCAL_XXX變量 (例如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等),但除了LOCAL_PATH,這個是需要的,因為所有的構建控制文件在單個GNU Make執行上下文中解析,其中所有變量都是全局的。
  • LOCAL_MODULE :=hello
    定義LOCAL_MODULE變量來標識您在Android.mk中描述的每個模塊,構建系統為自動為模塊添加前綴和后綴,例如我這個是hello,他最終會生成libhello.so
  • LOCAL_SRC_FILES := hello.c com_newtrekwang_ndkpractice_JNIUtils.h
    LOCAL_SRC_FILES變量必須包含C和/或C ++源文件的列表,這些文件將被構建并組裝到一個模塊中。這里可以只列出源文件,不需要頭文件,頭文件構建系統會自動查找

更多Android.mk功能請見Secrets of Android.mk

然后我的prac1文件現在有這個三個文件了

prac1

然后在此目錄下執行ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
如圖:

image.png

然后再看我們的文件夾就是這樣了:

image.png
image.png

常見ndk-build命令行參數

  • NDK_LOG=1:配置log級別,打印ndk編譯時的詳細輸出信息
  • NDK_PROJECT_PATH=.:制定NDK編譯的代碼路徑為當前目錄,如果不配置,則必須把工程代碼放到Android工程的jni目錄下
  • APP_BUILD_SCRIPT=./Android.mk:指定NDK編譯使用的Android.mk文件
  • NDK_APP_APPLICATION_MK=./Application.mk:指定NDK編譯使用的Application.mk文件
  • CLEAN:清除所有編譯出來的臨時文件和目標文件
  • -B:強制重新編譯已經編譯完成的的代碼
  • NDK_DEBUG=1:執行 debug build
  • NDK_DEBUG=0:執行release build
  • NDK_OUT=./mydir:指定編譯生成的文件的存放位置
  • -C /opt/myTest/:到指定目錄編譯native代碼

4.在Android項目中集成so文件

首先把那些so文件拷到Android工程的libs目錄下

拷so文件

gradle配置庫文件目錄

在android塊下設置sourceSets塊即可,比如我這里設置的是libs文件夾,說明我的so庫文件在libs文件夾里。

 sourceSets {
        main {
            jniLibs.srcDirs=["libs"] //指定庫文件的目錄,java代碼編譯時鏈接用
        }
    }

然后gradle同步一下,即可在Android模式目錄下看到jniLibs文件包,證明gradle已經識別了so庫文件

jniLibs

完善java代碼

就是在原來的基礎上加了個靜態塊,里面的System.loadLibrary("hello")功能就是加載hello這個庫文件,不然下面的native方法不能正常執行

package com.newtrekwang.ndkpractice;

public class JNIUtils {
        static {
            System.loadLibrary("hello");
        }
    public static native String getStringFromC();
}

然后就可以愉快的使用調方法了
比如我這里這個TextView顯示的字符串就是就是從JNIUtils類的native方法調出來的。

實例
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容