一、上一篇记录了Android如何调用JNI(Android Studio下通过JNI接口调用C++库(一)),这一篇记录Android如何通过JNI调用C++库
二、首先编写C++库(这里要确保你编写的C++库在本地是可以编译通过可以运行的),具体的代码可以看这里
1、根目录下面的CMakeLists.txt文件
cmake_minimum_required(VERSION 3.6.0)
project(verify_project)
include_directories(${PROJECT_SOURCE_DIR}/src)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/out)
if(WIN32)
set(LOCAL_PLATFROM windows/${CMAKE_BUILD_TYPE})
set(os_name windows)
elseif(UNIX AND NOT ANDROID AND NOT APPLE)
set(LOCAL_PLATFROM linux)
set(os_name linux)
elseif(APPLE AND NOT IOS)
set(LOCAL_PLATFROM macos)
set(os_name macos)
elseif(ANDROID)
set(LOCAL_PLATFROM ${CMAKE_ANDROID_ARCH_ABI})
set(os_name android)
endif()
add_subdirectory(${PROJECT_SOURCE_DIR}/src)
if(BUILD_EXAMPLE)
add_subdirectory(${PROJECT_SOURCE_DIR}/example)
endif()
2、新建 src文件夹和example文件夹,src/CMakeLists.txt文件
add_library(verify
./verify_callback.cpp
./verify_test.cpp
./verify_class.cpp
)
# INSTALL(FILES ${HEADERS} DESTINATION ${os_name}/include)
INSTALL(TARGETS verify DESTINATION ${os_name}/lib/${LOCAL_PLATFROM})
INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/verify_callback.h DESTINATION ${os_name}/include/verify)
INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/verify_test.h DESTINATION ${os_name}/include/verify)
INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/verify_class.h DESTINATION ${os_name}/include/verify)
if(MSVC)
INSTALL(FILES ${PROJECT_BINARY_DIR}/src/Debug/verify.pdb
DESTINATION ${os_name}/lib/${LOCAL_PLATFROM}
CONFIGURATIONS Debug)
endif()
3、src/verify_callback.h
#pragma once
#include <cstdint>
#include <functional>
using Callback = std::function<void(int)>;
void RegisteCallback(Callback callback);
void Start();
void Stop();
4、src/verify_callback.cpp
#include "verify_callback.h"
#include <iostream>
#include <thread>
class ValueCallback {
public:
static ValueCallback& GetInstance() {
static ValueCallback instance;
return instance;
}
void Start() {
if (running_) { return; }
running_ = true;
work_thread_ = std::thread(&ValueCallback::WorkFunc, this);
}
void Stop() {
if (!running_) { return; }
running_ = false;
work_thread_.join();
}
void RegisteCallback(Callback callback) { callback_ = callback; }
private:
ValueCallback() {}
ValueCallback(const ValueCallback&) = delete;
ValueCallback& operator=(const ValueCallback&) = delete;
~ValueCallback() {}
void WorkFunc() {
while (running_) {
int value = rand() % 9999;
if (value > 5555 && value < 5600) {
std::cout << "callback" << std::endl;
callback_(value);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
private:
bool running_ = false;
std::thread work_thread_;
Callback callback_;
};
void RegisteCallback(Callback callback) {
ValueCallback::GetInstance().RegisteCallback(callback);
}
void Start() { ValueCallback::GetInstance().Start(); }
void Stop() { ValueCallback::GetInstance().Stop(); }
5、src/verify_class.h
#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
class VerifyClass {
public:
VerifyClass();
~VerifyClass();
void SetValue(uint32_t id, const std::string& value);
std::string GetValue(uint32_t id);
private:
std::unordered_map<uint32_t, std::string> value_map_;
};
6、src/verify_class.cpp
#include "verify_class.h"
// std::unordered_map<uint32_t, std::string> value_map_;
VerifyClass::VerifyClass() {}
VerifyClass::~VerifyClass() {}
void VerifyClass::SetValue(uint32_t id, const std::string& value) {
value_map_[id] = value;
}
std::string VerifyClass::GetValue(uint32_t id) {
if (value_map_.find(id) == value_map_.end()) { return ""; }
return value_map_[id];
}
7、src/verify_test.h
#pragma once
#include <cstdint>
#include <string>
uint32_t GetRandValue();
std::string GetStringValue();
8、src/verify_test.cpp
#include "verify_test.h"
#include <iostream>
uint32_t GetRandValue() { return rand() % 100; }
std::string GetStringValue() { return "hello jni"; }
9、example/main.cpp
#include <iostream>
#include <string>
#include "../src/verify_callback.h"
#include "../src/verify_class.h"
#include "../src/verify_test.h"
int main(void) {
RegisteCallback([](int val) { std::cout << "val:" << val << std::endl; });
Start();
VerifyClass verify_class;
verify_class.SetValue(1,"hello");
verify_class.SetValue(2,"world");
std::cout << verify_class.GetValue(1) << std::endl;
std::cout << verify_class.GetValue(2) << std::endl;
std::cout << GetRandValue() << std::endl;
std::cout << GetStringValue() << std::endl;
getchar();
Stop();
return 0;
}
10、example/CMakeLists.txt
add_executable(verify_test ./main.cpp)
target_link_libraries(verify_test verify)
上面包含了回调,类,基本方法的使用
三、配置ANDROID_HOME和ANDROID_NDK_HOME,因为我是用的zsh,所以我就放在了.zshrc里面
export ANDROID_HOME=/Users/yangpan4485/Library/Android/sdk
export ANDROID_NDK_HOME=/Users/yangpan4485/Library/Android/sdk/ndk
四、编译Android构建脚本android_build.sh,这里的构建脚本表示会编译arm64-v8a和armeabi-v7a这两个平台下面的so库
#!/bin/bash
if [ -z "${ANDROID_HOME}" ]; then
echo "ANDROID_HOME is not set" && exit -1
fi
if [ -z "${ANDROID_NDK_HOME}" ]; then
echo "ANDROID_NDK_HOME is not set" && exit -1
fi
CMAKE=`find $ANDROID_HOME -name cmake | grep bin`
TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
BUILD_DIR=./build/android
echo "build android with ${CMAKE}"
ABI="arm64-v8a armeabi-v7a"
for abi in $ABI
do
echo "**********************"
echo " ${abi} shared "
echo "**********************"
if [ ! -d ${BUILD_DIR}/${abi} ]; then
mkdir -p $BUILD_DIR/${abi} || exit -1
fi
cd $BUILD_DIR/${abi}
${CMAKE} \
-DBUILD_SHARED_LIBS=ON \
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} \
-DANDROID_ABI="$abi" \
-DANDROID_NATIVE_API_LEVEL="android-19" \
-DANDROID_STL="c++_shared" \
-DANDROID_CPP_FEATURES="rtti exceptions" \
-DCMAKE_BUILD_TYPE=Release \
../../..
if [ $? -ne 0 ]; then
exit -1
fi
make -j4 && make install/strip || exit -1
cd -
done
五、执行./android_build.sh,这个时候如果当前ndk的版本过高的话,那么构建会出现问题,需要重新下载ndk
六、重新下载ndk,我这里下载的是16b版本,然后解压更换原来的ndk
下载地址:https://developer.android.google.cn/ndk/downloads/
七、重新执行./android_build.sh,这个时候会生成对应Android平台的so库,应该是在out目录下
八、新建Android工程(具体可参考上一篇文章),将生成的so库和头文件拷贝到对应目录下面,然后在jni层使用C++库里面的方法
我这里将头文件放在了verify/src/main/cpp/include目录下面
将so文件放在了verify/src/main/jniLibs目录下面
九、编写jni程序
1、这里我仍然是新建了一个Android Library命名为verify,然后新建3个class(VerifyCallback,VerifyClass,VerifyTest)和1个interface(TransmitterVerifyListener),这四个文件的内容如下
(1)VerifyCallback.java
package com.example.verify;
public class VerifyCallback {
static{
System.loadLibrary("verify_android");
}
private TransmitterVerifyListener mTransmitterVerifyListener;
public void registeVerifyListener(TransmitterVerifyListener transmitterVerifyListener){
mTransmitterVerifyListener = transmitterVerifyListener;
}
public native void start();
public native void stop();
public native void startListenTransmitter();
}
(2)VerifyClass.java
package com.example.verify;
public class VerifyClass {
static {
System.loadLibrary("verify_android");
}
public native long createObject();
public native void setValue(long verify , int id,String value);
public native String getValue(long verify , int id);
}
(3)VerifyTest.java
package com.example.verify;
public class VerifyTest {
static{
System.loadLibrary("verify_android");
}
public native int getRandValue();
public native String getStringValue();
}
(4)TransmitterVerifyListener.java
package com.example.verify;
public interface TransmitterVerifyListener {
public void onTransmitterVerifyValue(int value);
}
然后将3个class使用javah生成jni文件,目录结构如下所示
2、将这3个jni文件拷贝的cpp目录下面,然后新建这3个.h文件对应的.cpp文件(这里我重新命名了),然后再新建一个jni_helper.h和jni_helper.cpp文件,目录结构如下所示
其中jni_helper.h,jni_helper.cpp和其它几个cpp文件的内容如下
(1)jni_helper.h
//
// Created by yangpan4485 on 2020-02-24.
//
#ifndef VERIFYTEST_JNI_HELPER_H
#define VERIFYTEST_JNI_HELPER_H
#include <jni.h>
#include <string>
struct VerifyFields {
struct {
jfieldID verify_instance;
} native_object;
struct {
jmethodID onTransmitter;
}verify_callback;
};
/// 获取缓存的 Fields
VerifyFields &GetVerifyFields();
/// 获取当前线程的 Env
JNIEnv *GetJNIEnv();
void DetachCurrentThread();
/// 把 jstring 转换为 std::string
std::string JstringToString(JNIEnv* env,jstring val);
#endif //VERIFYTEST_JNI_HELPER_H
(2)jni_helper.cpp
#include "jni_helper.h"
#include <stdexcept>
#include <jni.h>
#include <pthread.h>
#include <sys/types.h>
#include <string>
#include <iostream>
#include <android/log.h>
#define TAG "verify-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) // 定义LOGD类型
JavaVM *myVm = nullptr;
/// 获取缓存的 Fields
VerifyFields &GetVerifyFields(){
static VerifyFields fields;
return fields;
}
/// 获取当前线程的 Env
JNIEnv *GetJNIEnv(){
JNIEnv *env = NULL;
if ((myVm)->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
int status = (myVm)->AttachCurrentThread(&env, 0);
if (status < 0) {
LOGD("###failed to attach current thread");
return NULL;
}
}
return env;
}
void DetachCurrentThread(){
myVm->DetachCurrentThread();
}
/// 把 jstring 转换为 std::string
std::string JstringToString(JNIEnv* env,jstring val){
jboolean isCopy;
const char* char_val = env->GetStringUTFChars(val, &isCopy);
std::string str_val(char_val);
if (isCopy == JNI_TRUE){
env->ReleaseStringUTFChars(val, char_val);
}
return str_val;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
myVm = vm;
if (myVm) {
LOGD("###m_vm init success");
} else {
LOGD("###m_vm init failed");
}
JNIEnv *env = NULL;
if ((vm)->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { return -1; }
VerifyFields &fields = GetVerifyFields();
// com.example.verify;
jclass clazz = env->FindClass("com/example/verify/VerifyCallback");
fields.native_object.verify_instance = env->GetFieldID(
clazz, "mTransmitterVerifyListener",
"Lcom/example/verify/TransmitterVerifyListener;");
clazz = env->FindClass("com/example/verify/TransmitterVerifyListener");
fields.verify_callback.onTransmitter = env->GetMethodID(clazz,"onTransmitterVerifyValue","(I)V");
LOGD("###m_vm init finish");
return JNI_VERSION_1_6;
}
#undef GET_CLASS
#undef GET_ID
void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if ((vm)->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
LOGD("###get env error");
}
myVm = nullptr;
}
(3)verify_callback_jni.cpp
#include "verify_callback_jni.h"
#include <android/log.h>
#include <pthread.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include "jni_helper.h"
#include "verify_callback.h"
#define TAG "verify-callback-jni" // 这个是自定义的LOG的标识
#define LOGD(...) \
__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) // 定义LOGD类型
jobject verify_obj;
JNIEXPORT void JNICALL Java_com_example_verify_VerifyCallback_start
(JNIEnv *env, jobject obj){
Start();
}
JNIEXPORT void JNICALL Java_com_example_verify_VerifyCallback_stop
(JNIEnv *env, jobject obj){
Stop();
}
void HandlerCallback(int val){
auto jni_env = GetJNIEnv();
auto verify_id = GetVerifyFields().verify_callback.onTransmitter;
jni_env->CallVoidMethod(verify_obj, verify_id, val);
DetachCurrentThread();
}
JNIEXPORT void JNICALL Java_com_example_verify_VerifyCallback_startListenTransmitter
(JNIEnv *env, jobject obj){
jobject mobj = env->GetObjectField(
obj, GetVerifyFields().native_object.verify_instance);
verify_obj = env->NewGlobalRef(mobj);
LOGD("###Start");
RegisteCallback([](int val) {
LOGD("###kCallback");
HandlerCallback(val);
});
}
(4)verify_class_jni.cpp
#include "verify_class_jni.h"
#include <android/log.h>
#include <pthread.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include "jni_helper.h"
#include "include/verify/verify_class.h"
#define TAG "verify-class-jni" // 这个是自定义的LOG的标识
#define LOGD(...) \
__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) // 定义LOGD类型
JNIEXPORT jlong JNICALL Java_com_example_verify_VerifyClass_createObject
(JNIEnv *env, jobject obj){
VerifyClass* verify_clz = new VerifyClass();
return (jlong)verify_clz;
}
JNIEXPORT void JNICALL Java_com_example_verify_VerifyClass_setValue
(JNIEnv *env, jobject obj, jlong verify, jint key, jstring value){
VerifyClass* verify_clz = (VerifyClass*)verify;
int i_key = key;
std::string str_value = JstringToString(env,value);
verify_clz->SetValue(key,str_value);
}
JNIEXPORT jstring JNICALL Java_com_example_verify_VerifyClass_getValue
(JNIEnv *env, jobject obj, jlong verify, jint key){
int i_key = key;
VerifyClass* verify_clz = (VerifyClass*)verify;
std::string str_value = verify_clz->GetValue(i_key);
return env->NewStringUTF(str_value.c_str());
}
(5)verify_test_jni.cpp
#include "verify_test_jni.h"
#include <android/log.h>
#include <pthread.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include "jni_helper.h"
#include "include/verify/verify_test.h"
#define TAG "verify-class-jni" // 这个是自定义的LOG的标识
#define LOGD(...) \
__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) // 定义LOGD类型
JNIEXPORT jint JNICALL Java_com_example_verify_VerifyTest_getRandValue
(JNIEnv *env, jobject obj){
uint32_t val = GetRandValue();
return val;
}
JNIEXPORT jstring JNICALL Java_com_example_verify_VerifyTest_getStringValue
(JNIEnv *env, jobject obj){
std::string str = GetStringValue();
return env->NewStringUTF(str.c_str());
}
3、修改verify目录下面的CMakeLists.txt文件
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
include_directories(
${CMAKE_SOURCE_DIR}/src/main/cpp
${CMAKE_SOURCE_DIR}/src/main/cpp/include/verify
)
add_library( # Sets the name of the library.
verify_android
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/jni_helper.cpp
src/main/cpp/verify_callback_jni.cpp
src/main/cpp/verify_class_jni.cpp
src/main/cpp/verify_test_jni.cpp
)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
add_library(
verify
SHARED
IMPORTED
)
set_target_properties( verify
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libverify.so)
target_link_libraries( # Specifies the target library.
verify_android
verify
# Links the target library to the log library
# included in the NDK.
${log-lib})
4、编写测试程序
(1)MainActivity.java
package com.example.verifytest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private Verify mVerify;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mVerify = new Verify();
mVerify.startVerify();
}
}
(2) Verify.java
package com.example.verifytest;
import android.util.Log;
import com.example.verify.VerifyTest;
import com.example.verify.TransmitterVerifyListener;
import com.example.verify.VerifyCallback;
import com.example.verify.VerifyClass;
public class Verify {
private TransmitterVerifyListener mTransmitterVerifyListener;
private VerifyTest mVerifyTest;
private VerifyCallback mVerifyCallback;
private VerifyClass mVerifyClass;
public Verify(){
}
public void startVerify(){
mTransmitterVerifyListener = new TransmitterVerifyListener() {
@Override
public void onTransmitterVerifyValue(int value) {
Log.d("####", "value:"+value);
}
};
mVerifyTest = new VerifyTest();
mVerifyCallback = new VerifyCallback();
mVerifyClass = new VerifyClass();
Log.d("###","rand value:" + mVerifyTest.getRandValue());
Log.d("###","string value:" + mVerifyTest.getStringValue());
mVerifyCallback.registeVerifyListener(mTransmitterVerifyListener);
mVerifyCallback.startListenTransmitter();
mVerifyCallback.start();
long clz_obj = mVerifyClass.createObject();
mVerifyClass.setValue(clz_obj,1,"hello");
Log.d("###","get value:"+mVerifyClass.getValue(clz_obj,1));
int value = 0;
while(true){
value++;
if(value > 100){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mVerifyCallback.stop();
}
}
十、运行程序,观察结果
十一、项目对应的github地址