博客借鉴
 Android NDK、JNI之- -(二)c代码中调用so文件
修改Android Studio工程目录中的package
- 切换到Project模式下,工程目录中的package是com.example.testndk4,想要修改成my.testndk4。
 
 
  - 首选删除原来的package,右键com.example.testndk4,弹出菜单中选择Delete,会发现package只剩下com.example
 - 继续右键Delete,则完全删除package。
 
- 新建package --> my.testndk4 
  
- 直接右键java目录,新建package为my.testndk4
 
 - 在新建package下,新建MainActivity
 - 在相关文件中修改package的配置,否则会出错。 
  
-  
首先是AndroidManifest.xml文件
 -  
然后是app下的build.gradle
 -  
然后是MainActivity
 
 -  
 - 经过以上操作,便可以编译运行,不会出错了。
 
开发一个libDemoA.so文件
配置ndk-bundle路径
- local.properties文件中添加代码如下: 
  
ndk.dir=D\:\\JAVA\\tools\\Android Studio\\Android_Sdk\\ndk-bundle
 
创建DemoA.java文件
- java目录下新建package为cn.fhy
 - 在cn.fhy包中新建DemoA.java类,编写代码如下:
 
package cn.fhy;
public class DemoA {
    
    private native String getName();
    private native int getAge();
    private native boolean isMale();
    private native int getAdditionResult(int x,int y);
    private native void showSomething();
    
}
 
生成.h文件
- 终端窗口切换到java目录下,然后编译cn.fhy.DemoA文件,如下所示:
 
- 编译之后便可以看到目录中生成cn_fhy_DemoA.h文件
 
新建jni文件夹
- 右键app,new – Folder – JNIFolder
 
将cn_fhy_DemoA.h文件移到jni文件夹中
在jni文件夹中新建DemoA.c文件
- 根据生成的cn_fhy_DemoA.h文件我们写下DemoA.c文件,代码如下:
 
//
// Created by FHY on 2022-04-29.
//
#include <string.h>
#include <jni.h>
#include <android/log.h>
jstring Java_cn_fhy_DemoA_getName(JNIEnv * env, jobject obj){
    char * str = "我来自DemoA.so,我的昵称是fhy!";
    return (*env) -> NewStringUTF(env,str);
}
jint Java_cn_fhy_DemoA_getAge(JNIEnv * env, jobject obj){
    return 22;
}
jboolean Java_cn_fhy_DemoA_isMale(JNIEnv * env, jobject obj){
    return JNI_TRUE;
}
jint Java_cn_fhy_DemoA_getAdditionResult(JNIEnv * env, jobject obj, jint x, jint y){
    return x + y;
}
void Java_cn_fhy_DemoA_showSomething(JNIEnv * env, jobject obj){
    __android_log_print(ANDROID_LOG_ERROR,"DemoA,so","这是来自DemoA.so的jniShowSomethign()方法!");
}
 
在jni文件夹中新建Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#加入这句可以使得so文件打印日志
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
LOCAL_MODULE:= DemoA
LOCAL_SRC_FILES := DemoA.c
include $(BUILD_SHARED_LIBRARY)
 
编译生成so文件
- 终端命令窗口,在java目录下,输入
ndk-build 
- 可以看到目录中生成的 libs 和 obj 文件夹。
 - 此时我们在app目录下新建一个 jniLibs 文件夹,如下:
 - 然后将libs中的内容全部复制过去,注意同时分arm64-v8a、armeabi-v7a、x86、x86_64四个子文件夹。
 
配置调用so文件的路径
- 在app下的build.gradle文件中添加代码如下:
 
调用so文件
-  
首先在DemoA.java中添加代码如下:
 -  

 -  
再在MainActivity中添加代码:
 -  

 -  
编译运行程序,在日志窗口中看到如下信息,便表示编译成功。
 -  

 
开发一个libDemoB.so文件
创建DemoB.java文件
- 新建package,com.fhy
 - 在com.fhy包中新建DemoB类
 
package com.fhy;
public class DemoB {
    private native String getName();
    private native int getAge();
    private native boolean isMale();
    private native int getAdditionResult(int x,int y);
    private native void showSomething();
}
 
直接编写Demo.c文件
- 为什么不先编译生成.h文件,再写.c文件呢? 
  
- 因为这个libDemoB.so文件就是为了调用libDemoA.so文件里面的方法的,所以,我们直接仿照DemoA.c文件来写DemoB.c文件。
 
 - 在 jni 文件夹中新建Demo.c文件
 
//
// Created by FHY on 2022-04-29.
//
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <android/log.h>
JNIEXPORT jstring JNICALL Java_com_fhy_DemoB_getName(JNIEnv * env, jobject obj){
    jstring result = "小明";
    void * handle = dlopen("libDemoA.so", RTLD_LAZY );
    if(handle){
        jstring (* getName)(JNIEnv*, jobject);
        getName = (jstring ( * ) (JNIEnv*, jobject)) dlsym(handle, "Java_cn_fhy_DemoA_getName");
        if(getName){
            result = getName(env, obj);
        }
        dlclose(handle);
        handle = NULL;
    }
    return result;
}
JNIEXPORT jint JNICALL Java_com_fhy_DemoB_getAge(JNIEnv * env, jobject obj){
    jint result = 16;
    void * handle = dlopen("libDemoA.so", RTLD_LAZY );
    if(handle){
        jint (* getAge)(JNIEnv*, jobject);
        getAge = (jint ( * ) (JNIEnv*, jobject)) dlsym(handle, "Java_cn_fhy_DemoA_getAge");
        if(getAge){
            result = getAge(env, obj);
        }
        dlclose(handle);
        handle = NULL;
    }
    return result;
}
JNIEXPORT jboolean JNICALL Java_com_fhy_DemoB_isMale(JNIEnv * env, jobject obj){
    jboolean result = 16;
    void * handle = dlopen("libDemoA.so", RTLD_LAZY );
    if(handle){
        jboolean (* isMale)(JNIEnv*, jobject);
        isMale = (jboolean ( * ) (JNIEnv*, jobject)) dlsym(handle, "Java_cn_fhy_DemoA_isMale");
        if(isMale){
            result = isMale(env, obj);
        }
        dlclose(handle);
        handle = NULL;
    }
    return result;
}
JNIEXPORT jint JNICALL Java_com_fhy_DemoB_getAdditionResult(JNIEnv * env, jobject obj, jint x, jint y){
    jint result = 0;
    void * handle = dlopen("libDemoA.so", RTLD_LAZY );
    if(handle){
        jint (* getResult)(JNIEnv*,jobject, jint, jint);
        getResult = (jint ( * ) (JNIEnv*, jobject, jint, jint)) dlsym(handle, "Java_cn_fhy_DemoA_getAdditionResult");
        if(getResult){
            result = getResult(env, obj, x, y);
        }
        dlclose(handle);
        handle = NULL;
    }
    return result;
}
JNIEXPORT void JNICALL Java_com_fhy_DemoB_showSomething(JNIEnv * env, jobject obj){
    void * handle = dlopen("libDemoA.so", RTLD_LAZY );
    if(handle){
        void (* showSomething)(JNIEnv*,jobject);
        showSomething = (void ( * ) (JNIEnv*, jobject)) dlsym(handle, "Java_cn_fhy_DemoA_showSomething");
        if(showSomething){
            showSomething(env, obj);
        }
        dlclose(handle);
        handle = NULL;
    }
}
 
- 具体代码解释可以我上面贴的借鉴博文-
 
修改Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#加入这句可以使得so文件打印日志
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
LOCAL_MODULE:= DemoB
LOCAL_SRC_FILES := DemoB.c
include $(BUILD_SHARED_LIBRARY)
 
编译生成libDemoB.so文件
- 终端命令窗口,java目录下,输入ndk-build命令行
 - 此时可以看到libs文件夹中的内容更新了,由libDemoA.so更新成libDemoB.so文件。
 
解释下为什么这里新建jniLibs
- 因为libs文件夹只能存在一种.so文件,而后面调用的时候必然同时调用libDemoA.so和libDemoB.so,所以需要将两种so文件放在同一个文件夹中,因此创建jniLibs,当做是自己的so库。
 - 当然肯定还有其他解决方法,比如Android.mk代码修改,只不过我不会,就用这种比较笨的方法。
 
将libDemoB.so文件复制到jniLibs文件夹中
- 此时jniLibs文件目录如下:
 
调用libDemoB.so文件
- DemoB.java
 
- MainActivity
 
- 最后运行程序,Log窗口输出信息如下:

 













