Android jni引用第三方so动态库和.a静态库并且调用(c)方法

最近花了一周时间来入门学习 Android JNI方面的知识,因为后续的工作很多需要用到c c++库,我需要用jni来包装一下c函数,来提供给上次java调用。总之多学点知识对自己有好处。

案例效果:
在这里插入图片描述
上文我们讲解了
android studio cmake生成.a文件(静态库)及调用(c c++)静态库.a

本文接着上文,这次我们利用JNI 来引用第三方动态库so文件,并且让Java调用它的函数。

第一步:生成.so动态库文件

编写C文件和头文件

DynamicLibraryTest.h

//
// Created by Administrator on 2023/9/5/005.
//

#ifndef ANDROIDCMAKE_DYNAMICLIBRARYTEST_H
#define ANDROIDCMAKE_DYNAMICLIBRARYTEST_H

#endif //ANDROIDCMAKE_DYNAMICLIBRARYTEST_H

int dynamicAdd(int a,int b,int c);

char * getDynamicName(char * firstName,char * lastName);

DynaminLibraryTest.cpp

//
// Created by Administrator on 2023/9/5/005.
//

#include "../include/static/DynamicLibraryTest.h"
#include 
#include 
#include 

int dynamicAdd(int a,int b,int c){
    return a+b+c;
}

char * getDynamicName(char * firstName,char * lastName){
    char *name = (char *) malloc(strlen(firstName) + strlen(lastName));
    strcpy(name, firstName); // 把firstName复制到name中
    strcat(name, lastName); // 把lastName追加到name中
    return name;
}

编写CmakeLists文件

    add_library(${CMAKE_PROJECT_NAME} SHARED   
        src/DynaminLibraryTest.cpp   
        )

make project之后,生成了.so动态库文件。
Android jni引用第三方so动态库和.a静态库并且调用(c)方法_第1张图片

生成动态库之后,我把文件复制到了jni目录下面.
Android jni引用第三方so动态库和.a静态库并且调用(c)方法_第2张图片

第二步:JNI动态注册.so动态库中的函数

编写cmakeList文件


cmake_minimum_required(VERSION 3.22.1)

project("cmake")

add_library(${CMAKE_PROJECT_NAME} SHARED
            native-lib.cpp
            src/libtest.c
            # 编写动态库用到的文件,已经生成了deynaminLibraryTest.so文件就注释掉这个代码
#        src/DynaminLibraryTest.cpp   
        )

#导入已经编译好的第三方静态库 或者 动态库  本例导入的静态库
add_library(calStatic STATIC IMPORTED)
#设置静态库(.a)导入的路径
set_target_properties(calStatic PROPERTIES IMPORTED_LOCATION
        ${CMAKE_CURRENT_SOURCE_DIR}/jni/${CMAKE_ANDROID_ARCH_ABI}/libcalStatic.a
)

#添加第三方 动态库
add_library(dynamicLibraryTest SHARED IMPORTED)
#设置动态库(.so)导入的路径
set_target_properties(dynamicLibraryTest PROPERTIES IMPORTED_LOCATION
        ${CMAKE_CURRENT_SOURCE_DIR}/jni/${CMAKE_ANDROID_ARCH_ABI}/libdynamicLibraryTest.so)

#通过target_link_libraries命令指明库文件,且通过target_include_directories命令指明相应的库头文件
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include/static/)
#需要链接或者编译的库
target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        # 第三方静态库。
        calStatic
        # 第三方动态库
        dynamicLibraryTest
        log)

JNI动态注册函数

#include 
#include 
#include 
#include 

#include "libtest.h"//内部头文件,cmake需要连接源文件地址
#include "CalculStatic.h"//第三方静态库 .a
#include "DynamicLibraryTest.h"//第三方动态库 .so

#include 
#define LOG_TAG "YIQI"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

//静态注册和动态注册,函数名一样的时候,优先用动态注册的,动态注册效率高。
//静态注册
//extern "C" JNIEXPORT jstring JNICALL
//Java_com_gitbaike_cmake_MainActivity_stringFromJNI(
//        JNIEnv* env,
//        jobject /* this */) {
//    std::string hello = "Hello from C++ 静态注册";
//    return env->NewStringUTF(hello.c_str());
//}

//静态注册
extern "C" JNIEXPORT void JNICALL
Java_com_gitbaike_cmake_MainActivity_HNPCInit(   JNIEnv* env,
                                                 jobject /* this */){
    HNPC_Init();
}

//动态注册
extern "C" {
  //调用了libcalStatic.a静态库中的方法
 jint addSum(JNIEnv *env,jobject instance,jint a,jint b,jint c){ //调用静态库
  return  calculAdd(a,b,c);
 }

//调用了libDynamicLibraryTest.so动态库中的方法
 jint jDynamicAdd(JNIEnv *env,jobject instance,jint a,jint b,jint c){
     return dynamicAdd(a,b,c);
 }

//调用了libDynamicLibraryTest.so动态库中的方法
 jstring jGetDynamicName(JNIEnv *env,jobject instance,jstring firstName,jstring lastName){
     char * name= getDynamicName((char*)env->GetStringUTFChars(firstName,0),
                                 (char*)env->GetStringUTFChars(lastName,0));
     return env->NewStringUTF(name);;
 }


 //入门程序  hello form c++
jstring stringFromJNI(JNIEnv *env, jobject instance) {
    std::string hello = "Hello from C++  动态注册";
    return env->NewStringUTF(hello.c_str());
}

//内部自定义c函数的调用,非第三方库
jint add(JNIEnv *env, jobject clazz, jint a, jint b) {
    return a + b;
}

/**
 * Native层,改变Java层对象的属性
 */
void changeName(JNIEnv *env, jobject clazz, jobject person){
    //获取person class对象
    //FindClass("com/gitbaike/cmake/model/Person"); 这里获取的是class类,不是对象
    jclass  personObject=env->GetObjectClass(person);//这里获取的是对象
    //获取setName方法的方法ID
    jmethodID setNameId=env->GetMethodID(personObject,"setName", "(Ljava/lang/String;)V");

    char *setNewName="Native 层赋予你的新名字:";
    jstring str=env->NewStringUTF(setNewName);
    //这里注意,第一个参数需要是jobject对象,所以传入的是person,而不是personObject(jclass对象)
    env->CallVoidMethod(person,setNameId,str);
}


/**
 * Native层,返回一个新的Person对象
 */
jobject getNewPerson(JNIEnv *env,jobject clazz){
    jclass newPerson= env->FindClass("com/gitbaike/cmake/model/Person");
    jmethodID personMethod=env->GetMethodID(newPerson,"", "(ILjava/lang/String;)V");
    jint  age=88;
    char * name="gitbaike";
    jstring jName=env->NewStringUTF(name);
    jobject  person=env->NewObject(newPerson,personMethod,age,jName);
    return person;
}

/**
 * Native层,返回一个新的Person对象集合
 * 返回java层一个list
 */
jobject getListPerson(JNIEnv *env,jobject clazz){
//因为list是无法实例对象,找到Arraylist,返回class对象
    jclass jclass1 = env->FindClass("java/util/ArrayList");
    //拿到构造函数id
    jmethodID contructMethod = env->GetMethodID(jclass1,"","()V");
    //生成一个Arraylist对象,就是我们要返回的对象
    jobject list = env->NewObject(jclass1,contructMethod);
    //拿到 list的 add方法的methodId,准备往method添加几个数据
    jmethodID methodAdd = env->GetMethodID(jclass1,"add","(Ljava/lang/Object;)Z");
    //拿到Person的class对象
    jclass studentClass = env->FindClass("com/gitbaike/cmake/model/Person");
    //拿到person的构造函数的methodId
    jmethodID jmethodID1 = env->GetMethodID(studentClass, "", "(ILjava/lang/String;)V");
    for(int i =1;i<=4;i++){
        char * name="tl";
        char newName[50]="";
//        std::to_string(i)
//       char * iStr=( char *)i;
        strcat (newName, name);
        char * is=reinterpret_cast<char*>(&i);
        strcat (newName, is);
        jobject person = env->NewObject(studentClass,jmethodID1,i,env->NewStringUTF(newName));
        //调用 list的add方法,因为返回时boolean值,所以CallBooleanMethod
        env->CallBooleanMethod(list,methodAdd,person);
    }
    return list;
}

//动态注册
jint RegisterNatives(JNIEnv *env) {
    jclass clazz = env->FindClass("com/gitbaike/cmake/MainActivity");
    if (clazz == NULL) {
        LOGE("con't find class: com/gitbaike/cmake/MainActivity");
        return JNI_ERR;
    }
    JNINativeMethod methods_MainActivity[] = {
            {"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
            {"add",           "(II)I",                (void *) add},
            {"changePersonName", "(Lcom/gitbaike/cmake/model/Person;)V",(void *) changeName},
            {"getPerson", "()Lcom/gitbaike/cmake/model/Person;",(void *)getNewPerson},
            {"getPeronList", "()Ljava/util/List;",(void *) getListPerson},
            {"addSum", "(III)I",(void *)addSum},
            {"dynamicAdd", "(III)I",(void *) jDynamicAdd},
            {"getDynamicName", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",(void *) jGetDynamicName}
    };
    // int len = sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]);
    return env->RegisterNatives(clazz, methods_MainActivity,
                                sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jint result = RegisterNatives(env);
    LOGD("RegisterNatives result: %d", result);
    return JNI_VERSION_1_6;
}

}

第三步:Android Java层调用JNI native函数

java调用:

package com.gitbaike.cmake;

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

import com.gitbaike.cmake.databinding.ActivityMainBinding;
import com.gitbaike.cmake.model.Person;
import com.google.gson.Gson;

import java.util.List;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    // Used to load the 'cmake' library on application startup.
    static {
        System.loadLibrary("cmake");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

//         Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());

        HNPCInit();

        Person person=new Person();
        person.setAge(12);
        person.setName("java person");

        Log.d(TAG,"调用本地方法前: person.getName:"+person.getName());
        changePersonName(person);
        Log.d(TAG,"调用本地方法后: person.getName:"+person.getName());

        Person nPerson=getPerson();
        Log.d(TAG,"调用本地方法 getPerson()后: person:"+nPerson.toString());

        List<Person> personList=getPeronList();
        Log.d(TAG,"调用本地方法 getPerson()后: personList:"+new Gson().toJson(personList) );

        Log.d(TAG,"调用本地方法 (引用第三方静态库.a)addSum:"+addSum(10,15,65));

        Log.d(TAG,"调用本地方法 (引用第三方动态库.so)dynamicAdd:"+dynamicAdd(100,200,300));

        Log.d(TAG,"调用本地方法 (引用第三方动态库.so)getDynamicName:"+getDynamicName("hello","  https://nav.vpssw.com  --->程序员网址导航<---"));
    }

    /**
     * A native method that is implemented by the 'cmake' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native void HNPCInit();

    public native int add(int a,int b);

    public  native void changePersonName(Person mPerson);

    public native Person getPerson();

    public native List<Person> getPeronList();

    public native int addSum(int a,int b,int c);


    public native int dynamicAdd(int a,int b,int c);

    public native String getDynamicName(String firstName,String lastName);

}

源码已经上传:https://download.csdn.net/download/qingfeng812/88301421

android studio cmake生成.a文件(静态库)及调用(c c++)静态库.a
android studio cmake生成so文件(动态库)及调用(c c++)动态库.so
java调用c函数 c函数调用java
jni静态注册与动态注册。
代码都是我跑通过的。
运行环境
as版本:Android Studio Giraffe | 2022.3.1 Patch 1
android AGP 8.1.1
gradle 版本8.0
jdk 17

你可能感兴趣的:(Andorid,JNI,android,c语言,开发语言)