Android Studio的native jni so编译配置

本文详细介绍在Android Studio中使用JNI和NDK进行C/C++开发的方法,包括配置流程、项目结构、CMakeLists.txt与build.gradle文件的编写,以及引入第三方库的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Android Studio中编译jni和so库,可以使用app/CMakeList.txt,也可以使用Android.mk

 

AndroidStudio 进行 JNI / NDK 开发:初步配置及使用

2017年09月08日 12:03:44

阅读数:5118

说干就干!

JNI:Java Native Interface(Java 本地编程接口),一套编程规范,它提供了若干的 API 实现了 Java 和其他语言的通信(主要是 C/C++)。Java 可以通过 JNI 调用本地的 C/C++ 代码,本地的 C/C++ 代码也可以调用 java 代码。Java 通过 C/C++ 使用本地的代码的一个关键性原因在于 C/C++ 代码的高效性。

NDK:Native Development Kit(本地开发工具),一系列工具的集合,提供了一系列的工具,帮助开发者快速开发 C/C++,极大地减轻了开发人员的打包工作。

一、安装所需工具

  • NDK:这套工具集允许为 Android 使用 C 和 C++ 代码。
  • CMake:一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果只计划使用 ndk-build,则不需要此组件。
  • LLDB:一种调试程序,Android Studio 使用它来调试原生代码。

Ps:CMake 是 AS 2.2 之后加入的一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程),简单来说就是简化 JNI 开发的编译步骤,不用像以前那样要各种手动生成(http://www.jianshu.com/p/e7c2c63fa70e

二、创建支持 C/C++ 的新项目

1、新建支持 C/C++ 的新项目

只要在新建项目时勾上这里就行:

在向导的 Customize C++ Support 部分,有下列自定义项目可供选择:

  • C++ Standard:使用下拉列表选择使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
  • Exceptions Support:如果希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle文件的 cppFlags中,Gradle 会将其传递到 CMake。
  • Runtime Type Information Support:如果希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle文件的 cppFlags中,Gradle 会将其传递到 CMake。

2、主要项目结构

对于开发者来说,主要关注这两个地方:

  • cpp 文件夹:用于编写 C/C++代码
  • CMakeLists.txt:CMake 脚本配置文件

3、编写代码安装运行

新建支持 C/C++ 的新项目时有默认的实例代码:

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    // 加载本地库
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    // 本地方法
    public native String stringFromJNI();
}
#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_xq_jnidemo01_MainActivity_stringFromJNI(JNIEnv *env,jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

三、在原有旧项目中引入

1、新建 C/C++ 源码文件夹和文件

2、编写 CMakeLists.txt 文件

Ps:如果嫌编写 CMakeLists.txt 文件麻烦,就直接新建一个支持 C/C++ 的新项目,拷贝修改即可(拷贝之后可以删去文件中的注释,便于阅读)

# TODO 设置构建本机库文件所需的 CMake的最小版本
cmake_minimum_required(VERSION 3.4.1)

# TODO 添加自己写的 C/C++源文件
add_library( demo-lib
             SHARED
             src/main/cpp/demo-lib.cpp )

# TODO 依赖 NDK中的库
find_library( log-lib
              log )

# TODO 将目标库与 NDK中的库进行连接
target_link_libraries( demo-lib
                       ${log-lib} )

3、配置 build.gradle 文件

这里写图片描述

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                // 默认是 “ cppFlags "" ”
                // 如果要修改 Customize C++ Support 部分,可在这里加入
                cppFlags "-frtti -fexceptions"
            }
        }
    }
    buildTypes {
        ...
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    ...
}

最后是编写代码安装运行 …

四、几个要注意的点

1、要在哪个类运用 JNI ,就得加载相应的动态库

// 加载动态库
    static {
        System.loadLibrary("demo-lib");
    }
  • 1
  • 2
  • 3
  • 4

2、快速生成代码:Alt + Enter

3、新建 C/C++ 源代码文件,要添加到 CMakeLists.txt 文件中

4、引入第三方 .so文件,要添加到 CMakeLists.txt 文件中

(1)新建资源文件夹 jniLibs(貌似在 libs中也行,只要在 CMakeLists.txt中添加路径指示)

(2)在 CMakeLists.txt中添加路径指示

Ps:这里要注意两个地方

① 在定义库的名字时,不要加前缀 lib 和后缀 .so,不然会报错:java.lang.UnsatisfiedLinkError: Couldn’t load xxx : findLibrary【findLibrary returned null错误: http://blog.youkuaiyun.com/treasure3334/article/details/17170927
② ABI 文件夹上面不要再分层,直接用“jniLibs/${ANDROID_ABI}/”的格式,不然也会报错

# TODO 添加第三方库
# TODO add_library(libavcodec-57
# TODO 原先生成的.so文件在编译后会自动添加上前缀lib和后缀.so,
# TODO       在定义库的名字时,不要加前缀lib和后缀 .so,
# TODO       不然会报错:java.lang.UnsatisfiedLinkError: Couldn't load xxx : findLibrary returned null
add_library(avcodec-57
            # TODO STATIC表示静态的.a的库,SHARED表示.so的库
            SHARED
            IMPORTED)
set_target_properties(avcodec-57
                      PROPERTIES IMPORTED_LOCATION
                      # TODO ${CMAKE_SOURCE_DIR}:表示 CMakeLists.txt的当前文件夹路径
                      # TODO ${ANDROID_ABI}:编译时会自动根据 CPU架构去选择相应的库
                      # TODO ABI文件夹上面不要再分层,直接就 jniLibs/${ANDROID_ABI}/
                      # TODO ${CMAKE_SOURCE_DIR}/src/main/jniLibs/ffmpeg/${ANDROID_ABI}/libavcodec-57.so
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)
add_library(avdevice-57
            SHARED
            IMPORTED)
set_target_properties(avdevice-57
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavdevice-57.so)

(3)将自己编写的 C/C++源文件与第三方库进行连接

5、引入第三方 .h 文件夹,要添加到 CMakeLists.txt 文件中

Ps:本来想在 jniLibs 中引入,但编译会出错,所以只能在 CPP 资源文件夹中引入

# TODO include_directories( src/main/jniLibs/${ANDROID_ABI}/include )
# TODO 路径指向上面会编译出错,指向下面的路径就没问题
include_directories( src/main/cpp/ffmpeg/include )
  • 1
  • 2
  • 3

6、CPP 资源文件夹中的同名问题

CPP 资源文件夹下面的文件和文件夹不能重名,不然 System.loadLibrary() 时找不到,会报错:java.lang.UnsatisfiedLinkError: Native method not found

7、undefined reference to ‘xxx’

// 使C与C++能够正常混编
// 指示编译器按照C语言进行编译
extern "C"
{
    ...
}

8、error: ‘xxx.so’,needed by ‘xxxx.so’,missing and no known rule to make it

不同厂商的 Android 手机支持的 CPU 架构不同【Android-ABIFilter:http://blog.youkuaiyun.com/qq_32452623/article/details/71076023】,一般引入的第三方 .so 文件有多个 ABI 的类型,Cmake会默认对下面 7 个ABI分别调试,但如果只引入一部分,那么要在 build.gradle 中设置 ndk 的 abiFilters 属性:

 externalNativeBuild {
            cmake {
                // 默认是 “ cppFlags "" ”
                // 如果要修改 Customize C++ Support 部分,可在这里加入
                cppFlags "-frtti -fexceptions"
            }
        }
        ndk {
            // abiFiliter: ABI 过滤器(application binary interface,应用二进制接口)
            // Android 支持的 CPU 架构
            abiFilters 'armeabi'//,'armeabi-v7a','arm64-v8a','x86','x86_64','mips','mips64'
        }

 

 

 

本人示例:

CMakeList.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.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.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.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )


add_library(
             sffdecoder
             SHARED
             src/main/cpp/simplest_ffmpeg_decoder.c )


add_library(
             avcodec-56
             SHARED
             IMPORTED )
set_target_properties(avcodec-56
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-56.so)


add_library(
             avdevice-56
             SHARED
             IMPORTED )
set_target_properties(avdevice-56
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavdevice-56.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(
#                       avdevice-56
#                       ${log-lib} )

add_library(
             avfilter-5
             SHARED
             IMPORTED )
set_target_properties(avfilter-5
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavfilter-5.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(
 #                      avfilter-5
 #                      ${log-lib} )

add_library(
             avformat-56
             SHARED
             IMPORTED )
set_target_properties(avformat-56
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavformat-56.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(
#                       avformat-56
 #                      ${log-lib} )

add_library(
             avutil-54
             SHARED
             IMPORTED )
set_target_properties(avutil-54
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavutil-54.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(
#                       avutil-54
 #                      ${log-lib} )

add_library(
             postproc-53
             SHARED
             IMPORTED )
set_target_properties(postproc-53
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libpostproc-53.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(
#                       postproc-53
#                       ${log-lib} )







add_library(
             swresample-1
             SHARED
             IMPORTED )
set_target_properties(swresample-1
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswresample-1.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(
#                       swresample-1
#                       ${log-lib} )



add_library(
             swscale-3
             SHARED
             IMPORTED )
set_target_properties(swscale-3
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswscale-3.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(
 #                      swscale-3
 #                      ${log-lib} )

target_link_libraries(sffdecoder
avcodec-56
avdevice-56
avfilter-5
avformat-56
avutil-54
postproc-53
swresample-1
swscale-3
${log-lib} )

include_directories( src/main/cpp/include )

app/build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.champion.ffmpeghelloworld"
        minSdkVersion 24
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
        ndk {
            // abiFiliter: ABI 过滤器(application binary interface,应用二进制接口)
            // Android 支持的 CPU 架构
            abiFilters 'armeabi'//,'armeabi-v7a','arm64-v8a','x86','x86_64','mips','mips64'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

    sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值