淘先锋技术网

首页 1 2 3 4 5 6 7

一、上一篇记录了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地址

github:https://github.com/yangpan4485/AndroidJnitest