Android HIDL概述与绑定模式的实现

news2024/11/15 12:33:17

 一、前言

Android O(8.0) 版本之后,底层实现有了比较大的变化,最显著的一个方面就是 HIDL 机制的全面实施。本文对于理解系统源码中 GnssUsbCamera 等模块的工作原理有极大帮助。

二、HIDL 设计目的

Android O(8.0) 之前系统的升级牵扯多方协作,极为麻烦,HIDL机制的推出就是将 frameworkhal 层分开,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译,这样在系统升级时,OEM 厂商 跳过 SoC 厂商,先对 framework 进行升级。

2.1、8.0 之前

frameworkhal 紧紧耦合存在于 system.img 中,因此在版本升级时需要: OEM 厂商适配 frameworkSoC厂商 适配 hal, 之后将修改打包到 system.img,生成 OTA 升级包,推送到手机进行 OTA 升级

2.2、8.0 之后

frameworkhal 进行了解耦, framework 存在于 system.imghal 存在于vendor.img,进行版本升级时,分为两次升级:

  • framework升级 : OEM 厂商适配 framework,将修改打包到 system.img, 生成OTA 升级包,推送到手机进行 OTA 升级(framework 发生改变,hal 层未变)。
  • hal升级 :SoC 厂商适配 hal, 将修改打包到 vendor.img, 生成OTA 升级包,推送到手机进行OTA升级(framework发生改变,hal 层发生改变)。

三、HIDL机制演进

3.1 老版本 Framework 与 HAL 的通信框架

正如上述所言,旧版的系统架构中, Android Framework 层与 Hal 层是打包成一个 system.img 的,且 Framework 与 hal 层之间是紧密耦合的,通过链接的方式使用相应的硬件 so 库。它们之间的架构一般有如下两种方式:

3.2 HIDL 类型介绍

为了解决两者之间这种紧耦合所带来的弊端,google 引入 HIDL 来定义 Framework 与 HAL 之间的接口,可以用下图来描述:

事实上虽然 google 推出了这种机制,但是很多厂商没有很快的跟上节奏,因此为了向前兼容, google 定义了三种类型:

  • ① 是 Treble Project 之前使用的实现架构,使用的是传统 HAL 和旧版 HAL
  • ② 直通模式,passthrough mode。如图所示,Framework 和 HAL 层工作在同一个进程当中,下面的 HAL 是使用 HIDL 封装后的库,是直通式 HAL。这些库文件也可用于 ③ 绑定模式
  • ③ 绑定模式,binderized mode。是直通式 HAL binder 化,变为绑定式 HAL。Framework 和 HAL 层工作在不同的进程,之间通过 Binder 进行 IPC
  • ④ 纯绑定式。相对于 ③ 来说,绑定式 HAL 中并不包含直通式 HAL,因此称为纯绑定式

上述可总结为

上述介绍参考此处

四、HIDL实现

绑定模式 是 google 为了向前兼容而定义的一种类型,且 Android 8.0 及后续版本的设备都必须只支持这种模式。这种模式下 Framework 与 Hal 分别位于不同的进程中,其实从具体实现来讲这种模式也更应该被称为 Binder 化的直通式。下面将通过这种方式实现一个具有加减乘除功能的 HIDL 服务,该服务的名称为 MyTest

4.1 创建IMyTest.hal文件

在系统源码中的 hardware/interfaces 目录下有很多的 HIDL,我们仿照其他 HIDL 来创建自己的目录,在源码根目录执行以下命令:

mkdir -p hardware/interfaces/my_test/1.0/default

之后创建 IMyTest.hal 文件:

touch hardware/interfaces/my_test/1.0/IMyTest.hal

这里定义了四种基本的运算:加、减、乘、除,这是上层调用 HAL 的入口,该文件内容如下:

package android.hardware.my_test@1.0;

interface IMyTest{

    //加法
    add(uint32_t a,uint32_t b) generates (uint32_t result);
    //减法
    sub(uint32_t a,uint32_t b) generates (uint32_t result);
    //乘法
    mul(uint32_t a,uint32_t b) generates (uint32_t result);
    //除法
    div(uint32_t a,uint32_t b) generates (uint32_t result);
    
};

4.2 使用hidl-gen生成HAL相关文件

Google帮我们提供了 hidl-gen 工具来生成 HAL 层相关的代码框架和代码实例,这样子我们只需要关心实现部分。在源码根目录执行以下命令:

source build/envsetup.sh
lunch xxx
PACKAGE=android.hardware.my_test@1.0
LOC=hardware/interfaces/my_test/1.0/default/
make hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

之后执行 update-makefiles.sh 脚本来为 HIDL 生成对应的 Android.bp 文件,源码根目录执行:

./hardware/interfaces/update-makefiles.sh

现在我们的工程目录结构如下:

接下来我们需要再创建两个文件:

touch hardware/interfaces/my_test/1.0/default/android.hardware.my_test@1.0-service.rc
touch hardware/interfaces/my_test/1.0/default/service.cpp

最终我们的工程目录结构如下:

4.3 调用流程分析

上述过程已经将 HIDL 服务所需要的文件创建完成,虽然其中很多文件还没有具体实现,我们先放在一边,先来对整体的调用流程及各个文件的作用略作说明:

Application上层应用
JNI指 framework 层,getService 获取 hal 层 service
android.hardware.my_test@1.0.so接口库,由hardware/interfaces/my_test/1.0/Android.bp 通过 IMyTest.hal 生成,这样只要这个接口库不变,那么 framework 的更新和 hal 层就隔绝开了
android.hardware.my_test@1.0-impl.so实现库,上层应用的最终调用
mytest-hal-serviceservice的名称
android.hardware.my_test@1.0-service.rc设备开机时通过解析 rc 文件启动此服务

 4.4 接口库生成

android.hardware.my_test@1.0.so 由hardware/interfaces/my_test/1.0/Android.bp 通过 IMyTest.hal 生成,该 Android.bp 文件是在上面一系列命令执行之后生成,而接口库是当我们最终执行编译模块时生成,可以说这个过程不需要我们手动参与。

4.5 实现库生成

由 hardware/interfaces/galaxy_one/1.0/default/Android.bp 在最后模块编译时通过 GalaxyOne.cpp 生成,

Android.bp 内容如下:

// FIXME: your file license if you have one

cc_library_shared {
    // FIXME: this should only be -impl for a passthrough hal.
    // In most cases, to convert this to a binderized implementation, you should:
    // - change '-impl' to '-service' here and make it a cc_binary instead of a
    //   cc_library_shared.
    // - add a *.rc file for this module.
    // - delete HIDL_FETCH_I* functions.
    // - call configureRpcThreadpool and registerAsService on the instance.
    // You may also want to append '-impl/-service' with a specific identifier like
    // '-vendor' or '-<hardware identifier>' etc to distinguish it.
    name: "android.hardware.my_test@1.0-impl",
    relative_install_path: "hw",
    // FIXME: this should be 'vendor: true' for modules that will eventually be
    // on AOSP.
    proprietary: true,
    srcs: [
        "MyTest.cpp",
    ],
    shared_libs: [    //可以添加需要的库
        "liblog",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
        "android.hardware.my_test@1.0",
    ],
}

4.6 MyTest.cpp的实现

实现库是通过 MyTest.cpp 编译生成的,现在完善 MyTest.h 和 MyTest.cpp

MyTest.h

Binder化直通式,同样需要将 HIDL_FETCH_XXX 打开

// FIXME: your file license if you have one

#pragma once

#include <android/hardware/my_test/1.0/IMyTest.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <log/log.h>

namespace android::hardware::my_test::V1_0::implementation {

using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct MyTest : public V1_0::IMyTest {
    // Methods from ::android::hardware::my_test::V1_0::IMyTest follow.
    Return<uint32_t> add(uint32_t a, uint32_t b) override;
    Return<uint32_t> sub(uint32_t a, uint32_t b) override;
    Return<uint32_t> mul(uint32_t a, uint32_t b) override;
    Return<uint32_t> div(uint32_t a, uint32_t b) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.

};

// FIXME: most likely delete, this is only for passthrough implementations
extern "C" IMyTest* HIDL_FETCH_IMyTest(const char* name);

}  // namespace android::hardware::my_test::V1_0::implementation

注意,hidl-gen生成的 MyTest.h 的代码中,命名空间不对;将android::hardware::my_test::implementation 改成 android::hardware::my_test::V1_0::implementation

MyTest.cpp

// FIXME: your file license if you have one

#include "MyTest.h"

namespace android::hardware::my_test::V1_0::implementation {

// Methods from ::android::hardware::my_test::V1_0::IMyTest follow.
Return<uint32_t> MyTest::add(uint32_t a, uint32_t b) {
    // TODO implement
	uint32_t result = a + b;
    ALOGE("MyTest::add  a = %d,b = %d,result = %d",a,b,result);
    return uint32_t {};
}

Return<uint32_t> MyTest::sub(uint32_t a, uint32_t b) {
    // TODO implement
	uint32_t result = a - b;
    ALOGE("MyTest::sub  a = %d,b = %d,result = %d",a,b,result);
    return uint32_t {};
}

Return<uint32_t> MyTest::mul(uint32_t a, uint32_t b) {
    // TODO implement
	uint32_t result = a * b;
    ALOGE("MyTest::mul  a = %d,b = %d,result = %d",a,b,result);
    return uint32_t {};
}

Return<uint32_t> MyTest::div(uint32_t a, uint32_t b) {
    // TODO implement
	uint32_t result = a / b;
    ALOGE("MyTest::div  a = %d,b = %d,result = %d",a,b,result);
    return uint32_t {};
}


// Methods from ::android::hidl::base::V1_0::IBase follow.

IMyTest* HIDL_FETCH_IMyTest(const char* /* name */) {
	ALOGE("my_test service init success....");
    return new MyTest();
}
//
}  // namespace android::hardware::my_test::V1_0::implementation

同样需要更改命名空间

4.7 编译

现在除了需要的 rc 文件没有补充、mytest-hal-service 服务没有生成外其余均已配置好了,现在进行编译生成对应的库。进入根目录下执行如下命令:(注意是在刚刚执行过的 source build/envsetup.sh 和 lunch 的窗口下编译,若是新窗口则需要重新执行这两条命令)

mmm  hardware/interfaces/my_test/1.0

编译成功后会在 out\target\product\xxxxxxx\vendor\lib64\hw 下生成 android.hardware.my_test@1.0-impl.so;在 out\target\product\xxxxxxx\system\lib64 下生成 android.hardware.my_test@1.0.so;

4.8 生成Service

接下来我们需要生成对应的 service 可执行文件,这个过程一共分为三步:

1.在 /default 下的 Android.bp 文件中追加如下内容

cc_binary {
    name: "android.hardware.my_test@1.0-service",
    defaults: ["hidl_defaults"],
    relative_install_path: "hw",
    vendor: true,
    srcs: [
        "service.cpp"
    ],
    init_rc: ["android.hardware.my_test@1.0-service.rc"],
    shared_libs: [
		"liblog",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
        "android.hardware.my_test@1.0",
    ],
}

2.补充 service.cpp 

defaultPassthroughServiceImplementation 会帮我们自动注册服务;

#define LOG_TAG "android.hardware.my_test@1.0-service"
 
#include <android/hardware/my_test/1.0/IMyTest.h>
#include <hidl/LegacySupport.h>
#include "MyTest.h"
 
// Generated HIDL files
using android::hardware::my_test::V1_0::IMyTest;
using android::hardware::my_test::V1_0::implementation::MyTest;
 
using android::hardware::defaultPassthroughServiceImplementation;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
 
int main() {
    return defaultPassthroughServiceImplementation<IMyTest>();
} 

3.补充 rc 文件

系统开机会解析该 .rc 文件,启动该服务。这里的 mytest-hal-service 相当于这个服务的别名;如果需要开机自启动,需要配置 seLinux 权限,这里暂不介绍;因此下面我们测试验证的时候通过手动启动的方式启动服务。

service mytest-hal-service /vendor/bin/hw/android.hardware.my_test@1.0-service
    class hal
    user system
    group system

再次在根目录执行如下命令:

mmm  hardware/interfaces/my_test/1.0

完成之后就会得到如下二进制可执行文件:

out\target\product\xxxxxxx\vendor\bin\hw\android.hardware.my_test@1.0-service

4.9 编写测试Client 

经过一系列过程之后,我们得到了三个产物:

1、android.hardware.my_test@1.0.so
2、android.hardware.my_test@1.0-impl.so
3、android.hardware.my_test@1.0-service

接下来模拟一个客户端来测试调用;在 default 目录下创建 test 目录,并新建 client.cpp、Android.bp 文件;创建好后工程目录如下:

client.cpp内容:

#include <android/hardware/my_test/1.0/IMyTest.h>
#include <hidl/Status.h>
#include <log/log.h>

using android::sp;
using android::hardware::my_test::V1_0::IMyTest;
using android::hardware::Return;

int main(){
    android::sp<IMyTest> service = IMyTest::getService();
    if (service == nullptr) {
        ALOGD("faile to get my_test service......");
        return -1;
    }
    ALOGE("success to get my_test service.....");

    uint32_t addResult = service->add(3,4);
    ALOGE("my_test service add: result = %d",(int)addResult);

    uint32_t subResult = service->sub(8,3);
    ALOGE("my_test service sub: result = %d",(int)subResult);

    uint32_t mulResult = service->mul(3,8);
    ALOGE("my_test service mul: result = %d",(int)mulResult);

    uint32_t divResult = service->div(8,2);
    ALOGE("my_test service div: result = %d",(int)divResult);

    return 0;
}

Android.bp内容:

cc_binary {
    name: "my_hidl_test",    //表示生成的 client 名称
    srcs: [
        "client.cpp"
    ],
    shared_libs: [
        "liblog",
        "android.hardware.my_test@1.0",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

根目录执行 mmm  hardware/interfaces/my_test/1.0 编译工程,成功后可在 out\target\product\xxxxxxx\system\bin 目录下找到 my_hidl_test

五、验证

5.1 将产物推入机器

现在我们一共得到 4 个产物,使用 adb 命令将其 push 到车机对应目录下:

android.hardware.my_test@1.0.so             ====》     /vendor/lib64
android.hardware.my_test@1.0-impl.so     ====》     /vendor/lib64/hw
android.hardware.my_test@1.0-service     ====》     /vendor/bin/hw
my_hidl_test                                                ====》     /system/bin

5.2 修改manifest.xml

HIDL 想要被 framework 获取使用还需要在 manifest.xml 中注册,该文件在车机 /vendor/etc/vintf/ 目录下(不同厂商可能不同,以实际情况为准),添加下面的内容:

<hal format="hidl">
		<name>android.hardware.my_test</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IMyTest</name>
            <instance>default</instance>
        </interface>
        <fqname>@1.0::IMyTest/default</fqname>
	</hal>
<hal format="hidl">

5.3 运行Service

手动后台运行

adb root
adb remount
./vendor/bin/hw/android.hardware.my_test@1.0-service &

5.4 运行client

./system/bin/my_hidl_test &

运行后查看系统日志,有如下内容则成功:

01-01 00:01:58.127 13377 13377 E my_hidl_test: success to get my_test service.....
01-01 00:01:58.127 11110 11110 E android.hardware.my_test@1.0-service: MyTest::add  a = 3,b = 4,result = 7
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service add: result = 0
01-01 00:01:58.128 11110 11110 E android.hardware.my_test@1.0-service: MyTest::sub  a = 8,b = 3,result = 5
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service sub: result = 0
01-01 00:01:58.128 11110 11110 E android.hardware.my_test@1.0-service: MyTest::mul  a = 3,b = 8,result = 24
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service mul: result = 0
01-01 00:01:58.129 11110 11110 E android.hardware.my_test@1.0-service: MyTest::div  a = 8,b = 2,result = 4
01-01 00:01:58.129 13377 13377 E my_hidl_test: my_test service div: result = 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1411690.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安全用电管理平台方案介绍——Acrelcloud-6000

安全用电管理平台是一个针对电力系统安全管理的平台&#xff0c;旨在提供对电力设备和用电行为进行监控、分析和管理的解决方案。该平台结合了物联网技术、数据分析和远程监控等技术手段&#xff0c;能够实时监测、分析和预警电力系统的安全状况&#xff0c;以便及时采取措施防…

再谈Android View绘制流程

一&#xff0c;先思考何时开始绘制 笔者在这里提醒读者&#xff0c;Android的View是UI的高级抽象&#xff0c;我们平时使用的XML文件也好&#xff0c;本质是设计模式中的一种策略模式&#xff0c;其View可以理解为一种底层UI显示的Request。各种VIew的排布&#xff0c;来自于开…

KernelGPT: LLM for Kernel Fuzzing

KernelGPT: Enhanced Kernel Fuzzing via Large Language Models 1.Introduction2.Background2.1.Kernel and Device Drivers2.2.Kernel Fuzzing2.2.1.Syzkaller规约2.2.2.规约生成 3.Approach3.1.Driver Detection3.2.Specification Generation3.2.1.Command Value3.2.2.Argum…

Vue2学习之第六、七章——vue-router与ElementUI组件库

路由 理解&#xff1a; 一个路由&#xff08;route&#xff09;就是一组映射关系&#xff08;key - value&#xff09;&#xff0c;多个路由需要路由器&#xff08;router&#xff09;进行管理。前端路由&#xff1a;key是路径&#xff0c;value是组件。 1.基本使用 安装vue-…

springboot快速写接口

1. 建proj形式 name会变成文件夹的名字&#xff0c;相当于你的项目名称 基础包 2. 基础依赖 3. 配置数据库 这里要打开mysql&#xff0c;并且创建数据库 方法&#xff1a; 安装好数据库&#xff0c;改好账号密码用navicat来建表和账号配置properties.yml文件即可 4.用res…

读AI3.0笔记07_游戏与推理

1. 始于游戏&#xff0c;不止于游戏 1.1. 开发超人类的游戏程序并不是人工智能的最终目的 1.2. AlphaGo所有的版本除了下围棋&#xff0c;其他什么也不会 1.2.1. 其最通用的版本AlphaGo Zero也一样 1.3. 这些游戏程序中没有一个能够将其在一款游戏中学到的知识迁移到其他游…

引领未来:云原生在产品、架构与商业模式中的创新与应用

文章目录 一、云原生产品创新二、云原生架构设计三、云原生商业模式变革《云原生落地 产品、架构与商业模式》适读人群编辑推荐内容简介目录 随着云计算技术的不断发展&#xff0c;云原生已经成为企业数字化转型的重要方向。接下来将从产品、架构和商业模式三个方面&#xff0c…

融合创新:传统企业数字化转型的业务、战略、操作和文化变革

引言 随着科技的不断演进&#xff0c;传统企业正站在数字化转型的前沿&#xff0c;这是一场前所未有的全面变革之旅。数字化已经超越了单纯的技术升级&#xff0c;成为企业保持竞争力、开创未来的必然选择。本文将引领您探讨&#xff0c;迈向数字化未来的传统企业全面数字化转…

回归预测 | Matlab基于SSA-SVR麻雀算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于SSA-SVR麻雀算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SSA-SVR麻雀算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于SSA-SVR麻雀算法优化支持向量机的数据…

解密:消息中间件的选择与使用:打造高效通信枢纽

目录 第一章&#xff1a;消息中间件介绍 1.1 什么是消息中间件 1.2 消息中间件的作用 1.3 消息中间件的分类 第二章&#xff1a;消息中间件的选择标准 2.1 性能 2.2 可靠性 2.3 可扩展性 2.4 易用性 2.5 社区支持 2.6 成本 第三章&#xff1a;常见的消息中间件对比…

docker 网络及如何资源(CPU/内存/磁盘)控制

安装Docker时&#xff0c;它会自动创建三个网络&#xff0c;bridge&#xff08;创建容器默认连接到此网络&#xff09;、 none 、host docker网络模式 Host 容器与宿主机共享网络namespace&#xff0c;即容器和宿主机使用同一个IP、端口范围&#xff08;容器与宿主机或其他使…

[ACM学习] 进制转换

进制的本质 本质是每一位的数位上的数字乘上这一位的权重 将任意进制转换为十进制 原来还很疑惑为什么从高位开始&#xff0c;原来从高位开始的&#xff0c;可以被滚动地乘很多遍。 将十进制转换为任意进制

VsCode提高生产力的插件推荐-持续更新中

别名路径跳转 自定义配置// 文件名别名跳转 "alias-skip.mappings": { "~/": "/src", "views": "/src/views", "assets": "/src/assets", "network": "/src/network", "comm…

CNN卷积理解

1 卷积的步骤 1 过滤器&#xff08;卷积核&#xff09;&#xff08;Filter或Kernel&#xff09;&#xff1a; 卷积层使用一组可学习的过滤器来扫描输入数据&#xff08;通常是图像&#xff09;。每个过滤器都是一个小的窗口&#xff0c;包含一些权重&#xff0c;这些权重通过训…

Supervised Contrastive 损失函数详解

有什么不对的及时指出&#xff0c;共同学习进步。(●’◡’●) 有监督对比学习将自监督批量对比方法扩展到完全监督设置&#xff0c;能够有效地利用标签信息。属于同一类的点簇在嵌入空间中被拉到一起&#xff0c;同时将来自不同类的样本簇推开。这种损失显示出对自然损坏很稳…

支付宝AES如何加密

继之前给大家介绍了 V3 加密解密的方法之后&#xff0c;今天给大家介绍下支付宝的 AES 加密。 注意&#xff1a;以下说明均在使用支付宝 SDK 集成的基础上&#xff0c;未使用支付宝 SDK 的小伙伴要使用的话老老实实从 AES 加密原理开始研究吧。 什么是AES密钥 AES 是一种高级加…

k8s实例

k8s实例举例 &#xff08;1&#xff09;Kubernetes 区域可采用 Kubeadm 方式进行安装。 &#xff08;2&#xff09;要求在 Kubernetes 环境中&#xff0c;通过yaml文件的方式&#xff0c;创建2个Nginx Pod分别放置在两个不同的节点上&#xff0c;Pod使用动态PV类型的存储卷挂载…

虚幻UE 插件-像素流送实现和优化

本笔记记录了像素流送插件的实现和优化过程。 UE version&#xff1a;5.3 文章目录 一、像素流送二、实现步骤1、开启像素流送插件2、设置参数3、打包程序4、打包后的程序进行像素流参数设置5、下载NodeJS6、下载信令服务器7、对信令服务器进行设置8、启动像素流送 三、优化1、…

路飞项目--03

总页面 二次封装Response模块 # drf提供的Response&#xff0c;前端想接收到的格式 {code:xx,msg:xx} 后端返回&#xff0c;前端收到&#xff1a; APIResponse(tokneasdfa.asdfas.asdf)---->{code:100,msg:成功,token:asdfa.asdfas.asdf} APIResponse(code101,msg用户不存…

数据结构排序算详解(动态图+代码描述)

目录 1、直接插入排序&#xff08;升序&#xff09; 2、希尔排序&#xff08;升序&#xff09; 3、选择排序&#xff08;升序&#xff09; 方式一&#xff08;一个指针&#xff09; 方式二&#xff08;两个指针&#xff09; 4、堆排序&#xff08;升序&#xff09; 5、冒…