android studio通过 jni 调用第三方非标准 so库

调用第三方的so方法,但这个so内的方法不是标准的jni方法。这就需要我们自己写jni然后链接到第三方so库,通过jni调用so库中的方法。

1.简述:

要先有第三方的so库.so文件和编译库对应的.h头文件

  • 我们自己用 c/c++ 创建一个标准的so 库,比如 mynative.so
  • 然后用我们自己的mynative.so 库中去调用第三方的libnative.so库
  • 我们在上层 调用我们自己的mynativeso 库 ,就实现了对第三方 libnativeso 库 方法的调用。

2.先编译出一个第三方非标准so库

1).android studio 新创建一个项目

我们先制作一个非标准 jni 库,只要功能实现两个数相加 ,并返回相加后的结果。

2).main 新创建 jni 文件夹

3).新建 .cpp 文件和 .h 实现求和功能,并把文件放在jni目录下

test.cpp

#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).配置项目的build.gradle

6).配置 gradle.properties

android.useDeprecatedNdk=true

7).编译第三方非标准库,直接运行即可。

3.新建项目引用第三方 so 库

1)新建 android studio 项目
2)新建 java 类,封装想要调用的接口函数
package com.example.demo05;

public class JniCallNative {
    static {
        System.loadLibrary("mynativeso");
    }
    public static native  int  getAddFromNative(int a,int b);
}
3)Main 文件夹新建 jniLibs 文件夹,并拷贝第三方so到此文件夹下

4)创建 对应的 jni java 类文件的头文件

在Termial控制台中cd到项目目录中的源码目录下,并执行命令生成.h文件

  javac -encoding utf-8 -h .  JniCallNative.java

执行完成后,会在 java 目录生成 对应 .h 文件

5) main 文件夹 下新建 jni文件夹,实现底层逻辑
  • 将上一步 生成 .h 文件 拷贝进来,以及第三方so提供的对应.h头文件也要拷贝到jni目录中
  • 根据.h 文件 编写 对应的 .cpp 文件 ,在cpp 文件里实现对 第三方 so 库 的引用
  • 自己实现的逻辑中调用第三方.h内容

jninative.cpp 文件


#include <jni.h>
#include <string>
#include <test.h>//导入需要的.h文件,这个是必须的,如果依赖的第三方库没有.h,需要自己编写

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

    //生成add对象并调用方法
    Add addObj;
    int result = addObj.add(a,b);

    return result;


}

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)  配置环境 build.gradle和 gradle.properties

9) 编译自己的so库,直接运行即可

10) 主函数调用测试

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

package com.example.demo05;

import android.os.Bundle;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        TextView textView = findViewById(R.id.tv01);
        int addFromNative = JniCallNative.getAddFromNative(5, 5);
        textView.setText(addFromNative + "");


    }
}