LZ-Says:半夜睡覺滾了地上了徘六,無奈之下醒來了针炉,想想最近幾天因為一個括號導致JNI遲遲不能開展男摧,心里面無奈又崩潰洛搀,索性直接起來整完得了~
前言
當前畢業(yè)的時候敢茁,感覺自己掌握了全世界,隨著參加工作的時間一天天的增長留美,突然覺得丫的彰檬,啥都不會啊,要學的東西還是太多太多了谎砾。不過近來被飛大姐洗腦成功逢倍,<font color=#FF0000>萬事不過幾行代碼而已,干它~
So 今天為大家?guī)砗唵蔚膉ni配置景图,使用较雕,以及運行我們的第一個簡單小demo`
Hi Jni
總是再說jni,jni挚币,那么jni到底是什么東西亮蒋,我們一起來看看:
JNI是Java Native Interface的縮寫,它提供了若干的API實現(xiàn)了Java和其他語言的通信(主要是C&C++)妆毕。從Java1.1開始慎玖,JNI標準成為java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互笛粘。
JNI標準至少要保證本地代碼能工作在任何Java 虛擬機環(huán)境趁怔。
下面為大家附上官方Android平臺架構圖:
<center>可以看到Android上層的Application和ApplicationFramework都是使用Java編寫远舅,底層包括系統(tǒng)和使用眾多的Libraries都是C/C++編寫的,所以上層Java要調用底層的C/C++函數(shù)庫必須通過Java的JNI來實現(xiàn)痕钢。
Jni使用場景
當你開始著手準備一個使用JNI的項目時图柏,請確認是否還有替代方案。應用程序使用JNI會帶來一些副作用任连。下面給出幾個方案蚤吹,可以避免使用JNI的時候,達到與本地代碼進行交互的效果:
1随抠、JAVA程序和本地程序使用TCP/IP或者IPC進行交互裁着。
2、當用JAVA程序連接本地數(shù)據(jù)庫時拱她,使用JDBC提供的API二驰。
3、JAVA程序可以使用分布式對象技術秉沼,如JAVA IDL API桶雀。
這些方案的共同點是,JAVA和C處于不同的線程唬复,或者不同的機器上矗积。這樣,當本地程序崩潰時敞咧,不會影響到JAVA程序棘捣。
下面這些場合中,同一進程內JNI的使用無法避免:
1休建、程序當中用到了JAVA API不提供的特殊系統(tǒng)環(huán)境才會有的特征乍恐。而跨進程操作又不現(xiàn)實。
2测砂、你可能想訪問一些己有的本地庫茵烈,但又不想付出跨進程調用時的代價,如效率邑彪,內存瞧毙,數(shù)據(jù)傳遞方面胧华。
3寄症、JAVA程序當中的一部分代碼對效率要求非常高桐猬,如算法計算臼闻,圖形渲染等。
<font color=#FF0000>總之芬膝,只有當你必須在同一進程中調用本地代碼時悲没,再使用JNI篮迎。
Jni缺陷
一旦使用JNI,JAVA程序就喪失了JAVA平臺的兩個優(yōu)點:
1、程序不再跨平臺甜橱。要想跨平臺逊笆,必須在不同的系統(tǒng)環(huán)境下重新編譯本地語言部分。
2岂傲、程序不再是絕對安全的难裆,本地代碼的不當使用可能導致整個程序崩潰。一個通用規(guī)則是镊掖,你應該讓本地方法集中在少數(shù)幾個類當中乃戈。這樣就降低了JAVA和C之間的耦合性
Jni作用
JNI可以這樣與本地程序進行交互:
1、你可以使用JNI來實現(xiàn)“本地方法”(native methods)亩进,并在JAVA程序中調用它們症虑。
2、JNI支持一個“調用接口”(invocation interface)归薛,它允許你把一個JVM嵌入到本地程序中谍憔。本地程序可以鏈接一個實現(xiàn)了JVM的本地庫,然后使用“調用接口”執(zhí)行JAVA語言編寫的軟件模塊主籍。
簡單了解以上內容后韵卤,我們開啟正題,在Android開發(fā)中崇猫,我們該怎么使用jni沈条,或者說是在Android Studio中,我們該怎么使用jni呢诅炉?表急蜡歹,往下瞅瞅~
話說,我們開發(fā)android應用程序基礎不就是下載官方相關的SDK涕烧,ADT啥啥啥的月而,同理jni也一樣。
現(xiàn)在為大家簡單介紹NDK~如下议纯。
Hi NDK
Android NDK 是一套允許使用原生代碼語言(例如 C 和 C++)實現(xiàn)部分應用的工具集父款。在開發(fā)某些類型應用時,這有助于重復使用以這些語言編寫的代碼庫瞻凤。
同理憨攒,官方也為我們提供了一個小例子,簡單走馬觀花看一下:
public class MyActivity extends Activity {
/**
* 使用 C/C++ 語言實現(xiàn)的原生方法
*/
public native void computeFoo();
}
native方法阀参,當年看到這個東西感覺好高大上肝集,沒想到而今我也能玩玩,哈哈蛛壳,GGG~
官方對于NDK是這樣說的:
NDK 不適用于大多數(shù)初學的 Android 編程者杏瞻,對許多類型的 Android 應用沒什么價值所刀。 因為它不可避免地會增加開發(fā)過程的復雜性,所以通常不值得使用捞挥。 但如果您需要執(zhí)行以下操作浮创,它可能很有用:
從設備獲取卓越性能以用于計算密集型應用,例如游戲或物理模擬砌函;
重復使用您自己或其他開發(fā)者的 C 或 C++ 庫蒸矛。
丫的,爺兒們好奇瞅瞅不行靶刈臁雏掠?
行~!
哈哈~
簡單有個印象后劣像,我們開始配置相關內容乡话,為什么要說這個配置呢,主要有以下幾個原因:
雖說配置是傻瓜式無腦操作耳奕,但是對于LZ小白這樣的人來說绑青,依然覺得是一件很高大上的事兒,何況屋群,丫的闸婴,連配置都不會,還開發(fā)個卵子芍躏?
凡事兒親歷親為邪乍,不經(jīng)歷,怎能有成長对竣?
配置之前庇楞,我們還需要了解我們需要配置or下載哪兒些工具,以及這些東西都是干嘛的否纬,不然稀里糊涂的吕晌,糟心。
同理临燃,我們也需要去簡單了解下使用NDK好處:
1睛驳、代碼的保護。由于apk的java層代碼很容易被反編譯膜廊,而C/C++庫反匯難度較大乏沸;
2、可以方便地使用現(xiàn)存的開源庫溃论。大部分現(xiàn)存的開源庫都是用C/C++代碼編寫的屎蜓;
3痘昌、提高程序的執(zhí)行效率钥勋。將要求高性能的應用邏輯使用C開發(fā)炬转,從而提高應用程序的執(zhí)行效率;
4算灸、便于移植扼劈。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用。
NDK以及所需構建工具簡介
NDK:這個還需要再說嘛菲驴?
CMake:外部構建工具荐吵;
CMake是一個開源的跨平臺系列工具,旨在構建赊瞬,測試和打包軟件先煎。
CMake用于使用簡單的平臺和編譯器獨立的配置文件來控制軟件編譯過程,并生成可以在選擇的編譯環(huán)境中使用的本地makefile和工作空間巧涧。
CMack工具套件由Kitware創(chuàng)建薯蝎,以響應對開源項目(如ITK和VTK)的強大的跨平臺構建環(huán)境的需求。
官方地址:https://cmake.org/ 有興趣可以簡單了解下~
- LLDB:Android Studio 上面調試本地代碼的工具
LLDB是下一代高性能調試器谤绳。它被構建為一組可重用的組件占锯,可以高度利用較大的LLVM項目中的現(xiàn)有庫,例如Clang表達式解析器和LLVM反匯編程序缩筛。
在Mac OS X中消略,LLDB是Xcode中的默認調試器,支持在桌面和iOS設備和模擬器上調試C瞎抛,Objective-C和C ++艺演。
LLDB項目中的所有代碼都可以使用標準的 LLVM許可證(一種開放源代碼“BSD風格”)許可證。
LLDB目前將調試信息轉換成clang類型桐臊,以便它可以利用clang編譯器基礎架構钞艇。這允許LLDB在表達式中支持最新的C,C++豪硅,Objective C和Objective C ++語言特性和運行時間哩照,而無需重新實現(xiàn)任何此功能。在編寫表達式的函數(shù)懒浮,拆卸指令和提取指令詳細信息等時飘弧,還可以利用編譯器來處理所有ABI細節(jié)。
主要優(yōu)點包括:
- C砚著,C ++次伶,Objective C的最新語言支持 ;
- 可以聲明局部變量和類型的多行表達式稽穆;
- 支持時使用JIT表達式冠王;
- 當JIT不能使用時,評估表達式中間表示(IR)
以上簡單了解下就好了舌镶,至于為啥要這么搞柱彻,就是為了方便以后有需要直接翻出來看看~
NDK配置(包含構建工具豪娜,調試工具)
<font color=#FF0000>下載安裝NDK,有倆種方式哟楷,其實都一樣瘤载,只是一個需要手動下載,解壓卖擅,配置目錄鸣奔,一個Android Studio自動完成以上操作。
配置NDK倆種方式
手動下載NDK
NDK下載地址如下:
https://developer.android.google.cn/ndk/downloads/index.html
大家可自行選擇相應版本進行下載惩阶。
下載完成后挎狸,解壓本地目錄,在Android Studio中配置路徑即可断楷,如下圖所示:
<center>強大的Android Studio走起~
1.點擊Project Structure伟叛,選擇 Download Android NDK;
<center>2.耐心等待吧脐嫂,LZ下載比較快统刮,解壓比較慢~
3.下載解壓完成,自動錄入地址账千,省事兒哈~
<center>到此侥蒙,關于NDK下載安裝倆種方式圖解完畢~
我們看一下ndk目錄各個作用,簡單了解下匀奏。
- docs: 幫助文檔
build/tools:linux的批處理文件
platforms:編譯c代碼需要使用的頭文件和類庫
prebuilt:預編譯使用的二進制可執(zhí)行文件
sample:jni的使用例子
source:ndk的源碼
toolchains:工具鏈
ndk-build.cmd:編譯打包c代碼的一個指令鞭衩,需要配置系統(tǒng)環(huán)境變量
配置構建工具以及調試工具
1.如下圖所示,點擊SDK Manager娃善,選擇下載安裝CMake以及LLDB论衍;
<center>簡單看一下版本信息,果斷OK~
<center>沒啥可說的聚磺,等待坯台,不過分分鐘搞定~
<center>到此,基本配置已完成瘫寝,但是如何查驗NDK是否成功安裝了呢蜒蕾?
配置下環(huán)境變量瞅瞅唄~
配置環(huán)境變量
復制NDK地址,按如下圖示進行操作即可焕阿。
cmd直接輸入ndk-build回車
<center>在這里吐槽下LZ之前遇到的坑咪啡。
LZ目錄習慣命名為HLQWorkSofe(Android) or HLQWorkSofe(Java),在之前從來沒有出現(xiàn)過問題暮屡,但是在NDK時候撤摸,卻怎么也不行,最后無奈下只能把()去掉。
問題截圖如下:
<center><font color=#FF0000>搞得LZ都快放棄了准夷,最后一試钥飞,好了,給我郁悶的冕象,大家注意啊~
創(chuàng)建個項目玩一玩
創(chuàng)建項目時代承,記得勾選下面的Include C++ support哦~
導入C++支持庫汁蝶,不然沒法玩哈~
<center>
一路next下之后渐扮,關于最后一步選擇時,在此作特殊說明下掖棉。
<center>C++ Standard:使用下拉列表選擇您希望使用哪種 C++ 標準墓律。選擇 Toolchain Default 會使用默認的 CMake 設置;
Exceptions Support:如果您希望啟用對 C++ 異常處理的支持幔亥,請選中此復選框耻讽。如果啟用此復選框,Android Studio 會將 -fexceptions 標志添加到模塊級 build.gradle 文件的 cppFlags 中帕棉,Gradle 會將其傳遞到 CMake针肥;
Runtime Type Information Support:如果您希望支持 RTTI,請選中此復選框香伴。如果啟用此復選框慰枕,Android Studio 會將 -frtti 標志添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake即纲。
點擊Finish之后具帮,我們稍等片刻~
查看默認生成例子
<center>下面我們一起來探究下官方提供的例子,看看從例子中我們能得知什么對我們有用的信息低斋。
從上圖可看到蜂厅,和以前唯一不同的就是多出了cpp目錄以及External Build Files倆個目錄,那么這倆個都有什么用呢膊畴?一起來看看掘猿。
cpp 目錄存放所有 native code 的地方,包括源碼唇跨,頭文件术奖,預編譯項目等。對于新項目轻绞,Android Studio 創(chuàng)建了一個 C++ 模板文件:native-lib.cpp采记,并且將該文件放到了你的 app 模塊的 src/main/cpp/ 目錄下。這份模板代碼提供了一個簡答的 C++ 函數(shù):stringFromJNI()政勃,該函數(shù)返回一個字符串:”Hello from C++”
External Build Files 目錄是存放 CMake 或 ndk-build 構建腳本的地方唧龄。有點類似于 build.gradle 文件告訴 Gradle 如何編譯你的 APP 一樣,CMake 和 ndk-build 也需要一個腳本來告知如何編譯你的 native library奸远。對于一個新的項目既棺,Android Studio 創(chuàng)建了一個 CMake 腳本:CMakeLists.txt讽挟,并且將其放到了你的 module 的根目錄下
首先還是來看看代碼吧,比較直觀也比較好理解丸冕。
1.查看activity代碼
package cn.hlq.hlqjnipro;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
首先static靜態(tài)塊去加載so庫耽梅,本地編寫native方法,調用方法輸出內容胖烛。
2.查看native-lib.cpp代碼
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring JNICALL
Java_cn_hlq_hlqjnipro_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
簡單可以看到眼姐,首先定義hello變量,之后return佩番。
3.查看CMakeLists.txt
簡單的翻譯了下众旗,如有不正之處歡迎指出`
# 使用Android Studio使用CMake的更多信息,閱讀文檔:https://d.android.com/studio/projects/add-native-code.html
# 設置CMake的最低版本構建本機所需庫
cmake_minimum_required(VERSION 3.4.1)
# 創(chuàng)建和名稱庫,使它是靜態(tài)的或共享,并提供了相對路徑的源代碼
# 可以定義多個圖書館,CMake將為你構建這些內容
# Gradle自動與你的APK包共享庫
add_library( # 設置庫名稱
native-lib
# 集庫作為一個共享庫
SHARED
# 提供了一個相對路徑你的源文件
src/main/cpp/native-lib.cpp )
# 搜索指定預先構建的庫和存儲路徑變量。因為CMake包括系統(tǒng)庫搜索路徑中默認情況下,只需要指定想添加公共NDK庫的名稱趟畏,在CMake驗證庫之前存在完成構建
find_library( # 設置path變量的名稱
log-lib
# 在CMake定位前指定的NDK庫名稱
log )
# 指定庫CMake應該鏈接到目標庫中贡歧,可以鏈接多個庫,比如定義庫赋秀,構建腳本利朵,預先構建的第三方庫或者系統(tǒng)庫
target_link_libraries( # 指定目標庫
native-lib
# 目標庫到日志庫的鏈接 包含在NDK
${log-lib} )
一開始還覺的差不多點呢,結果看到這里越來越蒙圈猎莲,還是先運行一下绍弟,看看結果吧。
按照剛才的簡單分析益眉,這個demo輸出的結果應該為:Hello from C++ 晌柬。一起來驗證一下唄~
<center>嗯,確實郭脂,輸出了年碘,有些似懂非懂的,繼續(xù)研究~
經(jīng)過查詢展鸡,發(fā)現(xiàn)一個之前從未關注的內容屿衅,那就是從編譯到運行示例 APP 的流程到底是怎樣呢,如下:
- Gradle 調用外部構建腳本莹弊,也就是 CMakeLists.txt涤久;
- CMake 會根據(jù)構建腳本的指令去編譯一個 C++ 源文件,也就是 native-lib.cpp忍弛,并將編譯后的產(chǎn)物扔進共享對象庫中响迂,并將其命名為 libnative-lib.so,然后 Gradle 將其打包到 APK 中细疚;
- 在運行期間蔗彤,APP 的 MainActivity 會調用 System.loadLibrary() 方法,加載 native library。而這個庫的原生函數(shù)然遏,stringFromJNI()贫途,就可以為 APP 所用了;
- MainActivity.onCreate() 方法會調用 stringFromJNI()待侵,然后返回 “Hello from C++”丢早,并更新 TextView 的顯示;
注意:Instant Run 并不兼容使用了 native code 的項目秧倾。Android Studio 會自動禁止 Instant Run 功能怨酝。
如果你想驗證一下 Gradle 是否將 native library 打包進了 APK,你可以使用 APK Analyzer:
選擇 Build > Analyze APK中狂。
從 app/build/outputs/apk/ 路徑中選擇 APK凫碌,并點擊 OK扑毡。
如下圖胃榕,在 APK Analyzer 窗口中,選擇 lib/<ABI>/瞄摊,你就可以看見 libnative-lib.so
轉自:http://blog.csdn.net/wl9739/article/details/52607010
仿造demo來一下勋又,首先布局中新增一個TextView,activity中編寫一個native方法换帜,調用native方法并輸出結果楔壤,如下:
TextView tv1 = (TextView) findViewById(R.id.sample_text_1);
tv1.setText(hlqFromJNI());
public native String hlqFromJNI();
在cpp中這么玩下:
#include <jni.h>
#include <string>
extern "C" {
JNIEXPORT jstring JNICALL
Java_cn_hlq_hlqjnipro_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT jstring JNICALL
Java_cn_hlq_hlqjnipro_MainActivity_hlqFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "真惡心啊 Fuck";
return env->NewStringUTF(hello.c_str());
}
}
大家有沒有注意到Java_cn_hlq_hlqjnipro_MainActivity_hlqFromJNI這個名稱,方法名必須為Java_包名全路徑_方法名
運行結果如下:
<center>一般情況下惯驼,Gradle 會將你的本地庫構建成 .so 文件蹲嚣,然后將其打包到你的 APK 中。如果你想 Gradle 構建并打包某個特定的 ABI 祟牲。你可以在你的 module 層級的 build.gradle 文件中使用 ndk.abiFilters 標簽來指定他們:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {...}
// or ndkBuild {...}
}
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a'
}
}
buildTypes {...}
externalNativeBuild {...}
}
提供給別人使用
ReBuild Project之后隙畜,將生成好的so庫提供有需要的人即可,不過要記得調用的時候包名要一致说贝,不然會報一個找不到so的問題议惰。
LZ簡單寫了一個測試demo,如下圖:
<center>運行結果如下:
<center>比較惡心的就是包名這塊乡恕,等以后再看看有沒有好的解決辦法吧言询。
拓展APP構建流程
查看官方文檔中,突然發(fā)現(xiàn)有關構建流程內容傲宜,一起了解下运杭,如下:
構建流程涉及許多將您的項目轉換成 Android 應用軟件包 (APK)的工具和流程。構建流程非常靈活函卒,因此了解它的一些底層工作原理會很有幫助辆憔。
典型 Android 應用模塊的構建流程圖如下:
典型 Android 應用模塊的構建流程通常依循下列步驟:
1.編譯器將您的源代碼轉換成 DEX(Dalvik Executable) 文件(其中包括運行在 Android 設備上的字節(jié)碼),將所有其他內容轉換成已編譯資源;
2.APK 打包器將 DEX 文件和已編譯資源合并成單個 APK躁愿。不過叛本,必須先簽署 APK,才能將應用安裝并部署到 Android 設備上彤钟;
3.APK 打包器使用調試或發(fā)布密鑰庫簽署您的 APK:
a. 如果您構建的是調試版本的應用(即專用于測試和分析的應用)来候,打包器會使用調試密鑰庫簽署您的應用。Android Studio 自動使用調試密鑰庫配置新項目逸雹;
b. 如果您構建的是打算向外發(fā)布的發(fā)布版本應用营搅,打包器會使用發(fā)布密鑰庫簽署您的應用
4.在生成最終 APK 之前,打包器會使用 zipalign 工具對應用進行優(yōu)化梆砸,減少其在設備上運行時的內存占用
構建流程結束時转质,您將獲得可用來進行部署、測試的調試 APK帖世,或者可用來發(fā)布給外部用戶的發(fā)布 APK休蟹。
參考文獻
- http://baike.baidu.com/link?url=QrA-HKewzfPKP5UejCXH8JhE4yQs5MhwG6EBYI8imO9k8zYIlM0h2DYffNSqdK8dG6LZLfT0dK5ocK2NEsOUJq ;
- http://blog.csdn.net/tongseng/article/details/53005123日矫;
- http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html赂弓;
- http://blog.csdn.net/baidu_26352053/article/details/53931045;
- https://developer.android.google.cn/ndk/guides/concepts.html哪轿;
- https://developer.android.google.cn/studio/build/index.html;
- http://blog.csdn.net/wl9739/article/details/52607010
結束語
這篇文章寫的夠揪心盈魁,個人感覺亂糟糟的,實際學習的時候也是亂糟糟的窃诉,幾乎都是靠著各種搜索杨耙,況且還有一些不靠譜的博文誤導或者說是版本有些老。
望觀看者海涵`有問題及時溝通飘痛。