博客借鉴 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文件,代码如下:
#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文件
开发一个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文件
#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窗口输出信息如下:
|