车机开发—【CarService启动流程】

news2024/11/28 21:58:47

汽车架构:车载HAL是汽车与车辆网络服务之间的接口定义(同时保护传入的数据):

车载HAL与Android Automotive架构:

  • Car App:包括OEM和第三方开发的App
  • Car API:内有包含CarSensorManager在内的API。位于/platform/packages/services/Car/car-lib
  • CarService:系统中与车相关的服务,位于/platform/packages/services/Car/
  • Vehicle HAL:汽车的硬件抽象层描述。位于hardware/interfaces/automotive/vehicle/2.0/default/(接口属性:hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/)

Framework CarService

Android O/P为Automotive场景提供了一系列的服务,这些服务统被称为CarService。它们与HAL层的VehicleHAL通信,进而通过车载总线(例如CAN总线)与车身进行通讯,同时它们还为应用层的APP提供接口,从而让APP能够实现对车身的控制与状态的显示

  • Car***Manager:packages/services/Car/car-lib/src/android/car/hardware
  • Car***Service:packages/services/Car/service/src/com/android/car/

CarService启动流程

和汽车相关的服务的启动主要依靠一个系统服务CarServiceHelperService开机时在SystemServer中启动:

CarServiceHelperService启动

private static final String CAR_SERVICE_HELPER_SERVICE_CLASS =
            "com.android.internal.car.CarServiceHelperService";
            
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        ......
        
​
    if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
                t.traceBegin("StartCarServiceHelperService");
                    mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
                t.traceEnd();
            }
        
        ......
        
     }

CarServiceHelperService被定义在frameworks/opt/car/下,它和其他系统服务一样,属于SystemService的子类,通过SystemServiceManager.startService启动:

SystemServiceManager.startService

public SystemService startService(String className) {
        final Class<SystemService> serviceClass = loadClassFromLoader(className,
                this.getClass().getClassLoader());
        return startService(serviceClass);
    }
    private static Class<SystemService> loadClassFromLoader(String className,
            ClassLoader classLoader) {
        try {
            return (Class<SystemService>) Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            ...
        }
    }

loadClassFromLoader通过类加载器直接获取CarServiceHelperService的class对象,拿到class对象进而再调用startService重载方法:

    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
​
​
            // Create the service.
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                throw new RuntimeException("Failed to create " + name
                        + ": service must extend " + SystemService.class.getName());
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (InstantiationException ex) {
                ...
            } catch (IllegalAccessException ex) {
                ...
            } catch (NoSuchMethodException ex) {
                ...
            } catch (InvocationTargetException ex) {
               ...
            }
    
            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }
​

接着通过反射构造CarServiceHelperService的实例对象,然后再调用startService重载方法:

 public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        // Start it.
        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");
    }

这里先将CarServiceHelperService保存到mServices这个list中,然后调用CarServiceHelperService的onStart方法正式启动此服务。

CarServiceHelperService.onStart

 @Override
    public void onStart() {
        EventLog.writeEvent(EventLogTags.CAR_HELPER_START, mHalEnabled ? 1 : 0);
​
        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(ICarConstants.CAR_SERVICE_INTERFACE);
        if (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,
                UserHandle.SYSTEM)) {
            Slog.wtf(TAG, "cannot start car service");
        }
        loadNativeLibrary();
    }

这里首先注册了开关机广播,CarWatchdogDaemonHelper用于监控此服务,接着会绑定一个包名为"com.android.car",Action为"android.car.ICar"的服务,这就是系统中和汽车相关的核心服务CarService,相关源代码在packages/services/Car/service目录下,然后我们先去看看CarService,等下再回头来看绑定此服务之后的mCarServiceConnection回调部分。

如下是CarService的AndroidManifest部分截图,可以看到CarService的sharedUserId是系统级别的,这是一个系统级服务,类似SystemUI,它编译出来同样是一个APK文件。 来具体看CarService,它的onStartCommand没什么东西,主要来看onBind:

CarService.onBind

@Override
    public IBinder onBind(Intent intent) {
        return mICarImpl;
    }

ICarImpl是一个Binder服务端,其顶级接口为ICar,在onCreate中初始化:

CarService.onCreate

 @Override
    public void onCreate() {
        //通知用户有关 CAN 总线故障 
        mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
        //获取Vehicle hal的client端
        mVehicle = getVehicle();
        
        if (mVehicle == null) {
            throw new IllegalStateException("Vehicle HAL service is not available.");
        }
        try {
            mVehicleInterfaceName = mVehicle.interfaceDescriptor();
        } catch (RemoteException e) {
             ...
        }
      
        //实例化ICarImpl
        mICarImpl = new ICarImpl(this,
                mVehicle,
                SystemInterface.Builder.defaultSystemInterface(this).build(),
                mCanBusErrorNotifier,
                mVehicleInterfaceName);
        //初始化
        mICarImpl.init();
        //Vehicle hal对端死亡回调
        linkToDeath(mVehicle, mVehicleDeathRecipient);
        //将mICarImpl注册到ServiceManager
        ServiceManager.addService("car_service", mICarImpl);
        //修改boot.car_service_created属性为1
        SystemProperties.set("boot.car_service_created", "1");
        super.onCreate();
    }

此方法中主要会对ICarImpl实例化,之后init进行初始化,最后将其注册到ServiceManager。

ICarImpl构造方法

ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
            CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,
            @Nullable CarUserService carUserService,
            @Nullable CarWatchdogService carWatchdogService) {
        ......
        mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext, mCarUserService);
        mCarBluetoothService = new CarBluetoothService(serviceContext, mPerUserCarServiceHelper);
        mCarInputService = new CarInputService(serviceContext, mHal.getInputHal(), mCarUserService);
        mCarProjectionService = new CarProjectionService(
                serviceContext, null /* handler */, mCarInputService, mCarBluetoothService);
        mGarageModeService = new GarageModeService(mContext);
        mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
        mCarAudioService = new CarAudioService(serviceContext);
        mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
        mFixedActivityService = new FixedActivityService(serviceContext);
        mInstrumentClusterService = new InstrumentClusterService(serviceContext,
                mAppFocusService, mCarInputService);
        mSystemStateControllerService = new SystemStateControllerService(
                serviceContext, mCarAudioService, this);
        mCarStatsService = new CarStatsService(serviceContext);
        mCarStatsService.init();
        if (mFeatureController.isFeatureEnabled(Car.VEHICLE_MAP_SERVICE)) {
            mVmsBrokerService = new VmsBrokerService(mContext, mCarStatsService);
        } else {
            mVmsBrokerService = null;
        }
        if (mFeatureController.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
            mCarDiagnosticService = new CarDiagnosticService(serviceContext,
                    mHal.getDiagnosticHal());
        } else {
            mCarDiagnosticService = null;
        }
        if (mFeatureController.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
            mCarStorageMonitoringService = new CarStorageMonitoringService(serviceContext,
                    systemInterface);
        } else {
            mCarStorageMonitoringService = null;
        }
        mCarConfigurationService =
                new CarConfigurationService(serviceContext, new JsonReaderImpl());
        mCarLocationService = new CarLocationService(serviceContext);
        mCarTrustedDeviceService = new CarTrustedDeviceService(serviceContext);
        mCarMediaService = new CarMediaService(serviceContext, mCarUserService);
        mCarBugreportManagerService = new CarBugreportManagerService(serviceContext);
        ......
         CarLocalServices.addService(CarPowerManagementService.class, mCarPowerManagementService);
        CarLocalServices.addService(CarPropertyService.class, mCarPropertyService);
        CarLocalServices.addService(CarUserService.class, mCarUserService);
        CarLocalServices.addService(CarTrustedDeviceService.class, mCarTrustedDeviceService);
        CarLocalServices.addService(CarUserNoticeService.class, mCarUserNoticeService);
        CarLocalServices.addService(SystemInterface.class, mSystemInterface);
        CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
        CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
        CarLocalServices.addService(FixedActivityService.class, mFixedActivityService);
        CarLocalServices.addService(VmsBrokerService.class, mVmsBrokerService);
        .....
        List<CarServiceBase> allServices = new ArrayList<>();
        allServices.add(mFeatureController);
        allServices.add(mCarUserService);
        allServices.add(mSystemActivityMonitoringService);
        allServices.add(mCarPowerManagementService);
        allServices.add(mCarPropertyService);
        allServices.add(mCarDrivingStateService);
        ...
        mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
}

这里省略了和Vehicle hal有关的初始化和分析,后续文章再看。

ICarImpl构造方法中创建了一系列CarService模块下的服务,这些服务有部分被添加到了CarLocalServices内部,其提供了getService静态方法用于直接获取这些服务,所有服务都被保存在ICarImpl内部的CarServiceBase类型数组mAllServices中(所有服务都是CarServiceBase的子类)。

ICarImpl构造方法完了之后会接着调用其init方法:

ICarImpl.init

 @MainThread
    void init() {
        mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG, Trace.TRACE_TAG_HAL);
        traceBegin("VehicleHal.init");
        //hal初始化
        //...省略
        traceEnd();
        traceBegin("CarService.initAllServices");
        for (CarServiceBase service : mAllServices) {
            service.init();
        }
        traceEnd();
    }

此方法很简单,遍历mAllServices,分别执行所有服务的init,各自初始化,有兴趣的可以自己去研究各个服务。

到此ICarImpl初始化完毕,最后会作为binder返回给绑定此服务的mCarServiceConnection:

private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            if (DBG) {
                Slog.d(TAG, "onServiceConnected:" + iBinder);
            }
            handleCarServiceConnection(iBinder);
        }
​
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            handleCarServiceCrash();
        }
    };

CarServiceHelperService.handleCarServiceConnection

   void handleCarServiceConnection(IBinder iBinder) {
        ...
        synchronized (mLock) {
            if (mCarService == iBinder) {
                return; // already connected.
            }
        
            mCarService = iBinder;
          ...
    
        sendSetCarServiceHelperBinderCall();
         ......
         
        }
    }

这个方法我们主要关注上面部分,返回的ICarImpl被保存在了CarServiceHelperService的mCarService,后续可通过mCarService跨进程通信。

CarServiceHelperService.sendSetCarServiceHelperBinderCall
   private void sendSetCarServiceHelperBinderCall() {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(ICarConstants.CAR_SERVICE_INTERFACE);
        data.writeStrongBinder(mHelper.asBinder());
        // void setCarServiceHelper(in IBinder helper)
        sendBinderCallToCarService(data, ICarConstants.ICAR_CALL_SET_CAR_SERVICE_HELPER);
    }

这里将会进行跨进程通信,首先构造传输数据,ICarConstants是定义在ExternalConstants的静态内部类:

     static final class ICarConstants {
        ....
        static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
        static final int ICAR_CALL_SET_CAR_SERVICE_HELPER = 0;
        ....
    }

CAR_SERVICE_INTERFACE用来标识远程服务接口,其具体传输数据是一个Binder对象,我们来看看mHelper是什么?

​
    private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();

mHelper是定义在CarServiceHelperService的内部类,是一个Binder对象:

private class ICarServiceHelperImpl extends ICarServiceHelper.Stub {
    ......
    .....
}

CarServiceHelperService.sendBinderCallToCarService 再回到前面看sendBinderCallToCarService方法:

private void sendBinderCallToCarService(Parcel data, int callNumber) {
        // Cannot depend on ICar which is defined in CarService, so handle binder call directly
        // instead.
        IBinder carService;
        synchronized (mLock) {
            carService = mCarService;
        }
        if (carService == null) {
            Slog.w(TAG, "Not calling txn " + callNumber + " because service is not bound yet",
                    new Exception());
            return;
        }
        int code = IBinder.FIRST_CALL_TRANSACTION + callNumber;
        try {
            
            carService.transact(code, data, null, Binder.FLAG_ONEWAY);
            
        } catch (RemoteException e) {
           
            handleCarServiceCrash();
        } catch (RuntimeException e) {
           
            throw e;
        } finally {
            data.recycle();
        }
    }

这个方法很明显就是跨进程传输的具体实现了,对端是mCarService即ICarImpl,调用binder的transact进行跨进程通信,其code代表需要调用的对端方法,data为携带的传输数据,ICAR_CALL_SET_CAR_SERVICE_HELPER等于0,这里调用的是对端的0号方法。

于是我们来看看ICar.aidl中定义的0号方法:

interface ICar {
    .....
    oneway void setCarServiceHelper(in IBinder helper) = 0;
    ......
  }

ICarImpl.setCarServiceHelper

接着来看setCarServiceHelper具体实现:

 @Override
    public void setCarServiceHelper(IBinder helper) {
        //权限检查
        assertCallingFromSystemProcess();
        ICarServiceHelper carServiceHelper = ICarServiceHelper.Stub.asInterface(helper);
        synchronized (mLock) {
            mICarServiceHelper = carServiceHelper;
        }
        mSystemInterface.setCarServiceHelper(carServiceHelper);
        mCarOccupantZoneService.setCarServiceHelper(carServiceHelper);
    }

这里将ICarServiceHelper的代理端保存在ICarImpl内部mICarServiceHelper,同时也传给了SystemInterface和CarOccupantZoneService,我们暂时不需要知道这三个类拿到ICarServiceHelper的代理端的具体用处,只需要知道他们有能力跨进程访问CarServiceHelperService就行了。

全文解析了车机开发中CarFramework框架的CarService启动流程;车机开发的知识点非常的多;总结如上图资料文档参考《车载技术手册》,里面内容包含以上进阶技术。

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通信。

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

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

相关文章

5个有效的华为(HUAWEI)手机数据恢复方法

5个有效的手机数据恢复方法 华为智能手机中的数据丢失比许多人认为的更为普遍。发生这种类型的丢失有多种不同的原因&#xff0c;因此数据恢复软件的重要性。您永远不知道您的智能手机何时会在这方面垮台&#xff1b;因此&#xff0c;预防总比哀叹好&#xff0c;这就是为什么众…

通过 FTP 使用 Python 自动压缩网站图像

图像压缩对于技术SEO世界来说并不陌生,但是随着核心生命体征形式的网站性能是一个排名因素,现在是时候开始采取行动了。我已经做了几十次网站审计,我发现 80% 的网站性能问题都可以在图像或 JavaScript 下进行。当我看到图像是一个大问题时,我会欢呼,因为它是最容易解决的…

注意啦,面试通过后,别忘了教师资格证认定

所有要「教师资格证认定」教程的宝子们看过来面试合格的小伙伴都可以进行认定工作 . 认定时间 查询各省份认定公告&#xff0c;确定认定时间范围。以下是公告汇总网址&#xff08;https://www.jszg.edu.cn/portal/qualification_cert/dynamics?id21691&#xff09; 认定次数 每…

【jQuery】js实现文件浏览功能

1.说明 近期遇到一个浏览用户文件的需求&#xff0c;类似于访问百度网盘那样的列表&#xff0c;包含文件和文件夹&#xff0c;这个功能实现起来很简单&#xff0c;从服务器获取到的文件列表至少要有文件id、父级文件id、是否文件夹这三个字段 2.html设计 前端排版看你实际情…

x86架构设备的OpenWrt的空间扩容问题

openwrt固件是squashfs-combined-efi非exf4格式 直接将原有根分区扩容 用插件是&#xff1a;fdisk,losetup,resize2fs,blkid df -h fdisk -l fdisk /dev/sda //进入fdisk分区管理工具注意fdisk后参数是磁盘名称&#xff0c;是要根据实际情况填写 fdisk /dev/sda //进入fdi…

【04-JVM面试专题-什么是双亲委派机制(父类委托机制)?如何打破双亲委派机制?双亲委派机制的优缺点?什么是沙箱安全机制呢?】

什么是双亲委派机制&#xff1f;如何打破双亲委派机制&#xff1f; JVM的双亲委派机制知道吗&#xff1f;怎么打破它呢&#xff1f;你看看自己掌握的怎么样呢&#xff1f; 什么是双亲委派机制&#xff1f;(父类委托机制) 检查某个类是否已经加载 自底向上&#xff0c;从Custom…

将数组中的每个元素四舍五入到指定的精度numpy.rint()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 将数组中的每个元素 四舍五入到指定的精度 numpy.rint() 选择题 请问np.rint(a)的输出结果是? import numpy as np anp.array([-1.72,-1.3,0.37,2.4]) print("【显示】a&#xff1a;\n…

requests库基本用法

目录 1 GET请求 1.1 无参数 1.2 查询字符串 2 POST 2.1 无参数 2.2 json数据 2.3 文件 3 一些常见的请求 3.1 base64 requests是请求用的&#xff0c;在发起请求中requests会默认帮我们解决一些问题&#xff0c;比如跨域 下面做几个例子&#xff0c;服务选用…

CTFer成长之路之SSRF漏洞

SSRF漏洞CTF SSRF Training 题目描述: web容器中存在一个flag&#xff0c;mysql中存在一个管理员账号密码&#xff0c;其余容器中均没有特定flag mysql容器中内置 tcpdump vulnweb容器中内置一个 fpm.py 攻击脚本 docker-compose.yml version: "3" services:w…

有色金属行业数字化之路探析

有色金属行业是我国国民经济的支柱产业和重要组成部分&#xff0c;是处于整个原材料生产链的上游环节&#xff0c;其支撑着冶炼、钢铁、智能制造业、芯片、建筑等行业的发展&#xff0c;也是是科学研究、国防建设等方面发展的重要材料基础&#xff0c;同时还是保障国家综合实力…

Linux之安装node

Linux之安装node步骤如下 1.去网站下载node 下载地址&#xff1a; https://npm.taobao.org/mirrors/ 2.上传到指定目录下 3.解压 tar -zxvf node-v17.3.0-linux-x644.配置node环境变量 //执行以下命令 vim /etc/profile //在path中加入以下内容 /usr/local/node-v15.14.0/b…

电力电子系统仿真软件--Psim仿真软件设计

目录 1.简介 2.应用优势 3.应用领域 4.电路结构 5.界面介绍 6.应用实例 6.1 主电路部分 6.2 控制电路部分 6.3 具体的电路原理及仿真搭建过程后续会一一介绍&#xff0c;请各位点赞关注&#xff0c;你的关注将是博主最大的更新动力。 7.电力系统仿真软件下载链接 1.简…

以萨技术在科创板IPO终止:计划募资15亿元,实控人为李凡平

2月21日&#xff0c;上海证券交易所披露的信息显示&#xff0c;因以萨技术股份有限公司&#xff08;下称“以萨技术”&#xff09;及其保荐人撤回发行上市申请&#xff0c;根据《上海证券交易所股票发行上市审核规则》第六十三条的相关规定&#xff0c;上海证券交易所终止其发行…

创业能否成功?这几个因素很重要!

创业能否成功&#xff1f;这几个因素很重要&#xff01; 2023-02-22 19:06:53 大家好&#xff0c;我是你们熟悉而又陌生的好朋友梦龙&#xff0c;一个创业期的年轻人 上周末跟朋友一起钓鱼&#xff0c;他跟吐槽现在生意越来越难做。他是我身边可以说是创业很成功的例子&#…

拨开迷雾 看见vivo穿越周期的秘密

文|智能相对论作者|佘凯文任何一个行业都有周期性&#xff0c;就好像我们在做股票投资的时候&#xff0c;提到最多的就是周期规律&#xff0c;因为只有掌握规律才可以让我们赚到钱。所以不论是哪家公司都逃脱不了行业周期的宿命。行业寒冬方显强者本色就拿手机行业来说吧&#…

初探 qiling ( 麒麟 ):开源的二进制分析、高级代码模拟框架

官方介绍&#xff1a; 官网&#xff1a;https://qiling.io/&#xff1a;https://twitter.com/qiling_iogithub 地址&#xff1a;https://github.com/qilingframework/qiling 1、qiling 简介 qiling 是什么 qiling 基于 python 开发&#xff0c;是一个开源的、可模拟多种架构…

Vue3 基础

Vue3 基础 概述 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&…

升职加薪必备,2023年程序员不能不知道的AI辅助编码工具

已经有很多人把chatGPT当做必备的Bug修复工具了&#xff0c;对于用AI写代码&#xff0c;有人感到失落&#xff0c;害怕被取代&#xff0c;而另一些人则认为人工智能将加快编写更好代码的过程。 尽管 AI 编写的代码并非完美无缺&#xff0c;但我相信&#xff0c;最终AI将取代人…

Java实例——网络实例

1、主机IP地址获取 步骤一&#xff1a;获取主机InetAddress 步骤二&#xff1a;获取主机IP地址 InetAddress address null;try {address InetAddress.getByName("www.baidu.com");}catch (UnknownHostException e) {System.exit(2);}System.out.println("Host…

聚类(性能度量)

文章目录聚类&#xff08;性能度量&#xff09;外部指标例1内部指标例2聚类&#xff08;性能度量&#xff09; 对数据集 D{x1,x2,...,xm}D\{x_1,x_2,...,x_m\}D{x1​,x2​,...,xm​} &#xff0c;假定通过聚类给出的簇划分为 C{C1,C2,...,Ck}C\{C_1,C_2,...,C_k\}C{C1​,C2​,…