AndroidAutomotive模块介绍(二)应用及接口介绍

news2025/1/11 19:52:21

前言

上一篇文章中从整体角度描述了 Android Automotive 模块。本篇文章将对 Android Automotive 中的 APP 以及 API 部分展开描述。
上一篇:AndroidAutomotive模块介绍(一)整体介绍
下一篇:AndroidAutomotive模块介绍(三)CarService服务

正文

1、CarServiceApp

Android Automotive 为系统定制了一些专门适用车载系统的应用,以代替传统的手机应用模块。

1.1 代码路径

代码路径为:/android/packages/apps/Car

1.2 应用

代码路径应用如下所示:

ubuntu16-017:/android/packages/apps/Car$ ls -l
total 76
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Cluster
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Dialer
drwxrwxr-x  3  domain users 4096 Jun  7  2022 externallibs
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Hvac
drwxrwxr-x  5  domain users 4096 Nov  9  2022 LatinIME
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Launcher
drwxrwxr-x  5  domain users 4096 Nov  9  2022 LensPicker
drwxrwxr-x 18  domain users 4096 Nov  9  2022 libs
drwxrwxr-x  5  domain users 4096 Nov  9  2022 LinkViewer
drwxrwxr-x  6  domain users 4096 Nov  9  2022 LocalMediaPlayer
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Media
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Messenger
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Notification
drwxrwxr-x  3  domain users 4096 Jun  7  2022 Overview
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Radio
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Settings
drwxrwxr-x  3  domain users 4096 Jun  7  2022 Stream
drwxrwxr-x  5  domain users 4096 May 11  2023 SystemUpdater
drwxrwxr-x  5  domain users 4096 Nov  9  2022 tests

原生提供了许多应用,介绍如下:

应用描述
Cluster仪表板
Dialer电话
Hvac空调
LatinIME输入法
Launcher桌面
LensPicker活动窗口选择应用(Launcher)
LinkViewer二维码
LocalMediaPlayer本地播放器
Media媒体应用
Messenger消息管理应用(消息及TTS)
Notification通知
Overview音源声卡选择应用
Radio收音机
Settings设置
Stream音源管理应用
SystemUpdater系统升级
test测试
libs支持库

2、CarServiceApi

2.1 代码路径

CarServiceApi 代码路径为 /android/packages/services/Car/car-lib。

ubuntu16-017:/android/packages/services/Car/car-lib$ ls -l
total 32
-rw-r--r-- 1  domain users 7494 Nov  9  2022 Android.bp
-rw-r--r-- 1  domain users  932 Nov  9  2022 AndroidManifest.xml
-rw-r--r-- 1  domain users 1535 Nov  9  2022 Android.mk
drwxrwxr-x 4  domain users 4096 Nov  9  2022 api
drwxrwxr-x 4  domain users 4096 Jun  7  2022 native
drwxrwxr-x 4  domain users 4096 Jun  7  2022 src
drwxrwxr-x 3  domain users 4096 Jun  7  2022 src_feature_future

如上所示,Android.bp 是 car-lib 的编译文件;AndroidManifest.xml 是编译配置文件;Android.mk 描述了添加 car-lib 到系统编译中;api 文件夹中描述了 car-lib 所支持的接口描述 txt 文件;native 文件夹中保存了 car-lib 所需的 libcarpowermanager 库,关于 CarPower 模块的回调信息;src 中描述了 car-lib 提供的接口代码;src_feature_future 文件夹下主要保存 FeatureConfiguration.java 文件,以静态变量的方式保存有关 feature 的默认状态。

2.2 Api 介绍

2.2.1 编译
2.2.1.1 编译命令

编译 Carlib 有三种不同的指令

2.2.1.1.1 make android.car

android.car 编译出的 Carlib 库包含 Car API 中定义的所有方法和实现细节。

编译结果保存到:/out/target/common/obj/JAVA_LIBRARIES/android.car_intermediates 路径下。

2.2.1.1.2 make android.car-system-stubs

android.car-system-stubs 包含了 Car 服务端的 API 接口定义,但不包含实际的实现代码。主要用于在开发 Car 服务时进行编译和链接,以便在编译时检查代码的正确性。确保 Car 服务与 Car 应用之间的通信接口正确对接。

2.2.1.1.3 make android.car-stubs

make android.car-stubs 包含了 Car 应用的 API 接口定义,但并不包含实际的实现代码。主要用于在开发 Car 应用时进行编译和链接,以便在编译时检查代码的正确性。确保 Car 应用与 Car 服务之间的通信接口正确对接。

2.2.1.2 编译文件

下面通过 Android.bp 文件分析下 car-lib 编译逻辑:

// 编译 libcarpowermanager 静态库,主要编译文件是 native 目录下的 CarPowerManager 相关文件
cc_library {
    name: "libcarpowermanager",

    aidl: {
        export_aidl_headers: true,
        local_include_dirs: [
            "src",
        ],
    },

    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
        "-Wno-unused-parameter",
    ],

    include_dirs: [
        "packages/services/Car/car-lib/native/include",
    ],

    shared_libs: [
        "libbinder",
        "liblog",
        "libutils",
    ],

    srcs: [
        "src/android/car/ICar.aidl",
        "src/android/car/hardware/power/ICarPower.aidl",
        "src/android/car/hardware/power/ICarPowerStateListener.aidl",
        "native/CarPowerManager/CarPowerManager.cpp",
    ],
}

java_library {
    name: "android.car.cluster.navigation",
    proto: {
        type: "lite",
    },
    static_libs: ["libprotobuf-java-lite"],
    srcs: ["src/android/car/navigation/navigation_state.proto"]
}

// android.car 库,是 Carlib API 库
java_library {
    name: "android.car",
    srcs: [
        "src/**/*.java",
        "src_feature_future/**/*.java",
        "src/**/I*.aidl",
    ],
    static_libs: ["android.hardware.automotive.YFvehicle-V2.0-java"],
    aidl: {
        include_dirs: [
            "system/bt/binder",
        ],
    },
    exclude_srcs: [
        "src/android/car/storagemonitoring/IoStats.aidl",
        "src/android/car/storagemonitoring/IoStatsEntry.aidl",
    ],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    installable: true,
}

// android.car-stubs 库,Car 应用端的 API 接口定义,不包含实现代码逻辑。
java_library_static {
    name: "android.car-stubs",
    srcs: [
        ":android.car-stub-docs",
    ],
    libs: [
        "android.car",
    ],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    compile_dex: true,
    dist: {
        targets: ["dist_files"],
    }
}

// android.car-system-stubs 库。Car 服务端 API 接口定义,不包含实现代码逻辑。
java_library_static {
    name: "android.car-system-stubs",
    srcs: [
        ":android.car-system-stubs-docs",
    ],
    libs: [
        "android.car",
    ],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    compile_dex: true,
    dist: {
        targets: ["dist_files"],
    }
}

在源码中添加 AndroidAutomotive 相关编译,包括 APP、Jar 包、CarService 服务等,编译文件在 /android/packages/services/Car/car_product/build/car.mk 文件中。

# Automotive specific packages 添加 Automotive 应用编译
PRODUCT_PACKAGES += \
    CarFrameworkPackageStubs \
    CarService \
    CarDialerApp \
    OverviewApp \
    CarLauncher \
    CarSystemUI \
    LocalMediaPlayer \
    CarMediaApp \
    CarMapsPlaceholder \
    CarSettings \
    android.car \
    car-frameworks-service \
    com.android.car.procfsinspector \
    libcar-framework-service-jni \
    CarHvacApp \
    
# should add to BOOT_JARS only once 添加 Carlib Jar 包编译
ifeq (,$(INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS))
PRODUCT_BOOT_JARS += \
    android.car

PRODUCT_HIDDENAPI_STUBS := \
    android.car-stubs

PRODUCT_HIDDENAPI_STUBS_SYSTEM := \
    android.car-system-stubs

PRODUCT_HIDDENAPI_STUBS_TEST := \
    android.car-test-stubs
2.2.2 Api 分类

Android Automotive 的 carlib 是一个用于 Car 应用开发的库,提供了一系列 API 接口供开发者使用。

CarApi 类图如下所示:
在这里插入图片描述

下表是所有 Car-lib API 接口描述:

接口类说明
CarAudioManager用于管理车辆音频统的接口,可以控制音量、音频输入输出等功能
CarSensorManager用于访问车辆传感器数据的接口,可以获取车辆的加速度、转向角度、车辆倾斜角度等传感器数据
CarInfoManager用于获取车辆信息的接口,可以获取车辆的VIN码、车辆速度、车辆位置等信息
CarDiagManager用于控制汽车EOL/Diag系统的接口
CarAppFocusManager设置和监听当前应用焦点的接口
CarPackageManager提供专用的和包管理相关的接口
CarNavigationStatusManager为仪表盘提供导航状态的接口
CarCabinManager控制汽车座舱系统的接口(CarPropertyManager 代替)
CarDiagnosticManager监控诊断数据的接口
CarHvacManager控制空调系统的接口(CarPropertyManager 代替)
CarPowerManager接收电源状态变化的接口
CarProjectionManager投屏管理接口
CarPropertyManager与车辆属性交互的接口
CarVendorExtensionManager访问厂商扩展车辆属性的接口(CarPropertyManager 代替)
CarInstrumentClusterManager配合仪表控制的接口(CarPropertyManager 代替)
CarTestManagerBinderWrapper仅用于系统测试
VmsSubscriberManager供地图服务订阅者使用的接口
CarBluetoothManager提供和特定的汽车蓝牙管理器交互的接口
CarStorageMonitoringManager检索闪存信息的接口
CarDrivingStateManager获取与驾驶状态相关信息的接口
CarUxRestrictionsManager获取驾驶过程中用户体验限制的接口
CarConfigurationManager显示存储在系统中的车辆配置值的接口
CarTrustAgentEnrollmentManager授权可信任设备的接口
CarMediaManager接收媒体音源变化的接口
CarBugreportManager报告错误的接口

Car-lib 常用的接口类有:

  • CarApiManager:

用于管理Car API的接口,可以通过该接口获取Car的各种功能模块,如 CarInfoManager、 CarSensorManager 等。

  • CarInfoManager:

用于获取车辆信息的接口,可以获取车辆的VIN码、车辆速度、车辆位置等信息。

  • CarSensorManager:

用于访问车辆传感器数据的接口,可以获取车辆的加速度、转向角度、车辆倾斜角度等传感器数据。

  • CarAudioManager:

用于管理车辆音频系统的接口,可以控制音量、音频输入输出等功能。

  • CarNavigationManager:

用于车辆导航功能的接口,可以获取车辆当前位置、目的地信息等。

  • CarPackageManager:

用于管理Car应用包的接口,可以安装、卸载、启动 Car 应用。

  • CarProjectionManager:

用于管理车辆投影功能的接口,可以控制车辆的投影屏幕显示。

  • CarInputManager:

用于处理车辆输入事件的接口,可以处理车辆上的按键事件、触摸事件等。

2.3 Api 使用方法

CarService 中各个服务本质上是 AIDL 接口的实现类,属于 Server 端,而对应的 Client 端就需要一个 IBinder 对象来访问 Server 端的方法,这些 IBinder 对象在 Car API 中被封装在一个个 XXXManager 类中。

Car API 与 CarService 中的服务,名称上存在对应关系,所以很好理解。例如:CarWatchdogManager 对应 CarWatchdogService,CarMediaManager 对应 CarMediaService。

不过也有例外:CarInfoManager、CarSensorManager、CarHvacManager、CarCabinManager、CarVendorExtensionManager 都对应 CarPropertyService。但是在 Android 11 中这些 Manager 都已经过时,Google 建议统一使用 CarPropertyManager。

对于 Car SDK 的调用,在 Android 9 与 Android 10 版本略有不同。

2.3.1 Car 类

Car 类是 Android Automotive 的 API 接口,Car 是 Car-lib 总功能的入口,Car 类中包含许多的 Manager,通过 getCarManager(String) 获取 Car-lib 提供的接口对象。

常用方法如下:

方法描述
public static Car createCar(Context context)获取Car实例
public static Car createCar(Context context, @Nullable Handler handler)获取Car实例
public static Car createCar(@NonNull Context context, @Nullable Handler handler, long waitTimeoutMs, @NonNull CarServiceLifecycleListener statusChangeListener)获取Car实例
public Object getCarManager(String serviceName)获取子Manager
public boolean isConnected()判断是否和CarService保持连接
public void disconnect()断开和CarService的连接
2.3.1.1 createCar()

createCar() 函数是应用创建 Car 对象的调用方法,这里来分析下 createCar() 函数的代码流程,下面是 /android/packages/services/Car/car-lib/src/android/car/Car.java 文件内容

    public static Car createCar(Context context, @Nullable Handler handler) {
        Log.i(TAG_CAR, "createCar");
        Car car = null;
        IBinder service = null;
        boolean started = false;
        int retryCount = 0;
        while (true) {
            service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
            if (car == null) {
                // service can be still null. The constructor is safe for null service.
                car = new Car(context, ICar.Stub.asInterface(service),
                        null /*serviceConnectionListener*/, null /*statusChangeListener*/, handler);
            }
            if (service != null) {
                if (!started) {  // specialization for most common case.
                    return car;
                }
                break;
            }
            if (!started) {
                car.startCarService();
                started = true;
            }
            retryCount++;
            if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) {
                Log.e(TAG_CAR, "cannot get car_service, waited for car service (ms):"
                                + CAR_SERVICE_BINDER_POLLING_INTERVAL_MS
                                * CAR_SERVICE_BINDER_POLLING_MAX_RETRY,
                        new RuntimeException());
                return null;
            }
            try {
                Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);
            } catch (InterruptedException e) {
                Log.e(CarLibLog.TAG_CAR, "interrupted while waiting for car_service",
                        new RuntimeException());
                return null;
            }
        }
        // Can be accessed from mServiceConnectionListener in main thread.
        synchronized (car) {
            if (car.mService == null) {
                car.mService = ICar.Stub.asInterface(service);
                Log.w(TAG_CAR,
                        "waited for car_service (ms):"
                                + CAR_SERVICE_BINDER_POLLING_INTERVAL_MS * retryCount,
                        new RuntimeException());
            }
            car.mConnectionState = STATE_CONNECTED;
        }
        return car;
    }

1、通过一个 while 循环一直去创建 Car 实例,并且建立与 CarService 服务的连接,直到实例化成功或者创建次数超过最大限制 CAR_SERVICE_BINDER_POLLING_MAX_RETRY(100)。

2、service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);首先获取CarService的服务实例

3、car = new Car(context, ICar.Stub.asInterface(service), null , null, handler); 这里前面方法中声明,所以判断一定是null,会new一个Car实例。

4、car.startCarService(); 这里实际是去用Bind的方式启动 CarService。

5、if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) { 这里会有个判断重试的次数,如果超过次数还没取到 CarService 服务,则会返回 null。sleep 的时间是 50ms,重试 100 次,也就是获取 Car 实例最长时间长达 5 s。

2.3.1.2 startCarService()

startCarService() 函数实际是调用 Context.bindServiceAsUser() 函数去启动 CarService 服务。

下面是 startCarService() 函数逻辑:

    private void startCarService() {
        Intent intent = new Intent();
        intent.setPackage(CAR_SERVICE_PACKAGE);
        intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
        boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
                Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
        Log.i(TAG_CAR, "bindServiceAsUser bound" +bound);
        synchronized (mLock) {
            if (!bound) {
                Log.i(TAG_CAR, "bindServiceAsUser 1");
                mConnectionRetryCount++;
                if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
                    Log.w(TAG_CAR, "cannot bind to car service after max retry");
                    mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
                } else {
                Log.i(TAG_CAR, "bindServiceAsUser 2");
                    mEventHandler.postDelayed(mConnectionRetryRunnable,
                            CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
                }
            } else {
                Log.i(TAG_CAR, "bindServiceAsUser 3");
                mEventHandler.removeCallbacks(mConnectionRetryRunnable);
                mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
                mConnectionRetryCount = 0;
                mServiceBound = true;
            }
        }
    }

如果调用 Context.bindServiceAsUser() 启动 CarService 服务失败,会重试连接 CAR_SERVICE_BIND_MAX_RETRY (20) 次。超过尝试次数后会调用 mConnectionRetryFailedRunnable。

2.3.1.3 connect()

connect() 方法实际就是调用 startCarService() 方法,因为 connect() 是公开的,startCarService() 是私有的。connect() 函数只能调用一次,如果当前已经处于连接状态,再次调用 connect() 函数会抛 IllegalStateException 异常,client 没有捕获该异常,则会引起 client 程序的崩溃。

    public void connect() throws IllegalStateException {
        synchronized (mLock) {
            if (mConnectionState != STATE_DISCONNECTED) {
                throw new IllegalStateException("already connected or connecting");
            }
            mConnectionState = STATE_CONNECTING;
            startCarService();
        }
    }

connect() 已经弃用了,我们知道 startCarService() 就是和 CarService 建立连接,因为在 CreatCar() 里已经保证获取到 CarService,所以 connect() 不在必要。

2.3.2 示例

通过 Car.createCar() 函数可以创建 Car 对象,参数需要传入 ServiceConnection,并在 service 连接成功后,获取想要的 Manager 实例,调用 connect() 函数,当与 CarService 连接成功后,会回调 ServiceConnection 对象的 onServiceConnected() 函数完成连接。如下所示:

private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);
            } catch (CarNotConnectedException e) {
                Log.e(TAG, "Car not connected in onServiceConnected");
            }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};

...
Car carApiClient = Car.createCar(context, mCarServiceConnection);
carApiClient.connect();
...

但是在 Android 10 以后,Google 改写了 Car API 的使用方式,Android 10 以后构建 Car 对象不再建议传入 ServiceConnection 对象,而是使用了下面的方法。

Car carApiClient = Car.createCar(context);
CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);

Android 10 以后 Car API 的调用方式由异步方法改为了同步方法,保留了 disconnect(),但是不需要调用 connect(),这样使用起来更加简单。

这种调用方式是 CarService 不发生异常的情况下,Client 端与 CarService 的连接也不会断开,但是如果 CarService 发生异常,Client 端会被直接杀死,使用如下方式可以避免:

Car car = Car.createCar(this, workThreadHandler, 2000, new Car.CarServiceLifecycleListener() {
    @Override
    public void onLifecycleChanged(@NonNull Car car, boolean ready) {
        // ready 在Service断开连接时会变为false
if (ready) {
            
        } else {
            // CarService 发生异常或连接被断开了,需要client端处理。
}
    }
});

通过创建 Car 对象时传入 CarServiceLifecycleListener 对象,注册回调的方式,通过回调以知道 CarService 的生命周期。

3、总结

本篇文档介绍了 AndroidAutomotive 架构中 APP 应用与 API 接口等内容,对于应用分类、功能、代码;API 接口介绍、初始化、使用方法等。

上一篇:AndroidAutomotive模块介绍(一)整体介绍
下一篇:AndroidAutomotive模块介绍(三)CarService服务

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

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

相关文章

GitHub repository - Branch - SSH clone URL - Clone in Desktop - Download ZIP

GitHub repository - Branch - SSH clone URL - Clone in Desktop - Download ZIP 1. Branch2. SSH clone URL3. Clone in Desktop4. Download ZIPReferences 1. Branch 显示当前分支的名称。从这里可以切换仓库内分支,查看其他分支的文件。 2. SSH clone U…

JavaWeb--JavaScript-事件绑定/BOM/DOM编程

目录 1. 事件绑定 1.1. 什么是事件 1.2. 常见事件 1.3. 事件的绑定 1.3.1. 属性绑定 1.3.2. DOM编程绑定 1.4. 事件的触发 1.4.1. 行为触发 1.4.2. DOM编程触发 2. BOM 编程 2.1. 什么是 BOM 2.2. window对象的常见属性(了解) 2.3. window对象的常见方法(了解) 2…

京东详情比价接口优惠券(2)

京东详情API接口在电子商务中的应用与作用性体现在多个方面,对于电商平台、商家以及用户都带来了显著的价值。 首先,从应用的角度来看,京东详情API接口为开发者提供了一整套丰富的功能和工具,使他们能够轻松地与京东平台进行交互。…

【机器学习】Logistic与Softmax回归详解

在深入探讨机器学习的核心概念之前,我们首先需要理解机器学习在当今世界的作用。机器学习,作为人工智能的一个重要分支,已经渗透到我们生活的方方面面,从智能推荐系统到自动驾驶汽车,再到医学影像的分析。它能够从大量…

16路HDMI+AV流媒体IPTV高清编码器JR-3216HD

产品简介: JR-3216HD 16路高清HDMIAV编码器是专业的高清音视频编码产品,该产品具有支持16路高清HDMI音视频采集功能,16路标清AV视频采集功能,16路3.5MM独立外接音频输入,编码输出双码流H.264格式,音频MP3/…

视频号小店究竟有什么秘密,值得商家疯狂入驻,商家必看!

大家好,我是电商花花。 我们都知道视频号和抖音本身都是一个短视频平台,但是随着直播电商的发展,背后的流量推动逐步显露出强大的红利市场和变现机会。 视频号小店流量大和赚钱之外,还非常适合普通人创业。 这也使得越来越多的…

访问者模式【行为模式C++】

1.概述 访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。 访问者模式主要解决的是数据与算法的耦合问题,尤其是在数据结构比较稳定,而算法多变的情况下。为了不污染数据本身,访问者会将多种算法独立归档&…

SpringBoot中使用Jackson序列化返回

SpringBoot中使用Jackson序列化返回 在Spring Boot应用中,使用Jackson库来处理JSON的序列化和反序列化是一种常见的做法。Jackson是一个高效的JSON处理器,广泛用于Java环境中,尤其是在与Spring框架集成时。本文将详细介绍如何在Spring Boot中…

Leetcode算法训练日记 | day24

一、组合问题 1.题目 Leetcode:第 77 题 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n 4, k 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4…

汽车咨询|基于SprinBoot的汽车资讯管理系统设计与实现(源码+数据库+文档)

汽车资讯管理系统目录 基于SprinBoot的汽车资讯管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师&#xff…

关于 Amazon DynamoDB 的学习和使用

文章主要针对于博主自己的技术栈,从Unity的角度出发,对于 DynamoDB 的使用。 绿色通道: WS SDK for .NET Version 3 API Reference - AmazonDynamoDBClient Amazon DynamoDB Amazon DynamoDB is a fast, highly scalable, highly available,…

全景剖析SSD SLC Cache缓存设计原理-2

四、SLC缓存对SSD的寿命是否有优化? 当使用QLC或TLC NAND闪存并将其切换到SLC模式进行写入时,会对闪存的寿命产生以下影响: 短期寿命提升: SLC模式下,每个存储单元仅存储一个比特数据,相对于QLC或TLC来说…

matlab学习002-函数及流程控制语句

目录 一,matlab编程基础 1)matlab脚本和函数文件 ①脚本文件 ②函数文件 2)函数的定义和调用 ①定义 ②调用 3)程序流程控制 ①使用for求 122^2……2^622^63之和 ②使用while语句求122^2……2^622^63之和 ③使用matl…

【御控物联】 Java JSON结构转换(2):对象To对象——属性重组

文章目录 一、JSON结构转换是什么?二、案例之《JSON对象 To JSON对象》三、代码实现四、在线转换工具五、技术资料 一、JSON结构转换是什么? JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换,生成新的JS…

【Python】实现导入、提交文件并显示其路径的基础GUI界面

The tkinter package (“Tk interface”) 是一个基于Tcl/Tk GUI工具标准的Python接口。集合在大多数操作系统都有Tk和tkinter 库,包括MacOS,Window还有一些Unix类的操作系统 【基础操作】 1 设置窗口 # -*- coding: utf-8 -*- from tkinter import *#创…

2024年认证杯SPSSPRO杯数学建模D题(第一阶段)AI绘画带来的挑战全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 D题 AI绘画带来的挑战 原题再现: 2023 年开年,ChatGPT 作为一款聊天型 AI 工具,成为了超越疫情的热门词条;而在 AI 的另一个分支——绘图领域,一款名为 Midjourney(MJ&…

Matlab调C/C++简单模板例子

如果你是需要快速搭建一个matlab调c/c环境,这篇文章可以参考 有了c代码,想在matlab里面调用,可以参考我这个模板 matlab调用代码: clear all close all clcinput1 1; input2 2;[output1,output2] mexfunction(input1,input2);…

充电有喜分析

参考相关应用 华为市场 -> 充电有喜2.0.1网络查找 -> 充电有喜1.0.0 http://www.xz7.com/downinfo/609008.html反编译app有个文字漏洞,找到 来福充电宝 https://sj.qq.com/appdetail/com.evenhaexplo.courte 需要解决的问题 电源插拔注册为什么需要悬浮窗权…

Linux C柔性数组(零长数组)

零长数组&#xff0c;大小为0&#xff0c;一般用在结构体中&#xff08;网络通信&#xff0c;省流&#xff09;&#xff0c;节省空间&#xff0c;方便善后&#xff08;相对于指针类型&#xff09;&#xff0c;我们通过具体例子进行理解。 常规定长数组 #include <stdio.h&…

[温故] 红黑树算法

前言 最近在突然想起一些基础的东西, 向着温故知新, 有了些新的感悟和大家分享一下. 排序算法是数据结构的一个重要组成部分, 当时学习的时候没有少折腾, 这里来看看大佬们怎么运用这些数据结构来构建庞大的计算机体系的. 二叉树是排序算法的一个衍生, 基于二叉树的构建不同…