AndroidStudio-Jni开发

AndroidStudio-Jni开发

主要记录本人进行Jni开发时,遇到的坑,及注意事项。

本文项目是需要使用Java通过Jni调用test.c中的代码,其中test.c中引用了.h头文件,对第三方的so库中方法进行调试。

创建NativeC++工程

使用studio,创建Native C++工程。

AndroidStudio-Jni开发_第1张图片

 创建项目之后,自动生成cpp文件夹,里面有CMakeLists.txt、native-lib.cpp两个文件。

  1. 接着在cpp中创建include文件夹,用来放.h的头文件。然后我项目中将native-lib.cpp,改名位test.c。cpp指的是使用C++进行编写,这里改成.c,使用C语言进行编写。
  2. 在main文件夹下创建jniLibs文件,将依赖的第三方so库放进去。
  3. 在File-ProjectStructure-SDKLocation 中对NDK进行配置。如:D:\Android\androidSdk\ndk-bundle
  4. build.gradle中,在android{...}内添加下列配置,创建项目之后会默认生成一系列配置,看着修改就行。这里注意下NDK版本,17以上不支持armeabi,需要引用armeabi中的so时,要降低NDK版本。
defaultConfig {
        ndk {
            //选择要添加的对应CPU类型的so
            abiFilters 'arm64-v8a','armeabi-v7a'//ndk版本17及以上不支持armeabi,否则编译会出现上面错误,需要引用armeabi库时,降低NDK版本到15
        }
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'arm64-v8a','armeabi-v7a'//ndk版本17及以上不支持armeabi,否则编译会出现上面错误,需要引用armeabi库时,降低NDK版本到15
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }

CMakeLists配置

CMakeLists.txt中主要是一些配置,这里要注意如果jni要生成导出so的话,路径不要与所依赖的第三方so库路径一样,否则会报错。我的设置是将jni产生的so放到了cpp文件夹下的jniLibs中。特别注意下,所放置的so路径一定要配置正确。我的配置如下:

# 编译本地库时我们需要的最小的cmake版本
cmake_minimum_required(VERSION 3.4.1)

#如果不指定so的导出路径,gradle会默认将编译出来的so库打包进apk,而不需要像以前一样将编译后的so库输出到jniLibs下再进行打包。
#如果导出到jniLibs文件夹中会造成冲突,可以使用下面路径进行导出
#${ANDROID_ABI}:编译时会自动根据CPU架构去选择相应的库
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/jniLibs/${ANDROID_ABI}) #生成的so库在指定路径

#加载CPP文件夹下待编译的cpp文件,对于so里方法的调用,也是在这个文件里进行封装,封装好之后给调用类CryptoUtil来调
add_library(
        demoforc #jni生成的so库名称
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        test.c)

#添加第三方头文件${CMAKE_SOURCE_DIR}:表示CMake.txt的当前文件夹路径
target_include_directories(demoforc PRIVATE ${CMAKE_SOURCE_DIR}/include)

#动态方式加载 STATIC:表示静态的.a的库 SHARED:表示.so的库。
add_library(第三方so名称 SHARED IMPORTED)

#设置要连接的so的相对路径  
set_target_properties(第三方so名称 PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/lib第三方so名称.so)

#默认配置项,不用修改
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)

#设置要链接的库文件的名称
target_link_libraries( # Specifies the target library.
        demoforc 
        # Links the target library to the log library
        第三方so名称
        # included in the NDK.
        ${log-lib})

我项目中产生的so及第三放so的位置:

AndroidStudio-Jni开发_第2张图片

 代码实现

创建jni的so调用类:TestUtil

public class TestUtil {

    static {
        System.loadLibrary("demoforc");
    }

    public native String getVersion();
    public native void testRun();


}

native方法创建好后,按键Alt+Enter,会自动在test.c中生成JNI的调用方法。如

JNIEXPORT jstring JNICALL
Java_com_demo_demoforc_TestUtil_getVersion(JNIEnv *env, jobject thiz) {
    // TODO: implement getVersion()
    /...
    ... 省略了部分代码
    .../
}

JNIEXPORT void JNICALL
Java_com_demo_demoforc_TestUtil_testRun(JNIEnv *env, jobject thiz) {
    // TODO: implement test()
  /...
    ... 省略了部分代码
    .../
}

调用类创建完成之后,在MainActivity中进行调用。

public class MainActivity extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = findViewById(R.id.sample_text);

        CryptoUtil cryptoUtil = new CryptoUtil();// 调用类
        tv.setText(cryptoUtil.getVersion());// 获取版本号
        cryptoUtil.testRun();// 调用算法测试信息
    }

}

test.c中,要在jni产生的方法中进行C代码的调用:

#include 
#include 
#include 
#include "include/alg.h" // 使用的第三方so库的头文件,添加之后可使用其中的方法
#include  // Android的log输出日志

// 导入的log输出,设置之后,可以在Logcat中看C中写的log输出
#define Logi(...) __android_log_print(ANDROID_LOG_INFO, "demoForC", __VA_ARGS__)


JNIEXPORT jstring JNICALL
Java_com_demo_demoforc_TestUtil_getVersion(JNIEnv *env, jobject thiz) {
    // TODO: implement getVersion()
    const char *version = ALG_GetVersion();
    return (*env)->NewStringUTF(env, version);
}

JNIEXPORT void JNICALL
Java_com_demo_demoforc_TestUtil_testRun(JNIEnv *env, jobject thiz) {
    // TODO: implement test()
    int ret = testRun();
}

int testRun() {
    // 实现代码
}

基本上的流程就是这样,其中在CMakeLists文件的配置上摸索了一段时间,一直找不到第三方依赖的so库,最后发现是路径配置的问题。

 

 

你可能感兴趣的:(Jni开发,android,android,studio,jni,cmake)