【第一步】
编写C++代码
Test.cpp
int test() {
return 10086;
}
【第二步】
生成so库
在AS中配置好cmake环境,将 Test.cpp 放入工程,编译出 so 库。(第一章节有详细介绍)
【第三步】
声明 extern int test(),并调用test函数
#include <jni.h>
#include <string>
#include <pthread.h>
#include <android/log.h>
#define LOG_TAG "native-lib"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#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 LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
using namespace std;
// 如果是C,需要添加 extern "C"{}
extern int test();
// 全局变量
JavaVM* javaVm;
struct Context {
jobject obj;
};
/**
* 子线程
* @param args
* @return
*/
void* threadTask(void* args) {
LOGI("start thread task");
LOGE("执行test函数:%d", test());
if (javaVm == NULL) {
LOGE("javaVm is null");
return JNI_FALSE;
}
JNIEnv* env;
// 将native线程添加到JVM中
jint isAttach = javaVm->AttachCurrentThread(&env, 0);
if (isAttach != JNI_OK) {
LOGE("attact thread error");
return JNI_FALSE;
}
Context* context = static_cast<Context *>(args);
// 得到字节码
jclass clazz = env->GetObjectClass(context->obj);
// 得到methodId
jmethodID methodId = env->GetMethodID(clazz, "updateUI", "()V");
// 执行方法
env->CallVoidMethod(context->obj, methodId);
// 分离
javaVm->DetachCurrentThread();
delete context;
context = NULL;
return JNI_FALSE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void*) {
LOGI("in JNI_Onload");
javaVm = vm;
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT void JNICALL
Java_com_nobug_jniproject_MainActivity_testThread(JNIEnv *env, jobject obj) {
LOGI("testThread from C");
Context* context = new Context;
context->obj = env->NewGlobalRef(obj);
pthread_t pid;
// 启动一个线程
pthread_create(&pid, 0,threadTask, context);
}
【第四步】
Java 调用 testThread
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private JNI jni = new JNI();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.sampleText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testThread();
}
});
}
public native void testThread();
public void updateUI() {
if (Looper.getMainLooper() == Looper.myLooper()) {
Toast.makeText(MainActivity.this, "updateUI", Toast.LENGTH_SHORT).show();
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "updateUI", Toast.LENGTH_SHORT).show();
}
});
}
}
}
【第五步】
注意Gradle配置
plugins {
id 'com.android.application'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.nobug.jniproject"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
cppFlags "-std=c++11 -frtti -fexceptions -Os -Wall"
}
}
ndk { // "armeabi-v7a", "arm64-v8a"
abiFilters 'armeabi-v7a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
version '3.18.1'
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
【第六步】
cmake配置
cmake_minimum_required(VERSION 3.18.1)
# project("jniproject")
# 导入库目录
# include_directories("C:/Program Files/Java/jdk-18.0.1.1/include")
# 生成一个动态库(windows:dll,android:so)
add_library(jniproject SHARED src/main/cpp/native-lib.cpp)
# 设置一个变量
# CMAKE_CXX_FLAGS c++参数
# CMAKE_C_FLAGS c参数
# CMAKE_C_FLAGS = --sysroot=XX
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
#add_library(Test SHARED IMPORTED)
#set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)
# 生成可执行文件(生成exe文件, 在VS工具上可用)
# add_executable (jniproject1 native-lib.cpp)
# log-lib 是变量名称 log是动态库名称 将liblog.so或liblog.a的路径赋值给log-lib
find_library(log-lib log)
# 链接ndk自带的库
target_link_libraries(
jniproject # 链接 libjniproject.so
Test # 链接 libTest.so
${log-lib}) # 链接 liblog.so
其中
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
可以改成:
add_library(Test SHARED IMPORTED)
set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)
【第七步】
运行app,查看工作台中是否有相关日志
image.png
[本章完...]