Android:JNI实战,加载三方库、编译C/C++

这篇具有很好参考价值的文章主要介绍了Android:JNI实战,加载三方库、编译C/C++。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.概述

Android Jni机制让开发者可以在Java端调用到C/C++,也是Android应用开发需要掌握的一项重要的基础技能。

计划分两篇博文讲述Jni实战开发。

本篇主要从项目架构上剖析一个Android App如何通过Jni机制加载三方库C/C++文件。 

二.Native C++

Android Studio可以直接创建一个可运行的、最简单的Jni Demo App。

字符串"Hello from C++"Jni传到Java再在TextView上显示。

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

运行:

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

文件目录结构:

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

这个默认创建的Jni Demo App的代码就不一一展示了。

接下来会详细讲解自定义Jni App架构和代码改造过程

三.自定义JNI App

3.1 目录架构

默认创建的Jni Demo App只是简单实现了一个字符串在JniJava之间的传递,并没有涉及到加载三方库.c/.cpp,所以接下来要做的就是,在默认Jni Demo App基础上进行升级改造,实现一个便于扩展、能够加载三方库和.c/.cppDemo。

先看看改造后的目录结构:

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

相对于AndroidStudio默认创建的Jni Demo App,主要的修改点有如下:

  1. MainActivity.java中的Load Jni so以及native函数声明部分单独抽离出来,写成一个专门的JNIDEMO.java文件,便于对Jni的调用
  2. 与 java 目录平级新建 jnicpp 目录放置C/C++源码文件
  3. 与 java 目录平级新建 jnilibs 目录放置需要加载的三方库
  4. 变更CMakeLists.txt位置,放置在jnilibs根目录

3.2 源码解析

3.2.1 JniActivity.java
package com.android.demo.activity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.android.demo.databinding.ActivityJniBinding;

import com.android.demo.jni.JNIDEMO;

public class JniActivity extends AppCompatActivity {
    private final String TAG = "JniActivity";

    private ActivityJniBinding binding;

    //实现一个JNIDEMO实例对象
    private JNIDEMO mJniDemo = new JNIDEMO();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityJniBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        //通过JNIDEMO实例调用java native方法,从而调用到Jni方法,实现对Jni字符串的获取
        tv.setText(mJniDemo.JavaGetStringFromJNI());
    }
}
3.2.2 JNIDEMO.java
package com.android.demo.jni;

public class JNIDEMO {
    private static final String TAG = "JNIDEMO";

    // 应用启动时,load编译Jni生成的so
    static {
        System.loadLibrary("jnidemo");
    }

    //Java从Jni获取String
    public native String JavaGetStringFromJNI();
}
3.2.3 jnidemo.cpp
#include <jni.h>
#include <string>
#include <unistd.h>
#include <android/log.h> //引用android log

#include <stdlib.h>
#include <stdio.h>

#include "jnidemo.h"

//定义日志打印的方法
#define TAG "JNITEST" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型

using namespace std;

#ifdef __cplusplus   //兼容C++代码
extern "C" {         //兼容C代码

JNIEXPORT jstring JNICALL
Java_com_android_demo_jni_JNIDEMO_JavaGetStringFromJNI(
        JNIEnv *env,      //Java虚拟机内存地址指针
        jobject instance  //Java对象实例
        ) {
    string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

}
#endif
3.2.4 jnidemo.h

此次Demo实现重点在工程架构,功能并不复杂,与默认创建的Jni Demo一样,只是传递一个string,所以.h文件中暂未有变量和函数声明。

/*
* Created by Shawn.xiao on 2023/12/31.
*/
#ifndef MYDEMO_JNIDEMO_H
#define MYDEMO_JNIDEMO_H
#endif

#ifdef __cplusplus
extern "C" {

}
#endif
3.2.5 build.gradle

build.gradleandroid{ }中需要指定 jnilibs CMakeList.txt 两个路径

android {
     
    ...
    
    sourceSets {
        main{
            jniLibs.srcDirs = ['src/main/jnilibs']
        }
    }

    externalNativeBuild {
        cmake {
            path file('src/main/jnicpp/CMakeLists.txt')
            version '3.18.1'
        }
    }
    
    ...
    
}

如果要导入第三方库,CMake在编译时,会默认在jniLibs.srcDirs目录下查找和编译如下4个主流平台的库,如果这4个平台的库没有配置全,编译就会报错。

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

但是通常我们只会配置移动端64位的库,也就是arm64-v8a

所以需要在build.gradle :: android{} :: defaultConfig{}里加上如下代码进行过滤

android {
    
    ...
 
    defaultConfig {

        ...

        externalNativeBuild {
            cmake {
                abiFilters "arm64-v8a"
                arguments "-DANDROID_STL=c++_shared", "-DANDROID_ARM_NEON=TRUE"
                //arguments "-DANDROID_STL=c++_static", "-DANDROID_ARM_NEON=TRUE"
            }
        }
    }

    ...

}
3.2.6 CMakeLists.txt
(1).最基础的CMakeList.txt
# 设置CMake版本
cmake_minimum_required(VERSION 3.18.1)

# 设置变量
set(jnicpp_src "${CMAKE_SOURCE_DIR}/src")    #src源码路径
set(jnicpp_inc "${CMAKE_SOURCE_DIR}/inc")    #inc头文件路径
set(jnilibs_dir "${CMAKE_SOURCE_DIR}/src/main/jnilibs")  #so/.a

# 1.创建和命名库,本demo里是要生成的库jnidemo.so
# 2.设置要生成的库的属性:STATIC(.a) 或 SHARED(.so)
# 3.设置生成库的源码路径
# 4.可以定义多个库,CMake都会进行编译,Gradle再自动将库打包到Apk
add_library(
        jnidemo    #设置so文件名称
        SHARED     #设置这个so文件为共享
        ${jnicpp_src}/jnidemo.cpp)   #源码路径

#指定需要使用的公共NDK库
find_library(
        log-lib  # 设置路径变量名称
        log)     # 指定需要CMake去搜寻定位的公共NDK库

#链接头文件
target_include_directories(
        jnidemo    #Jni库
        PRIVATE    #对外引用属性
        ${jnicpp_inc})  #头文件路径

#包含头文件
#这个方法与target_include_directories()不同
#设置后,当前目录的所有子目录中的CMakeLists.txt头文件包含都会引用该方法中的变量定义
#include_directories(${jnicpp_inc})

# 指定需要用CMake链接到目标库的库。
# 可以链接多个库,例如在本脚本中定义的库、预构建的第三方库或系统库。
target_link_libraries(
        jnidemo     #指定目标库
        ${log-lib}  # 链接NDK中的log-lib库到目标库
)

一个最基本的CMakeList.txt就写成了

编译工程生成apk,将后缀.apk改为.zip后解压,就会发现lib文件夹

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

看看对应64位移动端的arm64-v8a目录:

Android:JNI实战,加载三方库、编译C/C++,Android,android,c++,java

到这里,一个能够加载第三方库C/C++文件Jni App基本成型了

但是,对于要导入的第三方库,这个最基础的CMakeLists.txt能做到的仅仅只是把它们加载到了Apk中,如果Jni代码中需要引用到这些三方库里的方法,那么在CMakeLists.txt里还需要对三方库进行设置和链接,将它们链接到最终会编译生成的Jni库上。

(2).链接三方库的CMakeLists.txt
# 设置CMake版本
cmake_minimum_required(VERSION 3.18.1)

# 设置变量
set(jnicpp_inc "${CMAKE_SOURCE_DIR}/inc")    # jnicpp/inc头文件目录路径
set(jnicpp_src "${CMAKE_SOURCE_DIR}/src")    # jnicpp/src源文件目录路径

# CMake所有内置变量都只能到CMakeLists.txt所在目录路径,下面方式可以获取CMakeLists.txt所在目录的上一级目录路径
string(REGEX REPLACE "(.*)/(.*)" "\\1" CMAKE_UP_PATH  ${PROJECT_SOURCE_DIR})
set(jnilibs_dir "${CMAKE_UP_PATH}/jnilibs")  ##jnilibs目录路径 so/.a

# 1.使用add_library命令创建和命名要生成的jni库,本demo里要生成的是jnidemo.so
# 2.设置要生成的库的属性:STATIC(.a) 或 SHARED(.so)
# 3.设置生成库的源码路径
add_library(
        jnidemo    #设置so文件名称
        SHARED     #设置这个so文件为共享
        ${jnicpp_src}/jnidemo.cpp)   #源码路径

#查找指定需要使用的公共NDK库
find_library(
        log-lib  # 设置路径变量名称
        log)     # 指定需要CMake去搜寻定位的公共NDK库

# 使用add_library命令,通过指定IMPORTED选项表明这是一个要导入的库文件
# 相当于告知CMake,我要链接三个SHARED动态库:aaa、bbb、ccc
# 这三句必须在前面,要不然后面的语句不知道aaa、bbb、ccc是什么
add_library(aaa SHARED IMPORTED)
add_library(bbb SHARED IMPORTED)
add_library(ccc SHARED IMPORTED)

# CMake属性设置函数,IMPORTED_LOCATION 表示设置目标aaa、bbb、ccc的文件路径属性
# ${CMAKE_SOURCE_DIR}:表示CMakeLists.txt的当前文件夹路径
# ${ANDROID_ABI}:编译时会自动根据CPU架构去选择相应的库
set_target_properties(aaa
        PROPERTIES
        IMPORTED_LOCATION
        "${jnilibs_dir}/${ANDROID_ABI}/libqxrcamclient.so")

set_target_properties(bbb
        PROPERTIES
        IMPORTED_LOCATION
        "${jnilibs_dir}/${ANDROID_ABI}/libqxrcoreclient.so")

set_target_properties(ccc
        PROPERTIES
        IMPORTED_LOCATION
        "${jnilibs_dir}/${ANDROID_ABI}/libqxrsplitclient.so")

#链接头文件目录路径
target_include_directories(
        jnidemo    #Jni库
        PRIVATE    #对外引用属性
        ${jnicpp_inc})  #头文件路径

#包含头文件
#这个方法与target_include_directories()不同
#设置后,当前目录的所有子目录中的CMakeLists.txt头文件包含都会引用该方法中的变量定义
#include_directories(${jnicpp_inc})

# 指定需要用CMake链接到目标库的库。
# 可以链接多个库,例如在本脚本中定义的库、导入的第三方库或系统库。
target_link_libraries(
        jnidemo     #指定目标库
        ${log-lib}  # 链接NDK中的log-lib库到目标库
        aaa
        bbb
        ccc
)

四.结束语

到此,一个可以加载三方库、编译C/C++的Jni App就搭建完成了

这一篇博文主要介绍Jni App的项目架构,构建文件编写等, 并没有涉及Jni代码语法

下一篇会在此篇基础上讲解Jni开发详细语法。文章来源地址https://www.toymoban.com/news/detail-814750.html

到了这里,关于Android:JNI实战,加载三方库、编译C/C++的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android JNI系列详解之CMake和ndk-build编译工具介绍

    一、前提 CMake和ndk-build只是编译工具,本次主要介绍ndk-build和CMake的区别,下节课介绍他们的使用。 二、CMake工具介绍 CMake:cross platform make,是跨平台的编译工具 CMake是在AndroidStudio2.2之后引入(目前默认的NDK编译工具),使用时配合CMakeList.txt和gradle。 对C++的配置: 默认配置

    2024年02月12日
    浏览(35)
  • Android Java代码与JNI交互 JNI访问Java类方法 (七)

    🔥 Android Studio 版本 🔥    🔥 创建包含JNI的类 JNIAccessMethod.java 🔥  🔥 Java方法对应Native层方法名称 🔥 🔥 配置动态库名称 🔥  🔥 生成可关联的库链接 🔥  为了让Java能够调用 access-method-lib 库中的函数,您需要使用 CMake 构建脚本中的 target_link_libraries() 命令来关联 

    2024年02月16日
    浏览(41)
  • Android Java代码与JNI交互 JNI方法Java类字段 (六)

    🔥 Android Studio 版本 🔥    🔥 Java 基础类型数据对应 Native层的字母 🔥  通过 jni 查找java某个类中相应字段对应的数据类型 , 需要使用到 jni 中的 GetFieldID() 函数 🔥 Java 引用类型数据对应 Native层字符串 🔥   🔥 创建 JNIAccessField 文件 🔥 🔥 配置动态库名称 🔥  🔥 生成

    2024年02月16日
    浏览(44)
  • Android Java代码与JNI交互 JNI访问Java构造方法(九)

    🔥 Android Studio 版本 🔥    🔥 创建包含JNI相关函数类 JNIConstructorClass.java 🔥  🔥 配置动态库名称 🔥   🔥 生成可关联的库链接 🔥  为了让Java能够调用 constructor-class-lib 库中的函数,您需要使用 CMake 构建脚本中的 target_link_libraries() 命令来关联constructor-class-lib 库  🔥

    2024年02月15日
    浏览(40)
  • Android Java代码与JNI交互 JNI子线程访问Java方法 (八)

    🔥 Android Studio 版本 🔥   🔥 创建包含JNI相关函数类 JNIInvokeMethod.java 🔥  🔥 配置动态库名称 🔥  🔥 生成可关联的库链接 🔥  为了让Java能够调用 invoke-method-lib 库中的函数,您需要使用 CMake 构建脚本中的 target_link_libraries() 命令来关联 invoke-method-lib 库  🔥 提供给N

    2024年02月16日
    浏览(49)
  • Android Studio编译JNI项目,Cmake出错:Detecting C compiler ABI info - failed

    cmake的定义是什么 ?-----⾼级编译配置⼯具cmake就是将多个cpp、hpp文件组合构建为一个大工程的语言。他能够输出各种各样的makefile或者project文件,所有操作都是通过编译CMakeLists.txt来完成。 会在编译时跳过编译器检查,就不会再出现异常了。 弱小不是逃避的借口,应是竭尽

    2024年02月09日
    浏览(75)
  • Android基于JNI的Java与C++互调

    java调用C++: //导出c函数格式 extern \\\"C\\\" JNIEXPORT //供JNI调用  JNICALL 函数名格式 Java_包名_类名_函数名(包名.替换为_) Java_com_example_getapplist_MainActivity_stringFromJNI    包名:com_example_getapplist   类名:MainActivity.      函数名:stringFromJNI C++ Jni  CMakeList.txt  

    2024年02月10日
    浏览(38)
  • Android蓝牙使能流程图(Android Bluetooth enable) -- java & jni层

    在介绍Android enable流程之前,我们首先来看下Android的流程图 以上图示是android8以上的,主要跟Android8之前的架构在于Bluetooth stack跟vendor层之间增加一层HIDL,主要是抽象硬件层的作用 OK,回归主题,我们来看下Android的enable流程,首先上一张架构图 另外声明下:我们做了一些列

    2024年02月09日
    浏览(45)
  • Android Java代码与JNI交互 引用类型转换(五)

    🔥 Android Studio 版本 🔥   🔥 Java中基础数据类型对应Native的数据类型 🔥 🔥 Java中引用数据类型 对应Native中的数据类型 🔥   🔥 创建  JNIReferenceType 🔥 🔥 生成可关联的库链接 🔥 为了让Java能够调用reference-type-lib库中的函数,您需要使用 CMake 构建脚本中的 target_link_l

    2024年02月13日
    浏览(46)
  • Android Java代码与JNI交互字符串转换(四)

    🔥 Android Studio 版本 🔥  🔥 创建JNIString.java 🔥  🔥 Native层实现 JNI的callNativeString函数 🔥 🔥 配置动态库名称 🔥  编辑CMakeLists.txt 根据j ni/jni_string.cpp 配置动态库名称  🔥 生成可关联的库链接 🔥 为了让Java能够调用 string-lib库中的函数,您需要使用 CMake 构建脚本中的 

    2024年02月13日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包