零基礎帶你吃掉JNI全家桶(二)

##前言 上篇經過一個簡單的例子大概闡述了jni開發的基本流程,最後也編譯出了本身的so文件,本篇主要介紹怎麼引入第三方的so文件並進行調用java

零基礎帶你吃掉JNI全家桶(一)bash

零基礎帶你吃掉JNI全家桶(三)架構

1、如何調用第三方so中的方法?

經過上篇咱們知道,從Java層要調用native層的方法,要麼是靜態註冊,要麼是動態註冊,可是無論是哪種,兩個方法之間必須須要創建必定的通道關係,靜態註冊須要對應好方法名,動態註冊須要對兩個方法進行綁定,都是須要知道包名的,可是問題來了,那麼如何在個人項目中調用第三方so中的方法呢?本身的項目包名都是不同的,實際上有兩種方式:app

  • 若是第三方提供了so文件,同時也提供了SDK jar包文件,那實際上本身自己就不須要作太多的操做,直接調用API中的方法,sdk內部再去跟native方法進行映射,咱們只要將so庫文件導入進來放在指定位置,通常是在jniLibs目錄下,這樣sdk裏面就能夠跟native層通訊了。
  • 上面那種方式通常適用於集成第三方服務,好比高德、友盟等等,侷限性較大,只能在sdk限制下進行操做,假如是須要對其進行必定的擴展性或者沒有人給你提供SDK包(好比本身公司的一些內部庫常常會有這種狀況),那麼這個時候就須要本身經過須要把so導進來以後,編寫本地方法來進行映射通訊

2、關聯第三方so庫

上面第一種方法就不說了,通常直接調用sdk便可,咱們直接看第二種,大概分爲如下幾個步驟:ide

  • 導入第三方so文件,放在指定目錄下,通常就放在jniLibs目錄下面
  • 編寫CMakeLists.txt文件,引入so庫並進行關聯
  • 編寫native方法,包名必定要和so中對應的包名同樣

咱們就使用上篇例子生成出來的so,咱們來調用下,從build中把so拷出來,位置以下,這裏咱們用arm架構就好了,一個是64位的,一個是32位的 post

image.png

而後放到咱們新建的一個項目中,這裏把so名字重命名下,防止混淆 gradle

image.png

而後咱們須要在build.gradle中指定so文件目錄ui

sourceSets {
        main {
            jni.srcDirs = []
            jniLibs.srcDirs = ['src\\main\\jniLibs']
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
複製代碼

而後,由於自己本身是不須要生成so庫的,因此CMakeLists.txt中的native-lib能夠刪掉,咱們加入要引入的so庫,並與之關聯this

#定義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文件加載進來。spa

最後一步,編寫本地方法,新建一個類

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層的類?等等。

有新的想法和疑問的老哥能夠留言一塊兒討論哦,溜了溜了~

相關文章
相關標籤/搜索