##前言 上篇通過一個簡單的例子大概闡述了jni開發的基本流程,最後也編譯出了自己的so檔案,本篇主要介紹怎麼引入第三方的so檔案並進行呼叫
一、如何呼叫第三方so中的方法?
通過上篇我們知道,從Java層要呼叫native層的方法,要麼是靜態註冊,要麼是動態註冊,但是不管是哪一種,兩個方法之間必須需要建立一定的通道關係,靜態註冊需要對應好方法名,動態註冊需要對兩個方法進行繫結,都是需要知道包名的,但是問題來了,那麼如何在我的專案中呼叫第三方so中的方法呢?自己的專案包名都是不一樣的,實際上有兩種方式:
- 如果第三方提供了so檔案,同時也提供了SDK jar包檔案,那實際上自己本身就不需要做太多的操作,直接呼叫API中的方法,sdk內部再去跟native方法進行對映,我們只要將so庫檔案匯入進來放在指定位置,一般是在jniLibs目錄下,這樣sdk裡面就可以跟native層通訊了。
- 上面那種方式一般適用於整合第三方服務,比如高德、友盟等等,侷限性較大,只能在sdk限制下進行操作,假如是需要對其進行一定的擴充套件性或者沒有人給你提供SDK包(比如自己公司的一些內部庫經常會有這種情況),那麼這個時候就需要自己通過需要把so導進來之後,編寫本地方法來進行對映通訊
二、關聯第三方so庫
上面第一種方法就不說了,一般直接呼叫sdk即可,我們直接看第二種,大概分為以下幾個步驟:
- 匯入第三方so檔案,放在指定目錄下,一般就放在jniLibs目錄下面
- 編寫CMakeLists.txt檔案,引入so庫並進行關聯
- 編寫native方法,包名一定要和so中對應的包名一樣
我們就使用上篇例子生成出來的so,我們來呼叫下,從build中把so拷出來,位置如下,這裡我們用arm架構就行了,一個是64位的,一個是32位的
然後放到我們新建的一個專案中,這裡把so名字重新命名下,防止混淆
然後我們需要在build.gradle中指定so檔案目錄
sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['src\\main\\jniLibs']
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
複製程式碼
然後,因為本身自己是不需要生成so庫的,所以CMakeLists.txt中的native-lib可以刪掉,我們加入要引入的so庫,並與之關聯
#定義cmake支援的最小版本號
cmake_minimum_required(VERSION 3.4.1)
#加入lib2庫 ,定義為匯入形式
add_library(lib2 SHARED IMPORTED)
#關聯lib2庫為我們要匯入進來的libdata.so檔案,ANDROID_ABI會根據自身cpu架構選擇so檔案
set_target_properties( lib2
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libdata.so)
# 從系統裡查詢依賴庫,可新增多個
find_library( # 例如查詢系統中的log庫liblog.so
log-lib
# liblog.so庫指定的名稱即為log,如同上面指定生成的libnative-lib.so庫名稱為native-lib一樣
log )
複製程式碼
這樣CMakeLists檔案在執行的時候就會把libdata.so檔案載入進來。
最後一步,編寫本地方法,新建一個類
package com.example.taolin.jni_project;
public class NativeHelper {
static {
System.loadLibrary("data");
}
public static native String stringFromJNI();
public static native int add(int a,int b);
}
複製程式碼
主要包名,要和so中的一直的,也是上篇文章中的動態註冊程式碼,這裡貼上部分
//動態註冊
jint registerMethod(JNIEnv *env) {
jclass clz = env->FindClass("com/example/taolin/jni_project/NativeHelper");
if (clz == NULL) {
LOGD("con't find class: com/example/taolin/jni_project/NativeHelper");
}
JNINativeMethod jniNativeMethod[] = {{"stringFromJNI", "()Ljava/lang/String;", (void *) backStringToJava},
{"add", "(II)I", (void *) addNum},};
return env->RegisterNatives(clz, jniNativeMethod,
sizeof(jniNativeMethod) / sizeof(jniNativeMethod[0]));
}
複製程式碼
這樣的話,就在呼叫的時候,就可以找到對應的類,將Java層方法和Native層方法進行關聯起來,進行通訊
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
@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(NativeHelper.stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
}
複製程式碼
具體圖我就不截了,也是能成功顯示出字串的,這樣就呼叫了外部so的方法,大功告成!
這裡只是最簡單的,從native層返回一個字串,比較淺顯侷限,下篇將接著介紹下,Jni的語法,以及java層和native間更為複雜的互動過程,物件怎麼傳輸?,native層怎麼操作java層的類?等等。
有新的想法和疑問的老哥可以留言一起討論哦,溜了溜了~