Android 车载应用开发指南 - CarService 详解(下)
车载应用正在改变人们的出行体验。从导航到娱乐、从安全到信息服务,车载应用的开发已成为汽车智能化发展的重要组成部分。而对于开发者来说,如何将自己的应用程序无缝集成到车载系统中,利用汽车的硬件和服务能力,是一个极具挑战性的话题
那么,在Android平台上开发车载应用时,CarService究竟扮演了什么样的角色?它的功能和使用方法有哪些关键点?
随着车联网技术的普及,车载应用的种类和功能也在迅速增加,从简单的导航和音乐播放,到智能语音助手、驾驶行为分析等多样化的服务。CarService作为Android车载应用开发的基础组件,为开发者提供了与车辆深度交互的能力。在未来,车载应用的智能化、个性化将进一步提升驾驶体验,并成为汽车产品竞争的重要元素。

03 CarService 实现原理
想要弄清楚CarService实现方式,首先需要搞明白CarService的启动流程。
CarService 启动流程主要分为以下四个步骤:
-
SystemServer 启动 CarServiceHelperService 服务
-
在调用 startService() 后,CarServiceHelperService 的onStart() 方法通过 bindService 的方式启动 CarService(一个系统级别的 APK,位于 system/priv-app)
-
启动 CarService 后首先调用 onCreate(),创建 ICarImpl 对象并初始化,在此时创建了一系列 Car 相关的核心服务,并遍历 init 初始化
-
然后调用 onBind 将该 ICarImpl 对象返回给CarServiceHelperService,CarServiceHelperService 在内部的一个 Binder 对象 ICarServiceHelperImpl传递给 CarService,建立双向跨进程
3.1 启动 CarServiceHelperService 服务
SystemServer会在startOtherServices()方法中让SystemServiceManager先通过反射的形式创建出StartCarServiceHelperService对象。
-
源码路径:frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...// 仅在 automotive 中启动if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {t.traceBegin("StartCarServiceHelperService");final SystemService cshs = mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);if (cshs instanceof Dumpable) {mDumper.addDumpable((Dumpable) cshs);}if (cshs instanceof DevicePolicySafetyChecker) {dpms.setDevicePolicySafetyChecker((DevicePolicySafetyChecker) cshs);}t.traceEnd();}...}
然后在SystemServiceManager中调用StartCarServiceHelperService的onStart()方法。
CarServiceHelperService是CarService的 SystemService 端的配套服务。
-
源码路径:
frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
public SystemService startService(String className) {final Class<SystemService> serviceClass = loadClassFromLoader(className,this.getClass().getClassLoader());return startService(serviceClass);}public void startService(@NonNull final SystemService service) {// Register it.mServices.add(service);long time = SystemClock.elapsedRealtime();try {service.onStart();} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + service.getClass().getName()+ ": onStart threw an exception", ex);}warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");}
3.2 绑定 CarService 服务
-
源码路径:frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java
private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";@Overridepublic void onStart() {EventLog.writeEvent(EventLogTags.CAR_HELPER_START);IntentFilter filter = new IntentFilter(Intent.ACTION_REBOOT);filter.addAction(Intent.ACTION_SHUTDOWN);mContext.registerReceiverForAllUsers(mShutdownEventReceiver, filter, null, null);mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);mCarWatchdogDaemonHelper.connect();Intent intent = new Intent();intent.setPackage("com.android.car"); // 绑定包名,设置广播仅对该包有效intent.setAction(CAR_SERVICE_INTERFACE); // 绑定 action,表明想要启动能够响应设置的这个 action 的活动,并在清单文件 AndroidManifest.xml 中设置 action 属性// 绑定后回调if (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,mHandler, UserHandle.SYSTEM)) {Slogf.wtf(TAG, "cannot start car service");}loadNativeLibrary();}
-
源码路径:packages/services/Car/service/AndroidManifest.xml
sharedUserId 是系统级别的,类似 SystemUI,它编译出来同样是一个 APK 文件
-
设备文件路径:/system/priv-app/CarService/CarService.apk
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"package="com.android.car"coreApp="true"android:sharedUserId="android.uid.system">......<application android:label="@string/app_title"android:directBootAware="true"android:allowBackup="false"android:persistent="true"><service android:name=".CarService"android:singleUser="true"android:exported="true"><intent-filter><action android:name="android.car.ICar"/></intent-filter></service>......</application>
3.3 CarService 初始化
CarService进入启动时序后,会在onCreate()方法中进行一系列自身的初始化操作,步骤如下:
1)通过 HIDL 接口获取到 HAL 层的 IHwBinder 对象IVehicle,与 AIDL 的用法类似,必须持有 IHwBinder 对象我们才可以与 Vehicle HAL 层进行通信。
2)创建 ICarImpl 对象,并调用init方法,它就是ICar.aidl接口的实现类,我们需要通过它才能拿到其他的 Service 的 IBinder 对象。
3)将ICar.aidl的实现类添加到 ServiceManager 中。
4)设定 SystemProperty,将CarService设定为创建完成状态,只有包含CarService在内的所有的核心 Service 都完成初始化,才能结束开机动画并发送开机广播。
-
源码路径:packages/services/Car/service/src/com/android/car/CarService.java
@Overridepublic void onCreate() {LimitedTimingsTraceLog initTiming = new LimitedTimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG,Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);initTiming.traceBegin("CarService.onCreate");initTiming.traceBegin("getVehicle");// 获取 hal 层的 Vehicle servicemVehicle = getVehicle();initTiming.traceEnd();...//创建 ICarImpl 实例mICarImpl = new ICarImpl(this,mVehicle,SystemInterface.Builder.defaultSystemInterface(this).build(),mVehicleInterfaceName);//然后调用 ICarImpl 的 init 初始化方法mICarImpl.init();linkToDeath(mVehicle, mVehicleDeathRecipient);//将该 service 注册到 ServiceManagerServiceManager.addService("car_service", mICarImpl);//设置 boot.car_service_created 属性SystemProperties.set("boot.car_service_created", "1");super.onCreate();initTiming.traceEnd(); // "CarService.onCreate"}@Nullableprivate static IVehicle getVehicle() {final String instanceName = SystemProperties.get("ro.vehicle.hal", "default");try {//该 service 启动文件 hardware/interfaces/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rcreturn android.hardware.automotive.vehicle.V2_0.IVehicle.getService(instanceName);} catch (RemoteException e) {Slog.e(CarLog.TAG_SERVICE, "Failed to get IVehicle/" + instanceName + " service", e);} catch (NoSuchElementException e) {Slog.e(CarLog.TAG_SERVICE, "IVehicle/" + instanceName + " service not registered yet");}return null;}
接着再看ICarImpl的实现,如下所示:
1)创建各个核心服务对象
2)把服务对象缓存到 CarLocalServices 中,这里主要是为了方便 Service 之间的相互访问
-
源码路径:
/packages/services/Car/service/src/com/android/car/ICarImpl.java
@VisibleForTestingICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,String vehicleInterfaceName,@Nullable CarUserService carUserService,@Nullable CarWatchdogService carWatchdogService,@Nullable ICarPowerPolicySystemNotification powerPolicyDaemon) {...mContext = serviceContext;mSystemInterface = systemInterface;CarLocalServices.addService(SystemInterface.class, mSystemInterface);//创建 VehicleHal 对象mHal = constructWithTrace(t, VehicleHal.class,() -> new VehicleHal(serviceContext, vehicle));...// 创建核心服务对象,并缓存到 CarLocalServicesmCarPropertyService = constructWithTrace(t, CarPropertyService.class, () -> new CarPropertyService(serviceContext, mHal.getPropertyHal()));mCarDrivingStateService = constructWithTrace(t, CarDrivingStateService.class,() -> new CarDrivingStateService(serviceContext, mCarPropertyService));mCarUXRestrictionsService = constructWithTrace(t, CarUxRestrictionsManagerService.class, () -> new CarUxRestrictionsManagerService(serviceContext, mCarDrivingStateService, mCarPropertyService));...// 将创建的服务对象依次添加到一个 list 中保存起来List<CarServiceBase> allServices = new ArrayList<>();allServices.add(mFeatureController);allServices.add(mCarUXRestrictionsService); // mCarUserService depends on itallServices.add(mCarUserService);allServices.add(mSystemActivityMonitoringService);allServices.add(mCarPowerManagementService);allServices.add(mCarPropertyService);allServices.add(mCarDrivingStateService);allServices.add(mCarOccupantZoneService);addServiceIfNonNull(allServices, mOccupantAwarenessService);allServices.add(mCarPackageManagerService);allServices.add(mCarInputService);allServices.add(mGarageModeService);...}@MainThreadvoid init() {LimitedTimingsTraceLog t = new LimitedTimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG,Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);t.traceBegin("ICarImpl.init");t.traceBegin("VHAL.init");mHal.init();t.traceEnd();t.traceBegin("CarService.initAllServices");//启动的所有服务遍历调用 init 初始化(各个都继承了 CarServiceBase)for (CarServiceBase service : mAllServices) {t.traceBegin(service.getClass().getSimpleName());service.init();t.traceEnd();}t.traceEnd(); // "CarService.initAllServices"t.traceEnd(); // "ICarImpl.init"}
然后将上面 onCreate() 创建的 mICarImpl 对象返回:
-
onBind() 回调方法会继续传递通过 bindService() 传递来的 intent 对象(即上面的bindServiceAsUser方法)
-
onUnbind() 会处理传递给 unbindService() 的 intent 对象。如果 service 允许绑定,onBind() 会返回客户端与服务互相联系的通信句柄
-
源码路径:
/packages/services/Car/service/src/com/android/car/CarService.java
@Overridepublic IBinder onBind(Intent intent) {return mICarImpl;}
所以此处的 mICarImpl 会作为 IBinder 返回给CarServiceHelperService.java - bindServiceAsUser方法中的参数 mCarServiceConnection(回调)
3.4 回调 ServiceConnection
-
ICarImpl 初始化完毕,会作为 IBinder 返回给CarServiceHelperService.java - bindServiceAsUser方法中绑定此服务的 mCarServiceConnection(回调)
mCarServiceConnection 初始化如下:
-
其中返回的 ICarImpl 被保存在了 CarServiceHelperService 的 mCarService
-
mCarService.transact 跨进程通信,调用 ICar.aidl 中定义的第一个方法 setCarServiceHelper
-
源码路径:
/frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java
private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";private IBinder mCarService;private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();private final ServiceConnection mCarServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {if (DBG) {Slogf.d(TAG, "onServiceConnected: %s", iBinder);}handleCarServiceConnection(iBinder);}@Overridepublic void onServiceDisconnected(ComponentName componentName) {handleCarServiceCrash();}};@VisibleForTestingvoid handleCarServiceConnection(IBinder iBinder) {synchronized (mLock) {if (mCarServiceBinder == iBinder) {return; // already connected.}Slogf.i(TAG, "car service binder changed, was %s new: %s", mCarServiceBinder, iBinder);//1. 返回的 ICarImpl 被保存在了 CarServiceHelperService 的 mCarServiceBindermCarServiceBinder = iBinder;Slogf.i(TAG, "**CarService connected**");}sendSetSystemServerConnectionsCall();...}private void sendSetSystemServerConnectionsCall() {Parcel data = Parcel.obtain();data.writeInterfaceToken(CAR_SERVICE_INTERFACE);data.writeStrongBinder(mHelper.asBinder());//将 ICarServiceHelperImpl 类型的对象作为数据跨进程传递data.writeStrongBinder(mCarServiceConnectedCallback.asBinder());IBinder binder;synchronized (mLock) {binder = mCarServiceBinder;}int code = IBinder.FIRST_CALL_TRANSACTION;try {//2. 跨进程传输//对端是 mCarService 即 ICarImpl,调用 binder 的 transact 进行跨进程通信//其 code 代表需要调用的对端方法,data 为携带的传输数据//FIRST_CALL_TRANSACTION = 0x00000001,即调用对端 ICar.aidl 中定义的第一个方法 setCarServiceHelperif (VERBOSE) Slogf.v(TAG, "calling one-way binder transaction with code %d", code);// oneway void setSystemServerConnections(in IBinder helper, in IBinder receiver) = 0;binder.transact(code, data, null, Binder.FLAG_ONEWAY);if (VERBOSE) Slogf.v(TAG, "finished one-way binder transaction with code %d", code);}...}
跨进程 setSystemServerConnections
@Overridepublic void setSystemServerConnections(IBinder helper, IBinder receiver) {Bundle bundle;try {EventLog.writeEvent(EventLogTags.CAR_SERVICE_SET_CAR_SERVICE_HELPER,Binder.getCallingPid());assertCallingFromSystemProcess();//将 ICarServiceHelper 的代理端保存在 ICarImpl 内部 mICarServiceHelperICarServiceHelper carServiceHelper = ICarServiceHelper.Stub.asInterface(helper);synchronized (mLock) {mICarServiceHelper = carServiceHelper;}//同时也传给了 SystemInterface//此时他们有能力跨进程访问 CarServiceHelperServicemSystemInterface.setCarServiceHelper(carServiceHelper);mCarOccupantZoneService.setCarServiceHelper(carServiceHelper);mCarUserService.setCarServiceHelper(carServiceHelper);...}
3.5 小结
CarService的启动时序如下所示:

04 总结
本文讲解了CarService的总体结构、使用方法及启动流程。
CarService中实现的功能非常庞大,可以说相比传统手机端的 Android 系统,AAOS 中独特且最重要的部分都在 Framework 的CarService中。
-
首先 CarService 是一个系统级别的服务 APK,类似 SystemUI,其在开机时由 SystemServer 通过 CarServiceHelperService 启动。
-
CarServiceHelperService 通过绑定服务的方式启动 CarService,启动之后创建了一个 Binder 对象 ICarImpl,并通过 onBind 返回给 system_server 进程。
-
ICarImpl 构造方法中创建了一系列和汽车相关的核心服务,并依次启动这些服务即调用各自 init 方法。ICarImpl 返回给 CarServiceHelperService 之后,CarServiceHelperService 也将其内部的一个 Binder 对象(ICarServiceHelperImpl)传递到了 CarService 进程,自此 CarService 和 system_server 两个进程建立了双向 Binder 通信。
-
ICarImpl 返回给 CarServiceHelperService 之后,CarServiceHelperService 也将其内部的一个 Binder 对象(ICarServiceHelperImpl)传递到了 CarService 进程,自此 CarService 和 system_server 两个进程建立了双向 Binder 通信。
CarService为Android车载应用开发者提供了一个强大而灵活的平台,让应用程序能够充分利用汽车的硬件和服务能力,打造更加智能化和便捷的驾驶体验。掌握CarService的使用,是车载应用开发中的重要一环,也是实现车载生态系统中创新应用的关键。
“在车载应用的世界里,技术的每一次进步,都是为了让行驶的每一公里更加安全、便捷和愉悦。”
✦
END
✦
链接:https://juejin.cn/post/7353827463632404517 本文为转载,转载文章所包含的文字来源于作者。如因内容或版权等问题,请联系进行删除
相关文章:
Android 车载应用开发指南 - CarService 详解(下)
车载应用正在改变人们的出行体验。从导航到娱乐、从安全到信息服务,车载应用的开发已成为汽车智能化发展的重要组成部分。而对于开发者来说,如何将自己的应用程序无缝集成到车载系统中,利用汽车的硬件和服务能力,是一个极具挑战性…...
【Linux网络 —— 网络基础概念】
Linux网络 —— 网络基础概念 计算机网络背景网络发展 初始协议协议分层协议分层的好处 OSI七层模型TCP/IP五层(或四层)模型 再识协议为什么要有TCP/IP协议?什么是TCP/IP协议?TCP/IP协议与操作系统的关系所以究竟什么是协议? 网络传输基本流程…...
el-form动态标题和输入值,并且最后一个输入框不校验
需求:给了固定的label,叫xx单位,要输入单位的信息,但是属性名称都一样的,UI画图也是表单的形式,所以改为动态添加的形式,实现方式也很简单,循环就完事了,连着表单校验也动…...
一,初始 MyBatis-Plus
一,初始 MyBatis-Plus 文章目录 一,初始 MyBatis-Plus1. MyBatis-Plus 的概述2. 入门配置第一个 MyBatis-Plus 案例3. 补充说明:3.1 通用 Mapper 接口介绍3.1.1 Mapper 接口的 “增删改查”3.1.1.1 查询所有记录3.1.1.2 插入一条数据3.1.1.3 …...
安卓13删除下拉栏中的关机按钮版本2 android13删除下拉栏关机按钮
总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 顶部导航栏下拉可以看到,底部这里有个设置按钮,点击可以进入设备的设置页面,这里我们将更改为删除,不同用户通过这个地方进入设置。我们之前写过一个文章也是一样的删除…...
快递物流单号识别API接口代码
官网:快递鸟 API参数 一、接口描述/说明 (1)该接口仅对运单号做出识别,识别可能属于的一家或多家快递公司。 (2)接口并不返回物流轨迹,用户可结合即时查询接口和订阅查询接口完成轨迹查询、订…...
AI时代的程序员:如何保持和提升核心竞争力
1.引言 随着AIGC(如 ChatGPT、Midjourney、Claude 等)大语言模型的快速崛起,AI辅助编程工具逐渐成为程序员工作的重要组成部分。这一转变不仅改变了工作方式,更深刻影响了程序员的职业角色和技术路径。有人担心,AI将取…...
Oracle 数据库常用命令与操作指南
Oracle 数据库是企业级系统中常用的数据库管理系统,掌握基础的命令可以让你在日常管理中更加高效。本指南将介绍几条常用的 Oracle 数据库命令,涵盖用户权限管理、修改用户密码、删除用户、以及其他日常操作。 目录 授权用户操作权限使用最高权限登录 O…...
spring boot项目对接人大金仓
先确认一下依赖 第一 是否引入了mybatis-plus多数据源,如果引入了请将版本保持在3.5.0以上 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>${dynam…...
《操作系统 - 清华大学》1 -2:操作系统概述 —— 什么是操作系统
文章目录 1. 操作系统定义2. 操作系统的位置3. 操作系统软件的分类4. 操作系统软件的组成5. 操作系统内核特征 现在来继续讲什么是操作系统,操作系统什么样的?它是一个程序,它和其他程序是什么样的关系?然后它有些什么样的组成&am…...
power bi制作各季度收入累加柱状图——日期表、calculate、datesytd
一、数据介绍: 2017-2019年订单销售收入数据(订单日期,销售收入) 二、效果展示: 三、操作步骤: 1、建立日期表 (1)建立原因 本次度量值编写需要运用到datesytd这一时间智能函数…...
OceanBase 3.X 高可用 (一)
OceanBase 3.X 高可用(一) 一、分布式核心 OceanBase 3.x 采用的是paxos 协议,与raft协议相比。其复杂程度高,实现技术难度大。 Paxos 协议允许事务日志乱序发送,顺序提交。raft允许事务顺序发送,顺序提…...
CSR、SSR、SSG
客户端渲染(Client-Side Rendering, CSR)在SEO方面存在一些不利因素,主要原因包括: 初始加载内容的缺乏:CSR依赖于JavaScript来动态生成页面内容。当搜索引擎爬虫访问一个使用CSR技术构建的网站时,它们最初…...
linux -L16-linux 查看应用占用的资源top
linux 查看应用占用的资源top Targetsteps启动 top 命令排序进程:查看特定进程:过滤进程其他常用选项交互式帮助 Target linux 查看应用占用的资源top steps 在 Linux 系统中,top 命令是一个非常有用的工具,它提供了一个实时更…...
QT——多线程操作
一、单线程和多线程的区别 单线程指的是程序在执行时只有一个流程,也就是一次只能执行一个任务。当程序中某个任务需要花费大量时间时,单线程会导致整个程序阻塞,用户体验会变差。 多线程则是指程序在执行时可以同时执行多个任务,每个任务都是一个独立的线程。多线程可以…...
理解C语言之深入理解指针(三)
目录 1. 字符指针变量 2. 数组指针变量 2.1 数组指针变量是什么? 2.2 数组指针变量怎么初始化 3. ⼆维数组传参的本质 4. 函数指针变量 4.1 函数指针变量的创建 4.2 函数指针变量的使⽤ 4.3 两段有趣的代码 4.3.1 typedef 关键字 5. 函数指针数组 6. 转移…...
「芯片知识」MP3解码ic方案,音乐芯片在数字音频中的作用
MP3解码芯片是一种由内部晶振器组成的简单语音电路,将这种独特的MP3音乐芯片与其他零件进行接驳,便能够形成一个完整的语音集成电路。而深受顾客欢迎的MP3音乐芯片现如今已经广泛的使用在电子玩具和家用电器等众多的场合之中,它在数字音频中扮…...
MyBatis与 Springboot 的集成
MyBatis 是一个优秀的持久层框架,专注于 SQL 语句的灵活控制,与 Spring Boot 集成可以简化数据库操作,提升开发效率。Spring Boot 提供了与 MyBatis 无缝集成的支持,使得 MyBatis 可以轻松与 Spring Boot 应用结合使用。 一、MyB…...
迁移学习和外推关系
**迁移学习(Transfer Learning)和外推(Extrapolation)**都是机器学习中处理新数据的一种方式,但它们的定义、应用场景和挑战有所不同。让我们来对比两者并探讨它们的关系。 定义 迁移学习(Transfer Learni…...
小程序-生命周期与WXS脚本
生命周期 什么是生命周期 生命周期(Life Cycle)是指一个对象从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。 我们可以把每个小程序运行的过程,也概括为生命周期: 小程序的启动,表示生命…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
