第十一板块:Android 跨进程通信与 Binder 深度剖析 | 第二十七篇:Binder 线程池与死亡通知(Death Recipient)机制

第十一板块:Android 跨进程通信与 Binder 深度剖析 | 第二十七篇:Binder 线程池与死亡通知(Death Recipient)机制

所属板块:第十一板块 --- Android 跨进程通信与 Binder 深度剖析

前置知识:第二十六篇中的 Systrace/Perfetto 性能分析、第十板块中的 Watchdog/ANR 机制、Linux 进程管理与线程调度、Signal 机制

本篇定位 :这是 Android 系统跨进程通信(IPC)的基石与生命线 。如果说 Binder 是神经,那么 Binder 线程池 就是 神经突触的传导效率 ,而 Death Recipient 就是 神经元死亡的预警机制 。本篇将彻底拆解 Binder 线程池的调度算法BC_TRANSACTION/BC_REPLY 的内核流转线程优先级(Nice Value)与 cgroups 的映射Death Recipient 的注册与触发链路 。我们将深入 Kernel Binder Driverlibbinder 库Framework Service,揭示 Android 如何在高并发 IPC 场景下避免死锁与资源耗尽。全程无 AIDL 使用教程、无 IPC 优化建议,仅保留 Binder 内核与 Framework 的底层定义与调度规范。


1. 核心结论先行(Thesis Statement)

Binder 的通信模型是一个基于线程池的同步阻塞模型

  • Binder 线程池的本质系统服务处理并发请求的消费者队列。每个应用进程(Client)和每个系统服务进程(Server)都维护一个 Binder 线程池。Client 端的调用线程会阻塞,直到 Server 端的某个 Binder 线程处理完请求并返回。
  • Death Recipient 的本质跨进程的弱引用与心跳检测。它是 Client 在 Binder Driver 中注册的一个回调,当 Server 进程死亡(被 Kill 或 Crash)时,Driver 会通知 Client 清理资源,防止悬空引用(Dangling Reference)。
  • 同步阻塞的本质等待队列(Wait Queue) 。Client 线程发送 BC_TRANSACTION 后,会进入 wait_event_interruptible 状态,直到收到 BR_REPLY 或超时。
  • 线程池上限的本质防止资源耗尽(DoS)。Binder Driver 限制每个进程的 Binder 线程数量(默认为 15),防止恶意应用耗尽系统线程资源。

2. Binder 线程池架构全景图

2.1 从 Client 调用到 Server 响应的完整链路

#mermaid-svg-ClhqAhu1YL5djdZf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ClhqAhu1YL5djdZf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ClhqAhu1YL5djdZf .error-icon{fill:#552222;}#mermaid-svg-ClhqAhu1YL5djdZf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ClhqAhu1YL5djdZf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ClhqAhu1YL5djdZf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ClhqAhu1YL5djdZf .marker.cross{stroke:#333333;}#mermaid-svg-ClhqAhu1YL5djdZf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ClhqAhu1YL5djdZf p{margin:0;}#mermaid-svg-ClhqAhu1YL5djdZf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ClhqAhu1YL5djdZf .cluster-label text{fill:#333;}#mermaid-svg-ClhqAhu1YL5djdZf .cluster-label span{color:#333;}#mermaid-svg-ClhqAhu1YL5djdZf .cluster-label span p{background-color:transparent;}#mermaid-svg-ClhqAhu1YL5djdZf .label text,#mermaid-svg-ClhqAhu1YL5djdZf span{fill:#333;color:#333;}#mermaid-svg-ClhqAhu1YL5djdZf .node rect,#mermaid-svg-ClhqAhu1YL5djdZf .node circle,#mermaid-svg-ClhqAhu1YL5djdZf .node ellipse,#mermaid-svg-ClhqAhu1YL5djdZf .node polygon,#mermaid-svg-ClhqAhu1YL5djdZf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ClhqAhu1YL5djdZf .rough-node .label text,#mermaid-svg-ClhqAhu1YL5djdZf .node .label text,#mermaid-svg-ClhqAhu1YL5djdZf .image-shape .label,#mermaid-svg-ClhqAhu1YL5djdZf .icon-shape .label{text-anchor:middle;}#mermaid-svg-ClhqAhu1YL5djdZf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ClhqAhu1YL5djdZf .rough-node .label,#mermaid-svg-ClhqAhu1YL5djdZf .node .label,#mermaid-svg-ClhqAhu1YL5djdZf .image-shape .label,#mermaid-svg-ClhqAhu1YL5djdZf .icon-shape .label{text-align:center;}#mermaid-svg-ClhqAhu1YL5djdZf .node.clickable{cursor:pointer;}#mermaid-svg-ClhqAhu1YL5djdZf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ClhqAhu1YL5djdZf .arrowheadPath{fill:#333333;}#mermaid-svg-ClhqAhu1YL5djdZf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ClhqAhu1YL5djdZf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ClhqAhu1YL5djdZf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ClhqAhu1YL5djdZf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ClhqAhu1YL5djdZf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ClhqAhu1YL5djdZf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ClhqAhu1YL5djdZf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ClhqAhu1YL5djdZf .cluster text{fill:#333;}#mermaid-svg-ClhqAhu1YL5djdZf .cluster span{color:#333;}#mermaid-svg-ClhqAhu1YL5djdZf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ClhqAhu1YL5djdZf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ClhqAhu1YL5djdZf rect.text{fill:none;stroke-width:0;}#mermaid-svg-ClhqAhu1YL5djdZf .icon-shape,#mermaid-svg-ClhqAhu1YL5djdZf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ClhqAhu1YL5djdZf .icon-shape p,#mermaid-svg-ClhqAhu1YL5djdZf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ClhqAhu1YL5djdZf .icon-shape .label rect,#mermaid-svg-ClhqAhu1YL5djdZf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ClhqAhu1YL5djdZf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ClhqAhu1YL5djdZf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ClhqAhu1YL5djdZf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Server 进程 (System Server)
Linux 内核
Client 进程 (App)

  1. transact()
  2. ioctl(BINDER_WRITE_READ)
  3. 查找 Node
  4. 唤醒 Server 线程
  5. 执行 onTransact()
  6. 返回结果
  7. ioctl(BR_REPLY)
  8. 唤醒 Client 线程
  9. 返回结果
  10. 返回结果
    UI Thread (Binder Client)
    Binder Thread A
    Binder Thread B
    Binder Driver
    Binder Context (binder_proc)
    Binder Node (binder_node)
    等待队列 (wait_event)
    Binder Thread 1 (Pool)
    Binder Thread 2 (Pool)
    Binder Thread N (Max)
    ActivityManagerService

2.2 核心组件职责表

组件 层级 职责 学术定义
Binder Thread Pool Framework 消费者 Server 端用于处理 Binder 请求的线程集合。
binder_proc Kernel 进程上下文 内核中代表一个 Binder 进程的数据结构,管理线程、节点和缓冲区。
binder_node Kernel 实体引用 内核中代表一个 Binder 实体(如 AMS)的数据结构,包含强弱引用计数。
BC_TRANSACTION Kernel 命令 Client 发送给 Driver 的请求命令。
BR_REPLY Kernel 返回 Driver 返回给 Client 的响应命令。
Death Recipient Framework 死亡通知 Client 注册在 Driver 中的回调,用于监听 Server 死亡。

3. Binder 线程池的调度机制

3.1 线程池的创建与增长

Binder 线程池是按需创建的。

学术定义

  • 初始线程 :进程启动时创建的第一个 Binder 线程(通常是 main 线程或 binder_thread)。
  • 动态增长:当请求到来且没有空闲线程时,Driver 通知进程创建新线程,直到达到上限。
  • 上限(MAX_THREADS):默认值为 15。这是为了防止线程过多导致上下文切换开销过大和内存耗尽。

源码逻辑(libbinder)

cpp 复制代码
// frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::spawnPooledThread(bool isMain) {
    if (mThreadPoolStarted) {
        // 创建新的 Binder 线程
        sp<Thread> t = new PoolThread(isMain);
        t->run(name);
    }
}

3.2 线程优先级与 cgroups

Binder 线程的优先级直接影响 IPC 的响应速度。

线程类型 Nice Value cgroups 学术定义
System Server Binder -10 (高优先级) system-background 系统服务需要快速响应,防止 ANR。
App Binder 0 (默认) foreground / background 应用线程优先级随进程状态变化。
Lowmem Binder 10 (低优先级) background 低内存时运行的 Binder 线程。

学术定义

  • Nice Value: Linux 的进程优先级。值越小,优先级越高,获得的 CPU 时间片越多。
  • cgroups (Control Groups): Linux 内核机制,用于限制、记录和隔离进程组的资源(CPU、内存)。Android 使用 cgroups 确保前台进程获得更多 CPU 资源。

4. Death Recipient 机制

4.1 为什么需要 Death Recipient?

如果没有 Death Recipient,当 Server 进程死亡时:

  1. Client 持有的 Binder 代理(BpBinder)会变成僵尸引用
  2. Client 再次调用该 Binder 时,会向已死亡的进程发送请求,导致 SIGPIPE 信号或 Binder Error
  3. 系统可能崩溃或行为异常。

4.2 Death Recipient 的注册流程

Server 进程 Binder Driver Client 进程 Server 进程 Binder Driver Client 进程 #mermaid-svg-zEQhPdYoiBOZoH3d{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zEQhPdYoiBOZoH3d .error-icon{fill:#552222;}#mermaid-svg-zEQhPdYoiBOZoH3d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zEQhPdYoiBOZoH3d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zEQhPdYoiBOZoH3d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zEQhPdYoiBOZoH3d .marker.cross{stroke:#333333;}#mermaid-svg-zEQhPdYoiBOZoH3d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zEQhPdYoiBOZoH3d p{margin:0;}#mermaid-svg-zEQhPdYoiBOZoH3d .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-zEQhPdYoiBOZoH3d text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-zEQhPdYoiBOZoH3d .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-zEQhPdYoiBOZoH3d .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-zEQhPdYoiBOZoH3d #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-zEQhPdYoiBOZoH3d .sequenceNumber{fill:white;}#mermaid-svg-zEQhPdYoiBOZoH3d #sequencenumber{fill:#333;}#mermaid-svg-zEQhPdYoiBOZoH3d #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-zEQhPdYoiBOZoH3d .messageText{fill:#333;stroke:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-zEQhPdYoiBOZoH3d .labelText,#mermaid-svg-zEQhPdYoiBOZoH3d .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .loopText,#mermaid-svg-zEQhPdYoiBOZoH3d .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-zEQhPdYoiBOZoH3d .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-zEQhPdYoiBOZoH3d .noteText,#mermaid-svg-zEQhPdYoiBOZoH3d .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-zEQhPdYoiBOZoH3d .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-zEQhPdYoiBOZoH3d .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-zEQhPdYoiBOZoH3d .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-zEQhPdYoiBOZoH3d .actorPopupMenu{position:absolute;}#mermaid-svg-zEQhPdYoiBOZoH3d .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-zEQhPdYoiBOZoH3d .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-zEQhPdYoiBOZoH3d .actor-man circle,#mermaid-svg-zEQhPdYoiBOZoH3d line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-zEQhPdYoiBOZoH3d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} linkToDeath(BpBinder, DeathRecipient)记录 DeathRecipient 到 binder_node正常运行进程崩溃 (Crash/Kill)进程退出通知遍历 binder_node 的死亡通知列表binder_death (通知)DeathRecipient.binderDied() 回调清理资源,重新连接

4.3 内核层的实现

在 Kernel Binder Driver 中,binder_node 维护一个死亡通知链表。

c 复制代码
// drivers/android/binder.c
struct binder_node {
    struct hlist_head refs; /* 引用该节点的 binder_ref */
    struct list_head death;  /* 死亡通知列表 (binder_ref_death) */
    int internal_strong_refs;
    int local_strong_refs;
};

struct binder_ref_death {
    struct binder_ref *ref;
    struct list_head node; /* 挂在 binder_node.death 上 */
    void __user *cookie;   /* Client 提供的标识 */
};

学术定义

  • 弱引用(Weak Ref):Death Recipient 本质上是对 Binder 实体的弱引用。当实体死亡时,通知所有持有弱引用的对象。
  • Cookie:Client 传给 Driver 的一个指针,Driver 在通知时会原样返回,用于 Client 识别是哪个 Binder 对象死亡了。

5. Binder 阻塞与 ANR 的关联

5.1 同步阻塞模型的风险

Binder 调用是同步阻塞的。这意味着:

  1. Client 线程阻塞:Client 的调用线程会一直阻塞,直到 Server 返回。
  2. Server 线程阻塞:如果 Server 在处理请求时发生死锁或长时间 IO,Server 的 Binder 线程会阻塞。
  3. 连锁反应:如果 Server 是 System Server(如 AMS),那么它的 Binder 线程池可能被耗尽,导致整个系统无响应(System ANR)。

5.2 Oneway 调用的非阻塞特性

为了避免阻塞,可以使用 oneway 关键字。

调用类型 阻塞 返回值 适用场景
普通调用 需要结果的请求(如查询数据)。
Oneway 调用 通知类请求(如通知音量变化)。

学术定义

  • Oneway : Client 发送 BC_TRANSACTION 后立即返回,不等待 BR_REPLY。Server 收到请求后异步处理。
  • 顺序性保证 :Oneway 调用在同一个 Binder 实体上是串行 的,但在不同实体上可以并行

6. 关键源码深度解析

6.1 Binder Driver 的线程调度

c 复制代码
// drivers/android/binder.c
static int binder_thread_read(struct binder_proc *proc,
                             struct binder_thread *thread,
                             binder_uintptr_t binder_buffer,
                             size_t size,
                             int non_block) {
    // 1. 检查是否有事务需要处理
    if (list_empty(&thread->todo)) {
        // 2. 如果没有,进入等待队列
        ret = wait_event_interruptible(thread->wait, !list_empty(&thread->todo));
    }

    // 3. 从 todo 队列中取出事务
    struct binder_transaction *t = list_first_entry(&thread->todo, ...);

    // 4. 将事务转换为 BR_REPLY 或 BR_TRANSACTION 返回给用户空间
    put_user(cmd, ptr);
}

6.2 Death Recipient 的触发

c 复制代码
// drivers/android/binder.c
static void binder_release_work(struct binder_proc *proc,
                               struct list_head *work_list) {
    struct binder_work *w;
    list_for_each_entry(w, work_list, entry) {
        switch (w->type) {
        case BINDER_WORK_DEAD_BINDER:
            // 发送死亡通知
            binder_send_death_notification(proc, w);
            break;
        }
    }
}

7. 线程池与 Death Recipient 的常见误区

误区 学术解释
Binder 调用是异步的 默认不是。它是同步阻塞的,除非显式声明 oneway
Death Recipient 能防止 Crash 不能。它只能在对方死后清理资源,无法阻止对方死亡。
Binder 线程越多越好 错误。线程过多会导致上下文切换开销剧增,反而降低性能。
Client 死等 Server 是的。如果 Server 死锁,Client 会一直阻塞,最终导致 ANR。

8. 本篇总结(Knowledge Closure)

关键点 纯学术定义
Binder 线程池的本质 系统服务处理并发请求的消费者队列,受内核上限限制。
同步阻塞模型 Client 线程发送请求后进入等待队列,直到 Server 返回。
Death Recipient 的本质 跨进程的弱引用监听机制,用于检测 Server 进程死亡。
Oneway 调用 非阻塞调用,Client 不等待结果,适用于通知场景。
线程优先级 Binder 线程的 Nice Value 和 cgroups 直接影响 IPC 响应速度。

9. 第十一板块结语

至此,第十一板块:Android 跨进程通信与 Binder 深度剖析 的第一篇已完成。

我们从 Binder 线程池的调度算法 出发,深入 Death Recipient 的注册与触发 ,探索 同步阻塞模型的 ANR 风险 ,最终抵达 Oneway 调用的非阻塞特性

我们揭示了 Binder 通信的核心逻辑:用线程池消化并发,用阻塞等待结果,用死亡通知维护系统健壮性。

下一篇预告第十一板块:Android 跨进程通信与 Binder 深度剖析 | 第二十八篇:Binder 内存映射(mmap)与 FD 跨进程传输

相关推荐
jushi89991 小时前
FB Neo 街机模拟器全游戏整合版 含25000+街机游戏怀旧复古街机游戏 解压即玩 热门怀旧街机游戏全集安卓+PC电脑版
android·游戏·电脑
vensli1 小时前
体验专题——Android 应用瘦身实战
android·网络
AFinalStone1 小时前
Android12 U盘插拔链路源码全解析(七):应用层 —— MediaScanner与SAF
android·frameworks
AI玫瑰助手1 小时前
Python模块:import导入模块与模块的搜索路径
android·开发语言·python
yewq-cn2 小时前
Android Log System
android
问心无愧05133 小时前
ctf show web入门107
android·前端·笔记·android studio
AI科技星3 小时前
第三卷:质数王朝志 第四章:RSA护国玄阵,质数锁天地,一数镇万法
android·人工智能·架构·概率论·学习方法
AFinalStone3 小时前
Android12 U盘插拔链路源码全解析(八)实战调试与案例分析
android·frameworks
我命由我123453 小时前
Android 开发问题:View 的 getWidth、getHeight 方法返回的值都为 0
android·java·java-ee·android studio·android jetpack·android-studio·android runtime