android studio jni 呼叫 第三方 非標準 so庫

2020-08-08 20:24:21

1、簡介

當我們呼叫別人的非標準的 jni so 庫,無法使用 jni 標準介面的時候。我們需要採用間接的方式呼叫。
(1)比如現在我們有第三方給的 libnative.so 庫,無法自己直接 jni 呼叫
(2)我們自己用 c/c++ 建立一個標準的so 庫,比如 mynative.so
(3) 然後用我們自己的mynative.so 庫中去呼叫第三方的libnative.so庫
(4) 然後 我們在上層 呼叫我們自己的mynativeso 庫 ,就實現了對第三方 libnativeso 庫 方法的呼叫。

這種方式 我們要先有第三方 的 so 庫 和編譯庫 對應的標頭檔案

2、先編譯出一個第三方非標準so庫

1)android studio 新建立一個專案

我們先製作一個非標準 jni 庫,只要功能實現兩個 數相加 ,並返回

2) main 新建立 jni 資料夾

在这里插入图片描述

3) 新建 .cpp 檔案和 .h 實現 求和功能

test.cpp

//
// Created by lum on 20-8-8.
//

#include "test.h"
Add::Add(){}
Add::~Add(){}


int Add::add(int x, int y){
    return x + y;
}

test.h

#ifndef _TEST_JNI_ADD_H_
#define _TEST_JNI_ADD_H_

class Add{

public :
    Add();
    ~Add();
    int add(int x, int y);
};
#endif

在这里插入图片描述

4)、編寫 CMakeLists.txt 用來編譯 cpp 檔案到so

在 app 檔案下 新建 CMakeLists.txt 檔案

在这里插入图片描述

#指定cmake最小版本
cmake_minimum_required(VERSION 3.4.1)

#設定生成的so動態庫最後輸出的路徑
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

#生成so
add_library( # 設定生成庫的名字
             nativeso
             # 生成動態庫
             SHARED
             # 指定原始碼檔案,這裏指定test.cpp檔案
             src/main/jni/test.cpp )

#依賴的標頭檔案
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni)
find_library( # log庫的別名
              log-lib
             #log庫
              log )

#鏈接程式碼到指定的庫
target_link_libraries( # Specifies the target library.
                       nativeso
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
5)設定編譯環境

(1)設定 sdk tool 裡 CMake NDK
在这里插入图片描述

(2)設定 build.gradle(:app)在这里插入图片描述

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"

    defaultConfig {
        applicationId "com.example.myjni"
        minSdkVersion 26
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        // 新增
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
        //新增
        ndk {
            abiFilters 'arm64-v8a'
        }


    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    //新增
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }

    }
    //新增
    packagingOptions {//加上這些程式碼
        pickFirst 'lib/armeabi-v7a/libnativeso.so'
        pickFirst 'lib/armeabi-v8a/libnativeso.so'
        pickFirst 'lib/arm64-v8a/libnativeso.so'
        pickFirst 'lib/x86/libnativeso.so'
        pickFirst 'lib/x86_64/libnativeso.so'
    }

}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}

(3)設定 gradle.properties
新增

android.useDeprecatedNdk=true

在这里插入图片描述
(4)設定好 ndk 路徑
在这里插入图片描述

6)、編譯第三方 非標準 庫

點選 途中 綠色小錘子 按鈕
在这里插入图片描述
然後就會編譯出我們要的so 庫

在这里插入图片描述

3、新專案 參照第三方 so 庫

1)新建 android studio 專案
2)新建 java 類,封裝想要呼叫的介面函數

在这里插入图片描述

package com.example.myjninative;

public class JniCallNative {
    static {
        System.loadLibrary("mynativeso");
    }
    public static native  int  getAddFromNative(int a,int b);
}

3)Main 資料夾新建 jniLibs 資料夾

(1) main 資料夾 新建 jniLibs 資料夾
(2)jniLibs 資料夾 新建so庫 對應架構資料夾
(3)拷貝 第三方 so 庫 到對應架構資料夾下
在这里插入图片描述

4)建立 對應的 jni java 類 檔案的 標頭檔案

android studio 命令列切換到 java 目錄
執行javah -jni + 類檔案包名 + 類名
生成 .h 檔案

javah -jni com.example.myjninative.JniCallNative

在这里插入图片描述
在这里插入图片描述
我們可以在 java 目錄生成 對應 .h 檔案
在这里插入图片描述
com_example_myjninative_JniCallNative.h 檔案

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

#ifndef _Included_com_example_myjninative_JniCallNative
#define _Included_com_example_myjninative_JniCallNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myjninative_JniCallNative
 * Method:    getAddFromNative
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_myjninative_JniCallNative_getAddFromNative
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

5) main 資料夾 下新建 jni資料夾,實現底層邏輯

(1)將上一步 生成 .h 檔案 拷貝進來
(2)根據.h 檔案 編寫 對應的 .cpp 檔案 ,在cpp 檔案裏實現對 第三方 so 庫 的參照
(3)將第三方 so 庫裡用到的 標頭檔案 拷貝進來
在这里插入图片描述jninative.cpp 檔案
裏面實現對第三方 so 庫 方法的參照

//
// Created by lum on 20-8-8.
//

#include <jni.h>
#include <string>
#include <test.h>//匯入需要的.h檔案,這個是必須的,如果依賴的第三方庫沒有.h,需要自己編寫

extern "C"
JNIEXPORT jint JNICALL Java_com_example_myjninative_JniCallNative_getAddFromNative
  (JNIEnv *, jclass, jint a, jint b){

    //生成add物件並呼叫方法
    Add addObj;
    int result = addObj.add(a,b);

    return result;


}

test.h
第三方 庫 裡的 使用的標頭檔案

#ifndef _TEST_JNI_ADD_H_
#define _TEST_JNI_ADD_H_

class Add{

public :
    Add();
    ~Add();
    int add(int x, int y);
};
#endif
6) 設定環境

同上

build.gradle(app )多新增 一段
在这里插入图片描述

7) 編寫 自己 so 庫的 CMakeLists.txt 檔案

app 檔案下新建 CMakeLists.txt

在这里插入图片描述

#指定cmake最小版本
cmake_minimum_required(VERSION 3.4.1)
#設定生成的so動態庫最後輸出的路徑
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

#生成so
add_library( # 設定生成庫的名字
             mynativeso
             # 生成動態庫
             SHARED
             # 指定原始碼檔案,這裏指定test.cpp檔案
             src/main/jni/jninative.cpp )

#依賴的標頭檔案
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni)

#依賴的add庫
add_library(nativeso SHARED IMPORTED)
set_target_properties(nativeso
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libnativeso.so)

find_library( # log庫的別名
              log-lib
             #log庫
              log )

#鏈接程式碼到指定的庫
target_link_libraries( # Specifies the target library.
                       mynativeso

                       #add庫需要鏈接
                       nativeso

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

8 ) 編譯自己的標so 庫

點選 綠色小錘子
在这里插入图片描述看到 jniLibs 下多了自己的 so 庫
在这里插入图片描述

9) 主函數呼叫測試

主函數新增 呼叫 jni 上層 java 介面的函數
在这里插入图片描述
編譯 apk 測試執行

在这里插入图片描述
發現自己的 java 上層 可以呼叫到第三方 的 so 庫 裡加法函數

檔案參考:
Android ndk呼叫非jni標準so方法