【车载audio】【audio hal 01】【Android 音频子系统:Audio HAL Server 启动全流程深度解析】

Android 音频子系统:Audio HAL Server 启动全流程深度解析

1. 背景与架构概述

在 Android Treble 架构中,音频系统的核心逻辑被拆分为 Framework 层(AudioFlinger)Vendor 层(Audio HAL)android.hardware.audio.service 进程是 Vendor 层的守护进程,它的主要职责是:

  1. 对接硬件 :加载厂商实现的音频驱动库(.so)。
  2. 提供接口:将硬件能力通过 HIDL 或 AIDL 接口暴露给系统服务。
  3. 常驻运行 :作为独立进程,处理来自 audioserver 的 Binder 请求。

本文将深入分析该进程从编译、启动到最终与 AudioFlinger 握手的全过程。


2. 全链路启动流程图

AudioFlinger (Framework) hwservicemanager Audio HAL Service (vendor 分区) init 进程 AudioFlinger (Framework) hwservicemanager Audio HAL Service (vendor 分区) init 进程 ------ 阶段一:Framework 订阅监听 ------ ------ 阶段二:HAL 进程启动与初始化 ------ ------ 阶段三:动态发现与服务注册 ------ ------ 阶段四:跨进程握手 ------ 注册通知监听 (IDevicesFactory) 1 解析 init.rc 启动进程 2 main(): 初始化 Binder/AIDL 线程池 3 main(): 配置 hwbinder 共享内存大小 4 寻找版本匹配的 -impl.so 5 dlopen / dlsym (执行 HIDL_FETCH) 6 创建 DevicesFactory 对象 7 registerAsService("default") 8 发送通知:IDevicesFactory 已就绪 9 getService("default") 10 返回 Binder 代理 (Bp) 11 建立连接并监听死亡通知 12


3. 进程构建与配置

3.1 编译蓝图:cc_binary

Audio HAL Server 是一个标准的 C++ 二进制文件,通过 Android.bp 定义。

makefile 复制代码
cc_binary {
    name: "android.hardware.audio.service",

    init_rc: ["android.hardware.audio.service.rc"],
    relative_install_path: "hw",
    vendor: true,

    srcs: ["service.cpp"],

    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
    ],

    shared_libs: [
        "libcutils",
        "libbinder",
        "libbinder_ndk",
        "libhidlbase",
        "liblog",
        "libutils",
        "libhardware",
    ],

    defaults: [
        "android_hardware_audio_config_defaults",
    ],
}

3.2 启动配置:init.rc

系统启动时由 init 进程拉起。注意其运行权限为 audioserver,并赋予了 SYS_NICE 权限以确保音频流的实时性。

c 复制代码
service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service
    class hal
    user audioserver
    # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
    group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
    capabilities BLOCK_SUSPEND SYS_NICE
    # setting RLIMIT_RTPRIO allows binder RT priority inheritance
    rlimit rtprio 10 10
    ioprio rt 4
    task_profiles ProcessCapacityHigh HighPerformance
    onrestart restart audioserver

4. 源码深度解析:从 main 开始

4.1 进程入口:service.cpp

main 函数负责搭建整个 Binder 通信环境,并采用"从新到旧"的版本回退策略注册接口。

c 复制代码
/**
 * Audio HAL Service 进程入口函数
 *
 * 该 main 函数负责:
 * 1. 初始化 vendor 侧 Binder(vndbinder / hwbinder / AIDL Binder)运行环境
 * 2. 配置 Binder / HIDL RPC 线程池及共享内存参数
 * 3. 注册 Audio 相关的 HIDL HAL 接口(必选 / 可选)
 * 4. 通过 dlopen 方式加载并注册部分外部 HAL 实现(如 Bluetooth Audio)
 * 5. 进入 Binder 线程池循环,作为常驻 Audio HAL 服务运行
 *
 * 该进程通常运行在 vendor 分区,
 * 是 system_server 与底层音频硬件之间的核心服务节点。
 */
int main(int /* argc */, char* /* argv */ []) {

    /**
     * 忽略 SIGPIPE 信号。
     *
     * 在 Binder 或 socket 通信中:
     * - 如果对端进程异常退出
     * - 当前进程向已关闭的 fd 写数据
     * 默认行为会触发 SIGPIPE 并导致进程被系统杀死。
     *
     * Audio HAL 是常驻服务进程,必须具备健壮性,
     * 因此忽略 SIGPIPE,由代码自行处理错误返回。
     */
    signal(SIGPIPE, SIG_IGN);

    /**
     * 初始化 android::ProcessState,并指定使用 /dev/vndbinder。
     *
     * 说明:
     * - vndbinder 是 system binder 在 vendor 侧的镜像
     * - 用于 system_server ↔ vendor HAL 的跨分区 Binder 通信
     *
     * Audio HAL 运行在 vendor 进程中,
     * 必须显式使用 vndbinder,而不能使用 /dev/binder。
     */
    ::android::ProcessState::initWithDriver("/dev/vndbinder");

    /**
     * 启动 vndbinder 的 Binder 线程池。
     *
     * 该线程池用于:
     * - 处理来自 system_server 的 HIDL / Binder 请求
     * - 接收 Audio HAL 接口调用
     *
     * 如果不启动线程池,Binder 调用将无法被处理。
     */
    ::android::ProcessState::self()->startThreadPool();

    /**
     * 设置 AIDL(libbinder_ndk)线程池的最大线程数。
     *
     * Audio HAL 中的 AIDL 接口通常对并发要求不高,
     * 限制线程数可以:
     * - 减少竞态条件
     * - 降低锁复杂度
     * - 避免资源被过度并发访问
     */
    ABinderProcess_setThreadPoolMaxThreadCount(1);

    /**
     * 启动 AIDL Binder 线程池。
     *
     * 使基于 AIDL 的 HAL 接口可以开始接收客户端请求。
     */
    ABinderProcess_startThreadPool();

    /**
     * 定义系统属性默认值。
     *
     * 用于判断 hwbinder mmap 大小是否被厂商显式配置。
     */
    const int32_t defaultValue = -1;

    /**
     * 从系统属性中读取 hwbinder mmap 大小配置(单位:KB)。
     *
     * hwbinder mmap 区域用于:
     * - HIDL Binder 之间的大块数据传输
     * - FMQ(Fast Message Queue)
     * - 较大的参数结构体
     *
     * 在音频场景中,该大小可能影响:
     * - 音频参数下发
     * - offload / effect 配置
     */
    int32_t value =
        property_get_int32("persist.vendor.audio.service.hwbinder.size_kbyte", defaultValue);

    /**
     * 如果系统属性存在(即不等于默认值),
     * 则使用该值重新配置 hwbinder 的 mmap 大小。
     */
    if (value != defaultValue) {
        /**
         * 打印日志,记录当前使用的 mmap 配置。
         */
        ALOGD("Configuring hwbinder with mmap size %d KBytes", value);

        /**
         * 将 KB 转换为 Byte,并初始化 hwbinder 的共享内存大小。
         *
         * 注意:
         * - 该配置必须在 Binder 实际使用前完成
         * - 否则配置将不会生效
         */
        ProcessState::initWithMmapSize(static_cast<size_t>(value) * 1024);
    }

    /**
     * 配置 HIDL RPC 线程池。
     *
     * 参数说明:
     * - 16 :HIDL RPC 最大线程数
     * - callerWillJoin = true:
     *     表示 main 线程本身也会进入 RPC 线程池
     *
     * 这是 HIDL HAL 能够被 system_server 调用的必要条件。
     */
    configureRpcThreadpool(16, true /*callerWillJoin*/);

    // Automatic formatting tries to compact the lines, making them less readable
    // clang-format off

    /**
     * 定义必须注册的 Audio HAL 接口族列表。
     *
     * 每个 InterfacesList 的结构为:
     * - 第一个元素:接口族名称(仅用于日志)
     * - 后续元素  :按"版本从新到旧"排列的 HIDL 接口全名
     *
     * 只要其中任意一个版本注册成功,
     * 即认为该接口族在当前设备上可用。
     */
    const std::vector<InterfacesList> mandatoryInterfaces = {
        {
            "Audio Core API",
            "android.hardware.audio@7.1::IDevicesFactory",
            "android.hardware.audio@7.0::IDevicesFactory",
            "android.hardware.audio@6.0::IDevicesFactory", /*HidlServiceManagement: Registered android.hardware.audio@6.0::IDevicesFactory/default  当时注册成功的是*/
            "android.hardware.audio@5.0::IDevicesFactory",
            "android.hardware.audio@4.0::IDevicesFactory",
        },
        {
            "Audio Effect API",
            "android.hardware.audio.effect@7.0::IEffectsFactory",
            "android.hardware.audio.effect@6.0::IEffectsFactory",
            "android.hardware.audio.effect@5.0::IEffectsFactory",
            "android.hardware.audio.effect@4.0::IEffectsFactory",
        }
    };

    /**
     * 定义可选注册的 Audio 相关 HAL 接口族。
     *
     * 这些接口通常依赖特定硬件能力:
     * - SoundTrigger(语音唤醒)
     * - Bluetooth Audio
     *
     * 注册失败不会影响 Audio 核心功能,
     * 因此仅打印警告日志。
     */
    const std::vector<InterfacesList> optionalInterfaces = {
        {
            "Soundtrigger API",
            "android.hardware.soundtrigger@2.3::ISoundTriggerHw",
            "android.hardware.soundtrigger@2.2::ISoundTriggerHw",
            "android.hardware.soundtrigger@2.1::ISoundTriggerHw",
            "android.hardware.soundtrigger@2.0::ISoundTriggerHw",
        },
        {
            "Bluetooth Audio API",
            "android.hardware.bluetooth.audio@2.2::IBluetoothAudioProvidersFactory",
            "android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory",
            "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory",
        },
        {
            /**
             * 旧版 Bluetooth A2DP Offload HIDL 接口。
             *
             * 该接口在 Bluetooth Audio HAL V2
             * 完全支持 offload 后可被移除。
             */
            "Bluetooth Audio Offload API",
            "android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload"
        }
    };

    /**
     * 定义通过共享库方式注册的可选 HAL 实现。
     *
     * 与 passthrough HAL 不同:
     * - HAL 实现不与当前进程静态链接
     * - 通过 dlopen 在运行期动态加载
     *
     * 常见于 Bluetooth Audio 等模块。
     */
    const std::vector<std::pair<std::string,std::string>> optionalInterfaceSharedLibs = {
        {
            "android.hardware.bluetooth.audio-impl",
            "createIBluetoothAudioProviderFactory",
        },
        {
            "android.hardware.bluetooth.adapter1.audio-impl",
            "createIBluetoothAudioProviderFactory",
        },
    };
    // clang-format on

    /**
     * 注册所有必须存在的 Audio HAL 接口族。
     *
     * 若整个接口族注册失败,
     * 表示系统无法提供核心 Audio 能力,
     * 因此直接触发 fatal,终止进程。
     */
    for (const auto& listIter : mandatoryInterfaces) {
        auto iter = listIter.begin();
        const std::string& interfaceFamilyName = *iter++;

        LOG_ALWAYS_FATAL_IF(
            !registerPassthroughServiceImplementations(iter, listIter.end()),
            "Could not register %s", interfaceFamilyName.c_str());
    }

    /**
     * 注册所有可选的 Audio HAL 接口族。
     *
     * 注册失败不会中断进程,
     * 仅用于提示当前设备不支持对应能力。
     */
    for (const auto& listIter : optionalInterfaces) {
        auto iter = listIter.begin();
        const std::string& interfaceFamilyName = *iter++;

        ALOGW_IF(
            !registerPassthroughServiceImplementations(iter, listIter.end()),
            "Could not register %s", interfaceFamilyName.c_str());
    }

    /**
     * 通过 dlopen 方式注册外部 HAL 服务实现。
     *
     * 每个共享库内部通常提供一个工厂函数,
     * 用于创建并注册对应的 HAL Binder 服务。
     */
    for (const auto& interfacePair : optionalInterfaceSharedLibs) {
        const std::string& libraryName = interfacePair.first;
        const std::string& interfaceLoaderFuncName = interfacePair.second;

        if (registerExternalServiceImplementation(libraryName, interfaceLoaderFuncName)) {
            ALOGI("%s() from %s success",
                  interfaceLoaderFuncName.c_str(), libraryName.c_str());
        } else {
            ALOGW("%s() from %s failed",
                  interfaceLoaderFuncName.c_str(), libraryName.c_str());
        }
    }

    /**
     * 当前线程加入 HIDL RPC 线程池。
     *
     * 该调用不会返回,
     * main 线程进入 Binder 循环,
     * 进程开始作为 Audio HAL 服务长期运行。
     */
    joinRpcThreadpool();
}

4.2 接口注册核心逻辑:Passthrough 模式

在 Android 中,音频接口常通过 Passthrough 模式加载。这意味着 HAL 的具体实现代码直接加载到当前进程空间。

注册辅助函数
c 复制代码
/**
 * registerPassthroughServiceImplementations
 *
 * 用于注册一组 HIDL passthrough HAL 接口实现。
 *
 * 设计背景:
 * - 同一 HAL 接口通常存在多个版本(如 audio@7.1 / 7.0 / 6.0)
 * - 设备只需支持其中一个版本即可满足 framework 要求
 * - 因此采用"从新到旧逐个尝试"的 fallback 策略
 *
 * 返回值语义:
 * - 只要任意一个接口版本注册成功,即返回 true
 * - 所有版本都失败才返回 false
 */
static bool registerPassthroughServiceImplementations(Iter first, Iter last) {
    for (; first != last; ++first) {

        /**
         * 尝试注册当前接口版本对应的 passthrough HAL 实现。
         *
         * registerPassthroughServiceImplementation 内部会:
         * - 查找该接口的 HAL 实现
         * - 创建服务对象
         * - 将服务注册到 hwservicemanager
         */
        if (registerPassthroughServiceImplementation(*first) == OK) {

            /**
             * 当前接口版本注册成功,
             * 认为整个接口族已经满足要求,立即返回成功。
             */
            return true;
        }
    }

    /**
     * 所有接口版本均注册失败,
     * 表示该接口族在当前设备上不可用。
     */
    return false;
}
外部库动态加载逻辑

对于蓝牙音频等模块,通过 dlopen 灵活加载,避开了静态链接的限制。

c 复制代码
/**
 * registerExternalServiceImplementation
 *
 * 通过 dlopen + 工厂函数的方式,注册一个外部 HAL 服务实现。
 *
 * 设计背景:
 * - 某些 HAL(如 Bluetooth Audio)不适合 passthrough 方式
 * - HAL 实现可能依赖不同厂商库或运行时选择
 * - 因此采用动态加载的方式进行注册
 *
 * 参数说明:
 * - libName  : 共享库名(不包含 .so)
 * - funcName : 库中用于创建并注册 HAL 服务的工厂函数名
 */
static bool registerExternalServiceImplementation(const std::string& libName,
                                                  const std::string& funcName) {
    constexpr int dlMode = RTLD_LAZY;

    // 清除之前可能残留的 dlerror 状态
    dlerror();

    // 拼接出完整的共享库文件名
    auto libPath = libName + ".so";

    /**
     * 动态加载共享库。
     *
     * 若加载失败,说明该 HAL 实现不适用于当前设备,
     * 或者相关模块未编译进 vendor 镜像。
     */
    void* handle = dlopen(libPath.c_str(), dlMode);
    if (handle == nullptr) {
        const char* error = dlerror();
        ALOGE("Failed to dlopen %s: %s", libPath.c_str(),
              error != nullptr ? error : "unknown error");
        return false;
    }

    /**
     * 查找 HAL 工厂函数。
     *
     * 该函数通常负责:
     * - 创建 HAL Binder 服务实例
     * - 将其注册到 Binder / hwservicemanager
     */
    binder_status_t (*factoryFunction)();
    *(void**)(&factoryFunction) = dlsym(handle, funcName.c_str());

    if (!factoryFunction) {
        const char* error = dlerror();
        ALOGE("Factory function %s not found in libName %s: %s",
              funcName.c_str(), libPath.c_str(),
              error != nullptr ? error : "unknown error");

        // 工厂函数不存在,释放共享库
        dlclose(handle);
        return false;
    }

    /**
     * 调用工厂函数完成 HAL 注册。
     *
     * 仅当返回 STATUS_OK 时,
     * 才认为外部 HAL 服务注册成功。
     */
    return ((*factoryFunction)() == STATUS_OK);
}

5. 寻址与实例化:Passthrough 机制的终点

5.1 注册实现 (LegacySupport.cpp)

这是 HAL 实现被推送到 hwservicemanager 的最后一公里。

c 复制代码
/**
 * registerPassthroughServiceImplementation
 *
 * 用于注册一个 HIDL passthrough HAL 服务实例。
 *
 * 背景说明:
 * - Passthrough HAL 的特点是:
 *   - HAL 实现代码直接运行在当前进程中
 *   - 不通过独立的 binderized HAL service 进程
 * - 因此在注册前,必须:
 *   1. 找到本地 HAL 实现(不能是 remote)
 *   2. 校验其接口类型是否符合预期
 *   3. 将该实现注册到 hwservicemanager
 *
 * 该函数是 Audio HAL / Effect HAL 等
 * 在 vendor 进程中完成 HIDL 服务注册的"最后一道关卡"。
 *
 * 参数说明:
 * - interfaceName        : 用于查找 HAL 实现的接口名(可能是某个版本)
 * - expectInterfaceName  : 期望的接口 descriptor(用于安全校验)
 * - registerServiceCb    : 实际执行注册动作的回调函数
 * - serviceName          : HAL 服务名(通常是 "default")
 *
 * 返回值:
 * - OK           : 注册成功
 * - EXIT_FAILURE : 任一步骤失败
 *
 * warn_unused_result:
 * - 强制调用者必须检查返回值
 * - 防止 HAL 注册失败却被忽略
 */
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
        const std::string& interfaceName,
        const std::string& expectInterfaceName,
        RegisterServiceCb registerServiceCb,
        const std::string& serviceName) {

    /**
     * 通过 HIDL 内部机制获取 passthrough HAL 实现对象。
     *
     * getRawServiceInternal 的关键行为:
     * - 根据 interfaceName + serviceName 查找 HAL 实现
     * - retry = true:
     *     表示在首次失败时可进行重试(适配 HAL 初始化时序)
     * - getStub = true:
     *     强制获取 passthrough stub(即本地实现)
     *
     * 成功返回:
     * - 一个运行在当前进程内的 HAL 对象(sp<IBase>)
     */
    sp<IBase> service =
            getRawServiceInternal(interfaceName,
                                  serviceName,
                                  true  /* retry */,
                                  true  /* getStub */); //  -----------------重点6 拿到 DevicesFactory 对象

    /**
     * 如果未能获取 HAL 实现,说明:
     * - 该接口版本在当前设备上未实现
     * - 或 HAL 模块未编译 / 未链接进当前进程
     */
    if (service == nullptr) {
        ALOGE("Could not get passthrough implementation for %s/%s.",
              interfaceName.c_str(), serviceName.c_str());
        return EXIT_FAILURE;
    }

    /**
     * 校验该 HAL 实现是否为本地对象。
     *
     * 对于 passthrough HAL:
     * - service->isRemote() 必须返回 false
     *
     * 如果是 remote,说明:
     * - 该接口实际上是 binderized HAL
     * - 或被错误地代理到了其他进程
     *
     * 这与 passthrough HAL 的设计目标相违背。
     */
    if (service->isRemote()) {
        ALOGE("Implementation of %s/%s is remote!",
              interfaceName.c_str(), serviceName.c_str());
        return EXIT_FAILURE;
    }

    /**
     * 用于保存 HAL 实现实际声明的接口 descriptor。
     *
     * 该 descriptor 由 HAL 实现自身返回,
     * 用于防止"接口名与实现类型不匹配"的错误注册。
     */
    std::string actualName;

    /**
     * 通过 HIDL interfaceDescriptor() 获取实现的真实接口名。
     *
     * 示例返回值:
     * - "android.hardware.audio@6.0::IDevicesFactory"
     *
     * 使用 Return<void> 是因为:
     * - HIDL 接口调用本身是一次 Binder 风格调用
     * - 需要显式检查通信是否成功
     */
    Return<void> result = service->interfaceDescriptor(
            [&actualName](const hidl_string& descriptor) {
                actualName = descriptor;
            });

    /**
     * 如果接口 descriptor 获取失败,说明:
     * - HIDL 通信异常
     * - HAL 实现不完整或存在严重错误
     */
    if (!result.isOk()) {
        ALOGE("Error retrieving interface name from %s/%s: %s",
              interfaceName.c_str(),
              serviceName.c_str(),
              result.description().c_str());
        return EXIT_FAILURE;
    }

    /**
     * 校验 HAL 实现的真实接口类型是否符合预期。
     *
     * 该检查用于防止以下问题:
     * - HAL 实现返回了错误的接口类型
     * - 不同接口之间发生误注册
     *
     * 这是 Audio HAL 等核心模块的重要安全校验点。
     */
    if (actualName != expectInterfaceName) {
        ALOGE("Implementation of %s/%s is actually %s, not a %s!",
              interfaceName.c_str(),
              serviceName.c_str(),
              actualName.c_str(),
              expectInterfaceName.c_str());
        return EXIT_FAILURE;
    }

    /**
     * 调用注册回调函数,将 HAL 服务注册到 hwservicemanager。
     *
     * registerServiceCb 内部通常会:
     * - 将 service 转换为具体接口类型
     * - 调用 registerAsService(serviceName)
     */
    status_t status = registerServiceCb(service, serviceName); //  -----------------重点7 最终将 DevicesFactory 服务注册进 hwservicemanager 中

    /**
     * 根据注册结果打印日志。
     */
    if (status == OK) {
        ALOGI("Registration complete for %s/%s.",
              interfaceName.c_str(), serviceName.c_str());
    } else {
        ALOGE("Could not register service %s/%s (%d).",
              interfaceName.c_str(), serviceName.c_str(), status);
    }

    /**
     * 返回最终注册结果。
     */
    return status;
}

5.2 核心函数:getRawServiceInternal

该函数通过扫描 /vendor/lib64/hw 等目录,寻找符合命名的 .so 文件。

c 复制代码
// 核心函数:根据 VINTF / 系统配置 / 运行模式,
// 获取指定 HIDL HAL 的"原始实现对象(IBase)"
//
// 该函数是 HIDL getService 体系的底层实现,
// 同时支持:
// 1) HWBinder HAL(独立进程)
// 2) Passthrough HAL(so 直连)
// 3) Legacy HAL(无 VINTF 声明的历史模式)
// 4) Treble 测试 / Stub 场景
sp<::android::hidl::base::V1_0::IBase> getRawServiceInternal(
        const std::string& descriptor,   // 接口全名,如 android.hardware.audio@6.0::IDevicesFactory
        const std::string& instance,     // 实例名,通常是 "default"
        bool retry,                      // 是否允许在 HAL 未启动时重试等待
        bool getStub) {                  // 是否只获取本地 stub(不走 Binder)

    // Transport 表示 HAL 的部署方式(来自 VINTF)
    // - HWBINDER     : HAL 运行在独立进程,通过 Binder 通信
    // - PASSTHROUGH : HAL 以 so 形式直接 dlopen 到当前进程
    // - EMPTY       : VINTF 未声明(legacy 场景)
    using Transport = IServiceManager1_0::Transport;

    // Waiter 用于在 HWBinder 模式下等待 HAL service 注册完成
    // 解决 "client 启动早于 HAL server" 的时序问题
    sp<Waiter> waiter;

    // hwservicemanager(HIDL Service Manager)
    sp<IServiceManager1_1> sm;

    // 默认认为是 legacy(VINTF 未声明)
    Transport transport = Transport::EMPTY;

    // Recovery 模式是一个特例:
    // - 没有 hwservicemanager
    // - 没有完整 Binder 环境
    // 因此只能使用 passthrough HAL
    if (kIsRecovery) {
        transport = Transport::PASSTHROUGH;
    } else {
        // 获取 hwservicemanager
        sm = defaultServiceManager1_1();
		-------------- system/libhidl/transport/ServiceManagement.cpp
		+	// 获取 HIDL ServiceManager(V1.2 版本)的全局单例
		+	//
		+	// 这是 HIDL 世界中"所有 HAL 注册 / 查询"的根节点:
		+	//   - HAL Server 通过它 register()
		+	//   - HAL Client 通过它 get()
		+	//
		+	// 对应的远端服务是:
		+	//   /vendor/bin/hw/android.hidl.manager@1.2-service
		+	//
		+	// 本函数负责:
		+	// 1. 延迟初始化(lazy init)
		+	// 2. 线程安全的单例创建
		+	// 3. 确保 hwservicemanager 已经启动
		+	// 4. 建立当前进程到 hwservicemanager 的 Binder 通道
		+	sp<IServiceManager1_2> defaultServiceManager1_2() {
		+	    using android::hidl::manager::V1_2::BnHwServiceManager;
		+	    using android::hidl::manager::V1_2::BpHwServiceManager;
		+	
		+	    // 全局 mutex:保护 ServiceManager 单例的创建过程
		+	    // 使用 new + 引用,避免静态析构顺序问题(AOSP 常见写法)
		+	    static std::mutex& gDefaultServiceManagerLock = *new std::mutex;
		+	
		+	    // 全局缓存的 IServiceManager 单例
		+	    // 一旦成功获取,后续所有调用都会直接返回
		+	    static sp<IServiceManager1_2>& gDefaultServiceManager =
		+	            *new sp<IServiceManager1_2>;
		+	
		+	    {
		+	        // 保证多线程环境下只初始化一次
		+	        std::lock_guard<std::mutex> _l(gDefaultServiceManagerLock);
		+	
		+	        // 如果已经初始化过,直接返回
		+	        if (gDefaultServiceManager != nullptr) {
		+	            return gDefaultServiceManager;
		+	        }
		+	
		+	        // 检查 hwbinder 设备节点是否存在且可访问
		+	        //
		+	        // 含义:
		+	        // - /dev/hwbinder 不存在 → 设备不支持 HIDL/HwBinder
		+	        // - 或当前进程无权限 → vendor/system 隔离或 SELinux 问题
		+	        //
		+	        // 在这些情况下,HIDL ServiceManager 不可用
		+	        if (access("/dev/hwbinder", F_OK | R_OK | W_OK) != 0) {
		+	            // HwBinder not available on this device or not accessible to
		+	            // this process.
		+	            return nullptr;
		+	        }
		+	
		+	        // 阻塞等待 hwservicemanager 启动完成
		+	        //
		+	        // hwservicemanager 是 HIDL 架构的"中枢服务",
		+	        // 必须先于所有 HAL client / server 启动。
		+	        //
		+	        // 如果它还没 ready,这里会一直等
		+	        waitForHwServiceManager();
		+	
		+	        // 尝试从 Binder context 中获取 hwservicemanager 的 Binder 对象
		+	        //
		+	        // ProcessState::self()->getContextObject(nullptr)
		+	        //   → 获取 Binder Context Manager(类似 system_server 在 system binder 中的角色)
		+	        //
		+	        // fromBinder:
		+	        //   - 把一个通用的 IBinder
		+	        //   - 转换为强类型的 IServiceManager1_2 接口
		+	        //
		+	        // BpHwServiceManager : proxy(client 侧)
		+	        // BnHwServiceManager : stub(server 侧)
		+	        while (gDefaultServiceManager == nullptr) {
		+	            gDefaultServiceManager =
		+	                fromBinder<IServiceManager1_2,
		+	                           BpHwServiceManager,
		+	                           BnHwServiceManager>(
		+	                    ProcessState::self()->getContextObject(nullptr)); // 通过这里拿到了 ServiceManager 的代理 BpHwServiceManager
		+	
		+	            // 理论上不常发生,但在系统启动早期可能出现:
		+	            // - Binder 驱动 ready
		+	            // - hwservicemanager 进程尚未完全注册
		+	            if (gDefaultServiceManager == nullptr) {
		+	                LOG(ERROR) << "Waited for hwservicemanager, but got nullptr.";
		+	                sleep(1);
		+	            }
		+	        }
		+	    }
		+	
		+	    // 返回全局唯一的 hwservicemanager 接口
		+	    return gDefaultServiceManager;
		+	}
		-------------->		
		

        if (sm == nullptr) {
            // 正常系统中不应该发生,属于致命环境错误
            ALOGE("getService: defaultServiceManager() is null");
            return nullptr;
        }

        // 查询 VINTF manifest,确认该 HAL 的 Transport 类型
        // 这是 Treble 架构下的"最终裁判"
        Return<Transport> transportRet = sm->getTransport(descriptor, instance);

        if (!transportRet.isOk()) {
            // 无法从 hwservicemanager 获取 transport,直接失败
            ALOGE("getService: defaultServiceManager()->getTransport returns %s",
                  transportRet.description().c_str());
            return nullptr;
        }
        transport = transportRet;
    }

    // 根据 transport 派生出几种判断标志
    const bool vintfHwbinder = (transport == Transport::HWBINDER);     // 正规 HWBinder HAL
    const bool vintfPassthru = (transport == Transport::PASSTHROUGH); // VINTF 声明为 passthrough

    // Treble 测试模式(CTS / VTS)
    // 在该模式下,系统会放宽某些 VINTF 限制
    const bool trebleTestingOverride = isTrebleTestingOverride();

    // 是否允许 legacy HAL(未写入 VINTF 的 HAL)
    // 典型场景:
    // - 老设备
    // - 非 enforce VINTF 的系统
    // - Treble 测试 + userdebug
    const bool allowLegacy =
            !kEnforceVintfManifest || (trebleTestingOverride && isDebuggable());

    // VINTF 未声明,但系统允许 legacy,则进入 legacy 路径
    const bool vintfLegacy = (transport == Transport::EMPTY) && allowLegacy;

    // 如果没有强制启用 VINTF 校验,打印强烈警告
    // 原因:
    // - HAL 若未写入 VINTF
    // - 且启动较慢
    // 则 client 可能永远无法获取到 HAL(竞态条件)
    if (!kEnforceVintfManifest) {
        ALOGE("getService: Potential race detected. The VINTF manifest is not being enforced. If "
              "a HAL server has a delay in starting and it is not in the manifest, it will not be "
              "retrieved. Please make sure all HALs on this device are in the VINTF manifest and "
              "enable PRODUCT_ENFORCE_VINTF_MANIFEST on this device (this is also enabled by "
              "PRODUCT_FULL_TREBLE). PRODUCT_ENFORCE_VINTF_MANIFEST will ensure that no race "
              "condition is possible here.");
        // 粗暴但有效的兜底,给 HAL 一点启动时间
        sleep(1);
    }

    // =========================
    // HWBinder / Legacy 获取路径
    // =========================
    //
    // 条件说明:
    // - !getStub           : 调用方期望获取真正的 HAL(非 stub)
    // - vintfHwbinder      : 正规 HWBinder HAL
    // - vintfLegacy        : legacy HAL(通过 hwservicemanager 暴露)
    for (int tries = 0; !getStub && (vintfHwbinder || vintfLegacy); tries++) {

        // 第二次及以后尝试时,创建 Waiter
        // 用于等待 HAL service 注册完成
        if (waiter == nullptr && tries > 0) {
            waiter = new Waiter(descriptor, instance, sm);
        }

        // 每一轮尝试前重置 waiter 状态
        if (waiter != nullptr) {
            waiter->reset();  // 顺序不能乱,reset 内部有同步假设
        }

        // 从 hwservicemanager 获取 HAL
        Return<sp<IBase>> ret = sm->get(descriptor, instance); // -----------------重点3 拿到 DevicesFactory 对象
		--------- system/libhidl/transport/ServiceManagement.cpp
		+	Return<sp<IBase>> get(const hidl_string& fqName,
		+	                      const hidl_string& name) override {
		+	    // 最终返回的 HAL 实例(IBase 是所有 HIDL 接口的根)
		+	    sp<IBase> ret = nullptr;
		+	
		+	    /**
		+	     * openLibs 的职责:
		+	     * 1. 根据 fqName(例如 android.hardware.audio@6.0::IDevicesFactory)
		+	     * 2. 找到所有"可能匹配"的 HAL so
		+	     * 3. 逐个 dlopen
		+	     * 4. 对每个 so,回调下面这个 lambda
		+	     *
		+	     * ⚠️ 这是一个"遍历 + 尝试"的模型,不是只试一次
		+	     */
		+	    openLibs(fqName, [&](void* handle,
		+	                         const std::string& lib,
		+	                         const std::string& sym) {
		+	
		+	        /**
		+	         * 每个 Passthrough HAL so 必须导出一个"工厂函数"
		+	         * 典型名字类似:
		+	         *   HIDL_FETCH_IDevicesFactory
		+	         *
		+	         * 这个函数的签名是:
		+	         *   IBase* factory(const char* instanceName)
		+	         */
		+	        IBase* (*generator)(const char* name);
		+	
		+	        // 用 dlsym 从 so 里找这个 factory 符号
		+	        *(void **)(&generator) = dlsym(handle, sym.c_str()); 
		+	
		+	        if (!generator) {
		+	            // so 打开成功,但符号不存在
		+	            // 说明这个 so 并不是我们要的 HAL 实现
		+	            const char* error = dlerror();
		+	            LOG(ERROR)
		+	                << "Passthrough lookup opened " << lib
		+	                << " but could not find symbol " << sym
		+	                << ": " << (error == nullptr ? "unknown error" : error)
		+	                << ". Keeping library open.";
		+	
		+	            /**
		+	             * 为什么不 dlclose?
		+	             *
		+	             * - Passthrough HAL 可能被多个线程同时访问
		+	             * - dlclose + 并发调用 极其容易崩
		+	             * - Android 这里选择"宁愿泄露,也不炸进程"
		+	             */
		+	            return true;  // 继续尝试下一个 so
		+	        }
		+	
		+	        /**
		+	         * 调用 factory 函数
		+	         * name 是 instance name,比如:
		+	         *   "default"
		+	         *   "primary"
		+	         */
		+	        ret = (*generator)(name.c_str()); // 这里会直接调用 HIDL_FETCH_IDevicesFactory 函数 ,这里创建了 DevicesFactory 对象;  -----------------重点1 DevicesFactory 对象创建
		+	
		+	        if (ret == nullptr) {
		+	            // 这个 HAL so 存在,但不支持这个 instance
		+	            LOG(ERROR)
		+	                << "Could not find instance '" << name.c_str()
		+	                << "' in library " << lib
		+	                << ". Keeping library open.";
		+	
		+	            // 同样不 dlclose,理由同上
		+	            return true;  // 继续找其他 so
		+	        }
		+	
		+	        /**
		+	         * ⚠️ 关键设计点:
		+	         *
		+	         * fqName 是"期望的接口名"
		+	         * 但 ret 可能是:
		+	         *   - 子类
		+	         *   - vendor 扩展实现
		+	         *
		+	         * 所以这里要:
		+	         *   1. 通过反射拿到"真实 descriptor"
		+	         *   2. 用真实 fqName 做引用注册
		+	         */
		+	        using ::android::hardware::details::getDescriptor;
		+	        std::string actualFqName = getDescriptor(ret.get());
		+	
		+	        CHECK(actualFqName.size() > 0);
		+	
		+	        /**
		+	         * registerReference 的作用:
		+	         * - 建立 "fqName + instance" → 实例 的映射
		+	         * - 防止重复创建
		+	         * - 便于调试 / 生命周期管理
		+	         */
		+	        registerReference(actualFqName, name);
		+	
		+	        // false 表示:已经找到合适实现,停止遍历
		+	        return false;
		+	    });
		+	----------
		+	+	static void openLibs(
		+	+	        const std::string& fqName,
		+	+	        const std::function<bool /* continue */ (
		+	+	            void* /* handle */,
		+	+	            const std::string& /* lib */,
		+	+	            const std::string& /* sym */)>& eachLib) {
		+	+	
		+	+	    /**
		+	+	     * fqName 形如:
		+	+	     *   android.hardware.audio@7.0::IDevicesFactory
		+	+	     *
		+	+	     * 这是 HIDL 的"完全限定接口名":
		+	+	     *   package + version + interface
		+	+	     *
		+	+	     * Passthrough 模式下:
		+	+	     *   - 不通过 hwservicemanager
		+	+	     *   - 只能靠 fqName 来"反推出" HAL so 的名字
		+	+	     */
		+	+	
		+	+	    // 找到 "::",分离 package/version 与 interface 名
		+	+	    size_t idx = fqName.find("::");
		+	+	
		+	+	    // 如果 fqName 格式非法,直接失败
		+	+	    if (idx == std::string::npos ||
		+	+	            idx + strlen("::") + 1 >= fqName.size()) {
		+	+	        LOG(ERROR) << "Invalid interface name passthrough lookup: " << fqName;
		+	+	        return;
		+	+	    }
		+	+	
		+	+	    /**
		+	+	     * packageAndVersion:
		+	+	     *   android.hardware.audio@6.0
		+	+	     *
		+	+	     * ifaceName:
		+	+	     *   IDevicesFactory
		+	+	     */
		+	+	    std::string packageAndVersion = fqName.substr(0, idx);
		+	+	    std::string ifaceName = fqName.substr(idx + strlen("::"));
		+	+	
		+	+	    /**
		+	+	     * Passthrough HAL 的 so 命名约定:
		+	+	     *
		+	+	     *   <package>@<version>-impl*.so
		+	+	     *
		+	+	     * 举例:
		+	+	     *   android.hardware.audio@6.0-impl.so
		+	+	     *   android.hardware.audio@6.0-impl-primary.so
		+	+	     *
		+	+	     * prefix 用于后面文件扫描
		+	+	     */
		+	+	    const std::string prefix = packageAndVersion + "-impl";
		+	+	
		+	+	    /**
		+	+	     * Passthrough HAL 必须导出的 factory 符号名:
		+	+	     *
		+	+	     *   HIDL_FETCH_<InterfaceName>
		+	+	     *
		+	+	     * 举例:
		+	+	     *   HIDL_FETCH_IDevicesFactory
		+	+	     *
		+	+	     * 这是 HIDL 在编译期"强制约定"的 ABI
		+	+	     */
		+	+	    const std::string sym = "HIDL_FETCH_" + ifaceName;
		+	+	
		+	+	    constexpr int dlMode = RTLD_LAZY;
		+	+	    void* handle = nullptr;
		+	+	
		+	+	    // 清空 dlerror,防止读到旧错误
		+	+	    dlerror();
		+	+	
		+	+	    /**
		+	+	     * HAL 搜索路径说明(非常重要):
		+	+	     *
		+	+	     * Passthrough HAL 是 vendor 提供的 so,
		+	+	     * 所以只能从「允许 vendor 访问的路径」加载
		+	+	     *
		+	+	     * 查找顺序体现了 Android 的分区优先级策略
		+	+	     */
		+	+	    static std::string halLibPathVndkSp = details::getVndkSpHwPath();
		+	+	
		+	+	    std::vector<std::string> paths = {
		+	+	        HAL_LIBRARY_PATH_ODM,      // /odm/lib(64)/hw
		+	+	        HAL_LIBRARY_PATH_VENDOR,   // /vendor/lib(64)/hw
		+	+	        halLibPathVndkSp,           // /system/lib(64)/vndk-sp/hw
		+	+	#ifndef __ANDROID_VNDK__
		+	+	        HAL_LIBRARY_PATH_SYSTEM,   // /system/lib(64)/hw(非 VNDK 构建)
		+	+	#endif
		+	+	    };
		+	+	
		+	+	    /**
		+	+	     * Treble Testing Override 场景:
		+	+	     *
		+	+	     * - VTS / CTS 测试
		+	+	     * - HAL 可能被"静态链接"进测试进程
		+	+	     * - 不存在真实的 so 文件
		+	+	     *
		+	+	     * 因此:
		+	+	     *   dlopen(nullptr) → 表示"当前进程本身"
		+	+	     */
		+	+	    if (details::isTrebleTestingOverride()) {
		+	+	        handle = dlopen(nullptr, dlMode);
		+	+	        if (handle == nullptr) {
		+	+	            const char* error = dlerror();
		+	+	            LOG(ERROR) << "Failed to dlopen self: "
		+	+	                       << (error == nullptr ? "unknown error" : error);
		+	+	        } else if (!eachLib(handle, "SELF", sym)) {
		+	+	            // 如果回调返回 false,表示已经找到目标 HAL
		+	+	            return;
		+	+	        }
		+	+	    }
		+	+	
		+	+	    /**
		+	+	     * 正常路径:
		+	+	     * 依次扫描每一个 HAL 目录
		+	+	     */
		+	+	    for (const std::string& path : paths) {
		+	+	
		+	+	        /**
		+	+	         * findFiles:
		+	+	         *   在指定目录下查找:
		+	+	         *     prefix + "*.so"
		+	+	         *
		+	+	         * 举例:
		+	+	         *   android.hardware.audio@7.0-impl*.so
		+	+	         */
		+	+	        std::vector<std::string> libs = findFiles(path, prefix, ".so");
		+	+	
		+	+	        for (const std::string &lib : libs) {
		+	+	            const std::string fullPath = path + lib;
		+	+	
		+	+	            /**
		+	+	             * recovery 或 system 分区:
		+	+	             *   使用普通 dlopen
		+	+	             *
		+	+	             * vendor / odm:
		+	+	             *   必须使用 android_load_sphal_library
		+	+	             *
		+	+	             * 原因:
		+	+	             *   - linker namespace 隔离
		+	+	             *   - 防止 vendor HAL 直接依赖 system 私有符号
		+	+	             */
		+	+	            if (kIsRecovery || path == HAL_LIBRARY_PATH_SYSTEM) {
		+	+	                handle = dlopen(fullPath.c_str(), dlMode);
		+	+	            } else {
		+	+	#if !defined(__ANDROID_RECOVERY__) && defined(__ANDROID__)
		+	+	                handle = android_load_sphal_library(fullPath.c_str(), dlMode);
		+	+	#endif
		+	+	            }
		+	+	
		+	+	            if (handle == nullptr) {
		+	+	                const char* error = dlerror();
		+	+	                LOG(ERROR) << "Failed to dlopen " << lib << ": "
		+	+	                           << (error == nullptr ? "unknown error" : error);
		+	+	                continue;
		+	+	            }
		+	+	
		+	+	            /**
		+	+	             * eachLib 回调的职责:
		+	+	             *   - dlsym(HIDL_FETCH_xxx)
		+	+	             *   - 调 factory(name)
		+	+	             *   - 判断是否是目标 instance
		+	+	             *
		+	+	             * 返回值含义:
		+	+	             *   true  → 继续找下一个 so
		+	+	             *   false → 已经找到,立即停止遍历
		+	+	             */
		+	+	            if (!eachLib(handle, lib, sym)) {
		+	+	                return;
		+	+	            }
		+	+	        }
		+	    }
		+	}
		+	---------->
		+	
		+	    // 如果找到了就返回实例,找不到就是 nullptr
		+	    return ret; //  -----------------重点2 返回 DevicesFactory 对象
		+	}
		--------->
        if (!ret.isOk()) {
            ALOGE("getService: defaultServiceManager()->get returns %s for %s/%s.",
                  ret.description().c_str(), descriptor.c_str(), instance.c_str());
            break;
        }

        sp<IBase> base = ret; // -----------------重点4 拿到 DevicesFactory 对象
        if (base != nullptr) {
            // 校验返回的 Binder 对象是否真的实现了期望的接口
            // 防止版本不匹配 / descriptor 错误
            Return<bool> canCastRet =
                details::canCastInterface(base.get(), descriptor.c_str(),
                                          true /* emitError */);

            // 接口匹配,成功获取 HAL
            if (canCastRet.isOk() && canCastRet) {
                if (waiter != nullptr) {
                    waiter->done();
                }
                // 返回 raw IBase(上层还会 wrap 成 BpXXX)
                return base; // -----------------重点5 拿到 DevicesFactory 对象
            }

            // 接口不匹配或发生严重错误,是否继续尝试由错误类型决定
            if (!handleCastError(canCastRet, descriptor, instance)) break;
        }

        // legacy HAL 或调用方不允许 retry,则不再等待
        if (vintfLegacy || !retry) break;

        // 等待 HAL server 注册(有超时)
        if (waiter != nullptr) {
            ALOGI("getService: Trying again for %s/%s...",
                  descriptor.c_str(), instance.c_str());
            waiter->wait(true /* timeout */);
        }
    }

    // 清理 waiter 状态
    if (waiter != nullptr) {
        waiter->done();
    }

    // =========================
    // Passthrough / Stub 路径
    // =========================
    //
    // 以下情况会进入:
    // - getStub == true          : 明确要求本地 stub
    // - vintfPassthru == true    : VINTF 声明为 passthrough
    // - vintfLegacy == true      : legacy HAL
    if (getStub || vintfPassthru || vintfLegacy) {

        // Passthrough Service Manager
        // 负责 dlopen HAL so 并创建实例
        const sp<IServiceManager1_0> pm = getPassthroughServiceManager();
        if (pm != nullptr) {
            sp<IBase> base = pm->get(descriptor, instance).withDefault(nullptr);

            // wrapPassthrough:
            // 将本地 C++ 对象包装成 Binder 风格接口,
            // 使上层代码无需关心 transport 类型
            if (!getStub || trebleTestingOverride) {
                base = wrapPassthrough(base);
            }
            return base;
        }
    }

    // 所有路径失败,返回 nullptr
    return nullptr;
}

6. 对象的创建:HIDL_FETCH_xxx 工厂

在加载 .so 后,系统通过约定好的符号名调用工厂函数。

c 复制代码
IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name) {
    return strcmp(name, "default") == 0 ? new DevicesFactory() : nullptr;
}

6.1 继承关系

DevicesFactory 实现类必须严格继承自生成的 HIDL 接口类。

c 复制代码
class DevicesFactory : public IDevicesFactory 
struct IDevicesFactory : public ::android::hidl::base::V1_0::IBase

7. 服务发现:AudioFlinger 如何连接 HAL

AudioFlinger 启动时,并不会由于 HAL 启动较慢而挂起。它采用了一种"注册-监听"的异步模式。

7.1 订阅通知

c 复制代码
void DevicesFactoryHalHidl::onFirstRef() {
    // 获取 hwservicemanager 的 Binder 代理
    sp<IServiceManager> sm = IServiceManager::getService();
    ALOG_ASSERT(sm != nullptr, "Hardware service manager is not running");
    
    // 创建一个监听器对象
    sp<ServiceNotificationListener> listener = new ServiceNotificationListener(this);
    // 向 hwservicemanager 注册监听
    Return<bool> result = sm->registerForNotifications(
            IDevicesFactory::descriptor, "", listener);
    if (result.isOk()) {
        ALOGE_IF(!static_cast<bool>(result),
                "Hardware service manager refused to register listener");
    } else {
        ALOGE("Failed to register for hardware service manager notifications: %s",
                result.description().c_str());
    }
}

7.2 异步获取服务

hwservicemanager 通知 HAL 已上线,AudioFlinger 才会真正调用 getService()

c 复制代码
class ServiceNotificationListener : public IServiceNotification {
  public:
    explicit ServiceNotificationListener(sp<DevicesFactoryHalHidl> factory)
            : mFactory(factory) {}

	// 当 DevicesFactory 注册这里就会拿到通知 
    Return<void> onRegistration(const hidl_string& /*fully_qualified_name*/,
            const hidl_string& instance_name,
            bool /*pre_existing*/) override {
        if (static_cast<std::string>(instance_name) == "default") return Void();
        sp<DevicesFactoryHalHidl> factory = mFactory.promote();
        if (!factory) return Void();
        sp<IDevicesFactory> halFactory = IDevicesFactory::getService(instance_name); // 这里 获得了 DevicesFactory 的 Bp 代理
        if (halFactory) {
            factory->addDeviceFactory(halFactory, true /*needToNotify*/);
        }
        return Void();
    }

  private:
    wp<DevicesFactoryHalHidl> mFactory;
};

8. 总结

Android Audio HAL 的启动是一套严密的流程:

  1. 进程就绪init 开启沙盒进程。
  2. 环境搭建main 配置多种 Binder 池。
  3. 动态扫描 :利用 dlopen 寻找厂商实现的 .so
  4. 服务挂名 :在 hwservicemanager 注册。
  5. 异步握手AudioFlinger 通过监听机制最终实现跨进程调用。

这种设计保证了系统服务与厂商驱动的强解耦,即便硬件驱动初始化缓慢,也不会导致上层核心服务崩溃。

相关推荐
恋猫de小郭2 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker7 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴7 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭17 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab18 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋1 天前
Android 协程时代,Handler 应该退休了吗?
android