ANR源码分析之Service Timeout

本文详细分析了Service Timeout的流程,指出由于Service默认在UI线程运行,所以不能执行耗时操作以避免ANR。流程从startService开始,通过ContextImpl.java的realStartServiceLocked,再到ActivityThread的scheduleCreateService,服务超时时间默认为20s,后台线程则为200s。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 在前面的一篇文章中,分析了Broadcast Timeout的流程,接下来继续分析Service Timeout的流程。Service默认不会运行在子线程中,它也不会运行在一个独立的进程中,它同样执行在UI线程中,因此也不能在Service中执行耗时操作,否则也会产生的ANR。

 Service Timeout整体流程如下图所示:



1.startService(ContextImpl.java)

/*
    * 启动服务
    */
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, mUser);
    }


    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);//验证Service的Intent
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());//调用AMS的start方法


            ........
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
 2.startService(ActivityManagerService.java)
 在ActivityManagerService.java类中
 public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        ......
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);//调用ActiveServices的startServiceLocked方法
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }
3.startServiceLocked(ActiveServices.java)
    在ActiveServices.java类中
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, String callingPackage, final int userId)
            throws TransactionTooLargeException {


        final boolean callerFg;
        if (caller != null) {
         final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
         .........
         //调用者不是处于后台线程组中
         callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
&n
<think>嗯,用户查询的是关于Android Binder超时问题的解决方案和调试方法。这明显是一个系统级开发问题,用户可能是Android framework工程师或系统开发者,遇到了Binder通信超时的困扰。 从用户提供的引用片段来看,虽然引用内容本身与Binder无直接关联,但引用[3]展示了AudioFlinger的代码片段,说明用户可能具备阅读Android系统源码的能力。用户真正需要的应该是实用的调试技巧和根本解决方案。 Binder超时通常表现为"Binder transaction failed"或"DeadObjectException"错误,核心原因是主线程阻塞导致IPC调用超时。需要从多个维度来解决: 首先是超时机制本身,Android的Binder事务默认有1分钟超时限制(SYNCHRONOUS),但关键服务如ActivityManager会缩短到10-20秒。这个可以通过adb shell dumpsys activity service命令验证具体服务的超时阈值。 然后是调试手段,最直接的是分析traces.txt。当发生ANR时,系统会生成这个文件,其中阻塞Binder调用的线程栈会明确显示在"Binder thread"等待队列里。比如看到"Binder:1234_4"线程状态为"nativePollOnce",就说明该Binder线程被占用。 另一个利器是binder_stats。通过adb shell cat /sys/kernel/debug/binder/stats可以查看事务失败计数,重点观察"failed transaction"字段的数值变化。如果特定进程的数值持续增长,就能锁定问题进程。 对于死锁检测,需要同时抓取客户端和服务端的线程堆栈。如果客户端显示"Binder.transact"阻塞,而服务端对应Binder线程卡在某个同步调用,就形成了典型的跨进程死锁。这时需要检查服务端是否在Binder方法中执行了耗时操作。 解决方案方面,最根本的是遵循Binder最佳实践:避免在主线程进行IPC、确保服务端方法执行时间短、跨进程传递大对象时使用Ashmem。对于必须的耗时操作,应该移出Binder线程,比如用HandlerThread处理。 特别要注意的是,Android 10之后引入了"StrictMode"增强检测,会主动报告主线程Binder调用。我们可以通过StrictMode.getVmPolicy()来启用这项检测。 最后补充一个实用技巧:通过adb shell service call activity 1599295570可以动态调整Binder调试级别,其中最后一个参数3表示启用所有调试信息(1=基本,2=详细,3=完整)。这对追踪偶发问题特别有效。</think>以下是针对Android Binder超时问题的解决方案和调试方法,结合系统机制与实践经验整理: --- ### **一、Binder超时核心机制** 1. **默认超时时间** Binder事务默认超时为**1分钟**(`SYNCHRONOUS`模式),但关键服务(如`ActivityManager`)会缩短至**10-20秒**。 ```c // frameworks/native/libs/binder/IPCThreadState.cpp status_t IPCThreadState::executeCommand(int32_t cmd) { switch (cmd) { case BR_TRANSACTION: { if (tr.target.ptr) { // 事务执行入口 error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags); } if ((tr.flags & TF_ONE_WAY) == 0) { // 同步调用 reply->writeInt32(error); sendReply(reply, 0); // 触发超时计时 } } } } ``` --- ### **二、调试方法** #### 1. **定位超时根源** - **查看日志** 过滤日志关键词: ```bash adb logcat | grep -E "Binder transaction|DeadObjectException|NOT RESPONDING" ``` - **ANR Traces分析** 获取ANR文件: ```bash adb pull /data/anr/traces.txt ``` 搜索`Binder thread`状态,确认阻塞位置(如`nativePollOnce`表示空闲等待)。 #### 2. **监控Binder状态** - **Binder事务统计** ```bash adb shell cat /sys/kernel/debug/binder/stats ``` 关注`failed transaction`计数和具体错误码(如`-32`表示`TIMED_OUT`)。 - **进程Binder线程数** ```bash adb shell ps -T | grep <process_name> ``` 检查Binder线程是否耗尽(默认上限为`15`)。 #### 3. **动态调试** - **增加Binder调试日志** 动态启用内核级日志: ```bash adb shell "echo 1 > /sys/module/binder/parameters/debug_mask" ``` - **StrictMode检测主线程IPC** 在开发者选项中启用**"严格模式"**,屏幕闪烁提示主线程Binder调用。 --- ### **三、解决方案** #### 1. **客户端优化** - **异步调用** 使用`oneway`关键字声明接口(`AIDL`): ```java interface IMyService { oneway void asyncCall(in Bundle data); // 非阻塞调用 } ``` - **缩短超时时间** 自定义超时阈值(谨慎使用): ```java Bundle.setClassLoader(getClass().getClassLoader()); Message msg = Message.obtain(); msg.what = MSG_CALL; mHandler.sendMessageDelayed(msg, 5000); // 设置5秒超时 ``` #### 2. **服务端优化** - **避免耗时操作** 将复杂逻辑移至工作线程: ```java @Override public void onTransact(int code, Parcel data, Parcel reply, int flags) { if (code == LONG_OPERATION) { new Thread(() -> { // 耗时任务 reply.writeInt(RESULT_OK); }).start(); return true; } return super.onTransact(code, data, reply, flags); } ``` - **拆分大事务** 跨进程传输大数据时使用`ashmem`共享内存: ```java Parcel parcel = Parcel.obtain(); parcel.writeFileDescriptor(ashmemFd); // 传递文件描述符 ``` #### 3. **系统级调整** - **增加Binder线程池** 修改进程的Binder线程上限(需root): ```bash adb shell "echo 25 > /proc/<pid>/settings/max_binder_threads" ``` - **调整内核参数** 修改Binder事务缓冲区大小(仅调试用): ```bash adb shell "echo 16777216 > /sys/module/binder/parameters/debug_size" ``` --- ### **四、典型场景案例** 1. **死锁场景** **现象**:客户端A持有锁等待服务端B响应,同时服务端B等待客户端A释放锁。 **解决**:使用`adb shell dumpsys activity processes`检查线程依赖链,避免跨进程嵌套锁。 2. **服务阻塞** **案例**:`AudioFlinger`的Binder线程因音频处理阻塞[^3]。 **方案**:将`removeEffect_l()`等耗时操作移出Binder线程。 --- ### **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值