當我們呼叫別人的非標準的 jni so 庫,無法使用 jni 標準介面的時候。我們需要採用間接的方式呼叫。
(1)比如現在我們有第三方給的 libnative.so 庫,無法自己直接 jni 呼叫
(2)我們自己用 c/c++ 建立一個標準的so 庫,比如 mynative.so
(3) 然後用我們自己的mynative.so 庫中去呼叫第三方的libnative.so庫
(4) 然後 我們在上層 呼叫我們自己的mynativeso 庫 ,就實現了對第三方 libnativeso 庫 方法的呼叫。
這種方式 我們要先有第三方 的 so 庫 和編譯庫 對應的標頭檔案
我們先製作一個非標準 jni 庫,只要功能實現兩個 數相加 ,並返回
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
在 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} )
(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 路徑
點選 途中 綠色小錘子 按鈕
然後就會編譯出我們要的so 庫
package com.example.myjninative;
public class JniCallNative {
static {
System.loadLibrary("mynativeso");
}
public static native int getAddFromNative(int a,int b);
}
(1) main 資料夾 新建 jniLibs 資料夾
(2)jniLibs 資料夾 新建so庫 對應架構資料夾
(3)拷貝 第三方 so 庫 到對應架構資料夾下
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
(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
同上
build.gradle(app )多新增 一段
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} )
點選 綠色小錘子
看到 jniLibs 下多了自己的 so 庫
主函數新增 呼叫 jni 上層 java 介面的函數
編譯 apk 測試執行
發現自己的 java 上層 可以呼叫到第三方 的 so 庫 裡加法函數
檔案參考:
Android ndk呼叫非jni標準so方法