Android:JNI与NDK(二)交叉编译与动态库,静态库

2023-11-18

本篇目录

一、前言

本篇主要以window开发环境为背景介绍一下NDK开发中需要掌握的交叉编译等基础知识,选window系统主要是照顾大多数读者,mac ,linux操作系统基本是同样适用的。

交叉编译就是在A平台编译出可以在B平台执行的文件,对于我们安卓开发者来说交叉编译就是在window或者mac或者linux系统上编译出可在安卓系统上运行的可执行文件,什么时候需要用到交叉编译呢?音视频开发基本都会用到ffmpeg,opengl es等三方库,这时我们就需要在window或者mac或者linux系统上编译出可在安卓系统执行的文件,这里可编译出静态库或者动态库使用,这时候就会用到交叉编译。

本篇虽然是一些基础的知识或者操作,但是对于后续三方库的编译移植,CMake的配置是很重要的,否则后续遇到没用过的三方库你会感觉无从下手编译,很多CMake的配置也只是会配置而不懂具体什么含义。

进行本篇学习请先自己配置好MinGW(C/C++编译器)编译环境并配置到系统环境变量中,这些都是基础的操作,自己查询一下配置好就可以了,此外还需要自己下载好安卓平台提供的交叉编译工具链,下载地址:安卓平台交叉编译工具,我下载的是17c版本的。

好了,进入本文的学习

下文相关代码均来自:相关演示代码

二、常用C/C++编译器了解以及C/C++文件编译过程
常用C/C++编译器
编译器名称 描述
clang clang 是一个C、C++、Object-C的轻量级编译器。基于LLVM (LLVM是以C++编写而成的构架编译器的框架系统,可以说是一个用于开发编译器相关的库),对比gcc,它具有编译速度更快、编译产出更小等优点,但是某些软件在使用clang编译时候因为源码中内容的问题会出现错误
gcc GNU C编译器。原本只能处理C语言,很快扩展,变得可处理C++`。(GNU计划,又称革奴计划。目标是创建一套完全自由的操作系统)
g++ GNU c++编译器,后缀为.c的源文件,gcc把它当作是C程序,而g++当作是C++程序;后缀为.cpp`的,两者都会认为是c++程序,g++会自动链接c++标准库stl,gcc不会,gcc不会定义__cplusplus宏,而g++会
C/C++文件编译过程

C/C++文件要经过预处理、编译、汇编、和连接才能变成可执行文件。

过程名称 主要作用
预处理 预处理阶段主要处理include和define等。它把#include包含进来的.h 文件插入到#include所在的位置,把源程序中使用到的用#define定义的宏用实际的字符串代替
编译 编译阶段,编译器检查代码的规范性、语法错误等,检查无误后,编译器把代码翻译成汇编语言。
汇编 汇编阶段把 .s文件翻译成二进制机器指令文件.o,这个阶段接收.c, .i, .s的文件都没有问题
连接 链接阶段,链接的是其余的函数库,比如我们自己编写的c/c++文件中用到了三方的函数库,在连接阶段就需要连接三方函数库,如果连接不到就会报错

比如在命令行中我们执行如下命令:

gcc -o d:\main C:\Users\wanglei55\Desktop\main.c

将C:\Users\wanglei55\Desktop\main.c文件编译为可执行文件,输出到d盘名称为main,整个编译过程就包括预处理、编译、汇编、和连接过程。

以上主要介绍了常用C/C++编译器的区别以及C/C++文件的编译过程,大体了解一下即可。

三、交叉编译

接下来我们具体看一下交叉编译的流程,我们先来看一下window平台怎么编译出可执行文件。

我们编写如下C文件:
main.c

#include <stdio.h>
int main()
{
	int nn = 55;
    printf("nn = %d\n", nn);
    return 0;
}

很简单,就是输出一些信息,接下来我们将main.c用gcc编译器编译为可执行文件,执行如下命令:

gcc -o d:\main C:\Users\wanglei55\Desktop\main.c

这样就会在d盘根目录生成mian.exe文件(window平台下会加入扩展名.exe,mac/linux平台下则不会)。

接下来我们就可以在命令行执行这个可执行文件了:

到这里我们成功的在window平台生成了可执行文件,试想一下我们可以将这个可执行文件拷贝到安卓手机上执行吗?估计很多同学及时没试过也会觉得不会执行,但是为什么呢?最简单的说法就是安卓平台不认识.exe结尾的可执行文件,那如果我是在linux平台编译出来的呢?不就没有.exe了吗?及时在linux平台编译出来的拷贝到安卓平台同样是不能执行的,主要原因是两个平台的CPU指令集不一样,根本就无法识别指令

那我们怎么将main.c文件编译为可以在安卓平台执行的文件呢?这样就用到交叉编译了,这里就是在window平台编译出可在安卓平台执行的文件,既然要编译出在安卓平台执行的文件就需要用到目标平台提供的编译工具了,安卓提供的编译工具上面已经给出了下载链接,我下载的是17c版本的:

下载对应平台的zip包即可。

解压后(我解压到桌面上了)目录下toolchains目录就有对应平台的编译工具,安卓手机目前大部分cpu都是arm架构的了,我们以arm平台为例:

对应目录下就为我们提供了相应的gcc编译器。

接下来我们就用安卓平台提供的gcc编译器来编译main.c文件,这里要多说一下接下来的过程我会讲的细一些,因为这里很重要,很重要,很重要,我工作中接触很多同事不明白编译器的参数传入方式有问题只能百度,即使问题解决了也不明白咋回事,其实很简单,下面过程会讲解到,好了,我们具体看一下吧编译安卓平台可执行文件的过程吧:

首先cd到arm-linux-androideabi-gcc.exe所在目录,执行如下命令:

arm-linux-androideabi-gcc.exe -o d:\main C:\Users\wanglei55\Desktop\main.c

执行命令会报如下错误:

这种错误是说在我们编译的时候编译器找不到我们引入的stdio.h头文件,那怎么告诉编译器stdio.h头文件在哪呢?

给编译器指定头文件的查找目录

我们可以通过如下方式给编译器指定头文件的查找目录:

指定格式 说明
–sysroot=XX 使用xx作为这一次编译的头文件与库文件的查找目录,查找下面的 usr/include usr/lib目录,–sysroot即可指定头文件又可指定库文件
-isysroot XX 指定头文件查找目录,覆盖–sysroot ,查找 XX/usr/include目录下头文件
-isystem XX 指定头文件查找路径(直接查找根目录)
-IXX 头文件查找目录,I是大写的

指定方式有多种,选取其中一种即可。

既然知道了头文件的指定方式,那我们得知道stdio.h的头文件目录,stdio.h头文件位于如下目录:android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include

既然也知道头文件的目录了,我们就可以指定了,这里通过-isystem方式指定:

arm-linux-androideabi-gcc.exe -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include -o d:\main C:\Users\wanglei55\Desktop\main.c

执行上面命令,又会报如下错误:

又提示 asm/types.h头文件找不到,我们也没用这个头文件啊?这里实在stdio.h文件中引入的:

所以,我们还需要指定上面的头文件目录,头文件所在目录为:android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include\arm-linux-androideabi

修改命令如下,增加额外查找命令:

arm-linux-androideabi-gcc.exe -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include\arm-linux-androideabi -o d:\main C:\Users\wanglei55\Desktop\main.c

运行,还是会报错:

这里我就直接说了,上面我们都是指定头文件的查找路径,但是运行程序需要具体的实现来完成作用,比如在main.c中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明。系统把这些函数实现都被做到名为libc.so的动态库。那怎么指定查找具体实现库的目录呢?同样编译的时候可以指定库文件的查找目录:

指定方式 说明
–sysroot=XX 上面已经说过–sysroot=XX即可指定头文件又可指定库文件的查找目录
-LXX 指定库文件查找目录
-lxx 指定需要链接的库名,如果库名为libc.so,指定库名可简写:-lc ,lib和.so可去掉

printf这种常用的函数都在libc.so动态库中实现,那libc.so在哪个目录下呢?如下:

接下来我们需要在编译的时候指定相关库的查找路径以及库名,修改命令如下:

arm-linux-androideabi-gcc.exe --sysroot=C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\platforms\android-22\arch-arm -lc -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include\arm-linux-androideabi -o d:\main C:\Users\wanglei55\Desktop\main.c

到这里我们就可以正常编译了,但是要编译出安卓平台可执行文件,编译时还需要加入 -pie ,完整命令如下:

arm-linux-androideabi-gcc.exe --sysroot=C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\platforms\android-22\arch-arm -lc -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include\arm-linux-androideabi -pie -o d:\main C:\Users\wanglei55\Desktop\main.c

到此我们就可以编译出在安卓平台的可执行文件了:

整个过程是不是感觉很繁琐,其实最核心的就是编译过程中头文件和库文件目录的指定方式,让编译器可以找到对应文件,否则编译的时候就会报各种错误,如果你有ndk相关开发经验,应该会理解我们在cmake或者mk中的配置很多也是这种配置,就是为了让编译器编译的时候能查找到对应头文件或者库文件

四、动态库与静态库的编译与使用

在安卓平台上我们用的最多的是动态库与静态库,我们先来看看怎么编译出动态库与静态库并在安卓平台使用。

源文件为:
test.c

#include <stdio.h>
int test(){
    return 999;
}

就是定义了一个test方法,返回int值999,我们将这个源文件在电脑上先编译为动态库,然后在安卓平台使用。

编译使用动态库

在编译动态库的时候我们需要指定 -fPIC -shared额外参数给编译器,完整命令如下:

arm-linux-androideabi-gcc.exe --sysroot=C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\platforms\android-22\arch-arm -lc -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include\arm-linux-androideabi -fPIC -shared C:\Users\wanglei55\Desktop\test.c -o d:\libTest.so

这样就将桌面上的test.c源文件(test.c我放在了桌面)在d盘生成了libTest.so动态库,接下来我们在安卓工程中使用libTest.so动态库中的test()方法。

在工程中新疆如下目录,并将libTest.so拷贝进去:

如不特殊指定,使用三方的动态so库,目录名称必须为jniLibs。

接下来我们在native-lib.cpp文件中调用libTest.so库中的test()方法,由于是在c++文件中调用c文件编译为动态库中的test()方法,所以需要加上如下声明:

//C++中使用C代码需要这样声明,防止C++编译器将C中方法名编译后认不出了
extern "C"{
    extern int test();
}

调用test()方法如下:

JNIEXPORT jstring JNICALL
Java_com_wanglei55_ndk_MainActivity_stringFromJNI(JNIEnv *env,jobject /* this */) {

    LOGE("libTest.so动态库中test()方法返回值为:%d", test());
    int i = test();
    std::string s1 = std::to_string(i);
    std::string s2 = "Hello from C++";
    std::string s = s1 + s2;
    return env->NewStringUTF(s.c_str());
}

接下来我们还要在CMakelist.txt文件中配置一下让编译器编译的时候能够找到libTest.so库文件:

# CMAKE_CXX_FLAGS 会传给c++编译器
# CMAKE_C_FLAGS 会传给c编译器
# CMAKE_SOURCE_DIR 的值是当前CMakelist.txt所在目录
#相当于-L给编译器传查找库文件的目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")

# 相当于用-l给编译器传库名字参数
target_link_libraries( # Specifies the target library.
                       native-lib
                       # libTest.so 可以去掉lib与.so
                       Test
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

上面已经给出了相应注释不在多余解释,到此就可以运行工程了,控制台输入对应信息:

到此我们就自己编译了一个so动态库并在安卓平台使用了动态库中的方法。

编译使用静态库

接下来我们新建staticTest.c文件:

#include <stdio.h>
int staticTest(){
    return 666;
}

我们将staticTest.c编译为静态库,编译静态库需要分两步:
第一步:先将源文件使用gcc编译为.o文件,命令如下:

arm-linux-androideabi-gcc.exe --sysroot=C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\platforms\android-22\arch-arm -lc -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include -isystem C:\Users\wanglei55\Desktop\android-ndk-r17c-windows-x86_64\android-ndk-r17c\sysroot\usr\include\arm-linux-androideabi -fPIC -c C:\Users\wanglei55\Desktop\staticTest.c -o d:\staticTest.o 

接下来使用ar工具将上一步生成的staticTest.o 文件生成libStaticTest.a静态库,命令如下(第一步生成的staticTest.o文件我自己又拷贝到桌面了):

arm-linux-androideabi-ar.exe r d:\libStaticTest.a C:\Users\wanglei55\Desktop\staticTest.o

ar与gcc位于同一目录:

接下来我们就可以将静态库导入安卓工程使用了,静态库不用非得放入jniLibs目录,可以自己决定放入的目录,我放入的目录如下:

然后我们就可以使用其中的int staticTest()方法了:

extern "C"{
    extern int test();
    extern int staticTest();//声明静态库中的方法
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_wanglei55_ndk_MainActivity_stringFromJNI(JNIEnv *env,jobject /* this */) {

    LOGE("libTest.so动态库中test()方法返回值为:%d", test());
    LOGE("libStaticTest.a静态库中staticTest()方法返回值为:%d", staticTest());
    int i = test();
    int j = staticTest();
    std::string s1 = std::to_string(i);
    std::string s2 = std::to_string(j);
    //std::string s2 = "Hello from C++";
    std::string s = s1 +":::"+s2;
    return env->NewStringUTF(s.c_str());
}

最后,通动态库一样,也需要配置导入的静态库目录为了让编译器编译链接的时候能找到静态库,CMakeLists.txt中静态库导入配置如下:

。。。
#引入静态库
# IMPORTED: 表示静态库是以导入的形式添加进来(预编译静态库)
add_library(StaticTest STATIC IMPORTED)
。。。
#设置静态库的导入路径
set_target_properties(StaticTest PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/static/armeabi-v7a/libStaticTest.a)

#生成native-lib动态库需要用到Test StaticTest log动态或者静态库
target_link_libraries( # Specifies the target library.
                       native-lib
                       # libTest.so 可以去掉lib与.so
                       Test
                       StaticTest
                       # Links the target library to the log library
                       # included in the NDK.
                       log )

这样我们就算将静态库引入工程并能正常调用其中方法了:

好了,到此我们经过上述操作将源文件用命令行方式分别生成动态库与静态库并导入安卓工程正常使用了,这都是一些基础方面的知识但是很重要,以后我们使用的三方库很多都是下载源代码,然后自己来生成静态库或者动态库来使用,上面就是演示的这样一个大题流程,那静态库与动态库有什么区别呢?接下来我们讨论一下二者的区别。

五、动态库与静态库的区别

在平时工作中我们经常把一些常用的函数或者功能封装为一个个库供给别人使用,java开发我们可以封装为jar包提供给别人用,安卓平台后来可以打包成aar包,同样的,C/C++中我们封装的功能或者函数可以通过静态库或者动态库的方式提供给别人使用。

Linux平台静态库以.a结尾,而动态库以.so结尾。

那静态库与动态库有什么区别呢?

静态库

程序与静态库连接时,静态库中所有被使用的函数的机器码在编译的时候都被拷贝到最终的可执行文件中,并且会被添加到和它连接的每个程序中:

优点:运行起来会快一些,不用查找其余文件的函数库了。

缺点:导致最终生成的可执行代码量相对变多,运行时, 都会被加载到内存中. 又多消耗了内存空间。

动态库

与动态库连接的可执行文件只包含需要的函数的引用表,而不是所有的函数代码,只有在程序执行时, 那些需要的函数代码才被拷贝到内存中。

优点:生成可执行文件比较小, 节省磁盘空间,一份动态库驻留在内存中被多个程序使用,也同时节约了内存。

缺点:由于运行时要去链接库会花费一定的时间,执行速度相对会慢一些。

静态库是时间换空间,动态库是空间换时间,二者均有好坏。

如果我们要修改函数库,使用动态库的程序只需要将动态库重新编译就可以了,而使用静态库的程序则需要将静态库重新编译好后,将程序再重新编译一遍。

六、总结

本篇我们主要讲解了交叉编译,以及交叉编译出可在安卓平台运行的可执行文件,动态库,静态库,核心是理解整个流程,以及给编译器传递头文件,库文件的查找路径,本篇同样是基础知识部分,但是对于后续我们编译ffmpeg等三方开源库又是十分重要的基础知识,好了,本篇到此为止。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android:JNI与NDK(二)交叉编译与动态库,静态库 的相关文章

  • 数字逻辑练习题(十一)利用74LS161设计一个七进制计数器

    一 题目描述 已知74LS161为同步四位二进制加法计数器 其逻辑符号和功能表如下 请利用74LS161设计一个七进制计数器 应写出分析设计过程 二 问题解答 1 分析 采用同步置数法进行设计
  • mysql for centos_centos7下安装mysql及测试centos

    步骤1 下载并安装MySQL wget http dev mysql com get mysql community release el7 5 noarch rpm rpm ivh mysql community release el7
  • Android studio3.0对于百度地图api开发(8)——百度地图开发思考

    随着对于百度地图SDK的不断深入 对于百度地图的基本操作以及实现 每一块功能就像是一个个工具 他们功能不同 又能相互组合 这就为我们开发者提供了一个很好的平台 在这个平台 开发人员可以进行根据自己的需求进行组装 为了更好的交流 相互学了 我
  • 苹果cmsV10-Dplayer播放器插件整合前置广告、暂停广告

    简介 Dplayer播放器 整合前置广告 暂停广告3 0免费版 很多朋友在用maccms的时候会遇到采集的视频资源存在大量的广告 这款Dplayer播放器不经能去除视频里的垃圾广告 还能站长自己添加广告 播放器整合说明 1 整合的苹果cms
  • 程序员整体架构之开发架构

    开发架构 文章目录 开发架构 概述 前言 互联网发展特点 单体架构 面向服务架构 SOA 水平分层架构 微服务架构 水平拆分 垂直拆分 服务网格架构 中台架构 云原生架构 Serverless 架构 小结 公众号 概述 简述了互联网业务发展
  • springboot的多环境配置(测试,开发,生产)

    众所周知再开发过程中 从开发 测试 上线 至少也得有3个环境 然而每个环境的配置都不一样 例如数据库配置 Redis配置 等各种配置 如果在打包环节来一个一个进行修改配置的话 非常容易出错 对于多环境配置 也有很多的构建工具 而他们的原理基
  • unity3d笔记-Input.GetAxis

    关于Input GetAxis 1 Input GetAxis Horizontal 获得键盘上的A D键 2 Input GetAxis Vertical 获得键盘上的W S键 3 Input GetAxis Mouse x 获得鼠标沿屏
  • 图像紫边消除(depurple)

    图像紫边广泛存在于目前的手机摄像头 数码相机 监控摄像头等数字成像系统所得图像中 当我们使用这些设备在逆光 大光圈等条件下拍摄时 所得图像的局部区域 特别是高反差区域 亮暗对比反差很大的图像区域 比如天空 灯管与物体相接的边缘 会比较容易观
  • 通过h5页面上传视频到Linux服务器

    1 上传视频到本地 https www jb51 net article 132531 htm 2 上传视频到Linux服务器 建立ftp连接 保证服务器已经安装ftp及对应端口 帐号有权限 上传视频 https blog csdn net
  • 基于实数编码的遗传算法搜寻多元函数最值

    遗传算法介绍 遗传算法于20世纪70年代由美国的John holland提出 是一种通过模仿达尔文生物进化理论和遗传机制以寻求问题最优解的启发式算法 算法的运作主要依赖于三大算子 选择 交叉 变异 其算法流程如图1所示 图1 遗传算法流程图
  • 作为一枚python小白如何提升项目实战——Python茅台抢购脚本详细教程

    今天给大家推荐的GitHub开源项目就是一款京东抢茅台的脚本 当然推荐的脚本也是仅用于测试和学习研究 禁止用于商业用途 不能保证其合法性 准确性 完整性和有效性 请根据情况自行判断 主要功能 预约茅台 定时自动预约 秒杀预约后等待抢购 定时
  • Python IO编程详解

    一 文件系统操作 1 os os path和pathlib的对比 Python中处理文件路径和文件系统操作的传统方式 是通过os和os path模块中的函数来完成的 这些函数完全能够胜任需求 但往往会使得代码过于冗长 自Python 3 5
  • java随机生成6位数

    生成6位随机数 仅只有6位 int Math random 9 1 100000 Math Random 函数能够返回带正号的double值 该值大于等于0 0且小于1 0 即取值范围是 0 0 1 0 的左闭右开区间
  • 理解 __declspec(dllexport)和__declspec(dllimport)

    这段时间要把tinyxml从静态库弄成动态库 要用到 declspec dllexport 和 declspec dllimport 来导出dll和lib文件 终于弄明白了export和import的作用 下面从使用的角度来说明一下他们的功
  • 2020北京邮电大学计算机学院复试经验分享

    初试组内第4 复试组内第1 综合第2 已成功上岸 最近大家问我复试的比较多 趁还热乎 在这里给大家分享一下吧 仅供参考 然后初试经验贴在这里 不要因为初试成绩不好就放弃复试或者不认真对待 复试是干嘛的就是用来翻盘的 都坚持了一年了 也不差这
  • C# 获取计算机信息

    文章目录 一 本机信息 1 本机名 2 获得本机MAC地址 3 获得计算机名 4 显示器分辨率 5 主显示器分辨率 6 系统路径 二 操作系统信息 1 操作系统类型 2 获得操作系统位数 3 获得操作系统版本 三 处理器信息 1 处理器个数
  • Sublime Text3设置文本的自动换行

    1 点击Preferences Settings 然后出现以下页面 2 点击保存即可 如果想要修改其他属性 可以直接在Default里面找就可以
  • Java正则表达式验证电话号码

    在注册会员时 经常需要输入电话号码 电话号码是指手机号码或者固定电话 如果输入的内容不合法 则会向用户输出提示 本实例模拟实现电话号码的验证功能 接收用户在控制台输入的电话号码 然后进行判断 并将结果输出 在这里使用 Java正则表达式 一
  • linux改变文件所属用户和组

    1 改变文件所属用户 chown 用户名 文件名 2 改变文件所属组 chgrp 用户名 文件名

随机推荐

  • 狂神说-Mybatis笔记(总)

    环境 JDK1 8 MySQL 8 0 23 maven 3 6 1 IDEA2020 3 框架 需要配置文件 官方中文文档 https mybatis org mybatis 3 zh index html 一 简介 1 什么是Mybat
  • 通俗易懂权限管理模块设计-Java

    最近一直在做CMS系统 发现一些内容其实都是重复出现的 例如权限管理模块 权限管理模块就是为了管理用户是否有权利访问某个权限 如果不能则拒绝访问 其实Java中已经有很成熟的权限管理框架 例如 Shiro Spring Security等
  • 怎么让人物脚贴地 模型_Unity利用FinalIK实现角色脚掌贴着地面行走工具

    using System Collections Generic using UnityEditor using UnityEngine public class FootBones public GameObject Bone1 publ
  • 设计原则学习之里氏替换原则

    以下内容均来自抖音号 it楠老师教java 的设计模式课程 1 原理概述 子类对象 objectofsubtype derivedclass 能够替换程序 program 中父类对象 objectofbase parentclass 出现的
  • C语言程序的undefined,c语言中undefined reference to ""怎么解决

    2 gcc c test c gcc c main c 得到两个 o 文件 一个是 main o 一个是 test o 然后我们链接 o 得到可执行程序 3 gcc o main main o这时 你会发现 报错了 4 main o In
  • python安装pandas失败问题

    开始使用pip install pandas报错 后来将pip语句更换为 pip default time 100 install pandas 成功安装pandas
  • 朋友拿下字节27K的offer,实名羡慕了....

    最近有朋友去字节面试 面试前后进行了20天左右 包含4轮电话面试 1轮笔试 1轮主管视频面试 1轮hr视频面试 据他所说 80 的人都会栽在第一轮面试 要不是他面试前做足准备 估计都坚持不完后面几轮面试 其实 第一轮的电话面试除了一些常规的
  • IBM也下场LLM了,自对齐、高效率的单峰驼Dromedary来了

    近期IBM Research发布了dromedary 并指出这个模型通过一种称为自对齐 SELF ALIGN 的新方法 结合了原则驱动 principle driven 的推理和LLM的生成能力 用于AI代理的自我对齐 使人类的监督最少化
  • 链式调用demo

    所谓链式调用就是调用完一个函数后还能再继续调用其它函数 这样大大减少了代码量 尤其是项目比较大的时候 逻辑集中清晰明了 且易于查看和修改 react hooks处理hooks原理用到了链式调用 fiber memorizedStaste h
  • 费马小定理题

    费马小定理 假如p是质数 且gcd a p 1 那么 A题HDU 4704 首先是挡板法 隔板法 然后用即可 高中数学范围不多叙述 然后得到答案是 这题读入数据大 就算快速幂也肯定TLE 所以用费马小定理 把数据规模降到int 范围内 时间
  • Re: Programming in C with Bluetooth Sockets

    Re Programming in C with Bluetooth Sockets This is the function run by the bluetooth device that will recieve the data c
  • 若依框架注册功能

    后台 逻辑峰的博客 CSDN博客 若依框架登录注册 前台
  • 响应式开发

    响应式开发是指一个网站能够兼容多个终端 不同屏幕分辨率的终端上网页展示方式是不一样的 实现原理 根据用户的行为以及设备的不同 实现页面的不同展示效果 具体的开发过程 1 设置视口标签 width 视口的宽度 device width 设备的
  • Java使用S7协议连接西门子PLC1200、1500

    Java使用S7协议连接西门子PLC1200 1500 1 引入s7包 2 测试代码 可参考使用 1 引入s7包 使用 https github com s7connector s7connector
  • CSDN周赛59期简要题解

    本期题目相对比较友好 而且在比赛报名界面还提示了非编程题考察的章节 诚不欺我 本期非编程题需要选手阅读的章节是第2章 逆向思考 从递推到递归 2 3节 堆栈和队列 遍历的数据结构 选择和判断都考到了栈的数据结构 稍微有点基础知识找出正确答案
  • 21_OpenCV复制矩阵

    本文是关于矩阵复制的相关函数 目录 1 cv repeat 根据需要重复多次复制 2 实现矩阵的转置操作 cv transpose 1 cv repeat 根据需要重复多次复制 函数原型 void cv repeat cv InputArr
  • java.net.SocketTimeoutException: Read timed out问题排查

    欢迎关注博主微信订阅号 问题日志 java sql SQLException I O Error Read timed out at net sourceforge jtds jdbc TdsCore executeSQL TdsCore
  • Windows安装Apache Maven 3.5.4

    一 安装前的准备 1 1 官网下载Apache Maven Maven 3 6 3 此时最新版 的下载地址 https maven apache org download cgi Maven其他版本的下载地址 https archive a
  • 服务器启动显示按f1f2f10,电脑开机提示按f1f2f5 电脑开机要按F1F2F5

    电脑开机要求按F1 F2 F3或F5 有朋友跟我反应说他的XP系统 开机的时候要手动按F1才可以进WIN程序 那怎么改成默认的呢 可以尝试下以下方法 方法一 开启计算机或重新启动计算机后 及时按下 Del 键进入BIOS的设置界面 随便点击
  • Android:JNI与NDK(二)交叉编译与动态库,静态库

    本篇目录 一 前言 本篇主要以window开发环境为背景介绍一下NDK开发中需要掌握的交叉编译等基础知识 选window系统主要是照顾大多数读者 mac linux操作系统基本是同样适用的 交叉编译就是在A平台编译出可以在B平台执行的文件