Android-Service-ANR-的监控机制

本文详细分析了Android系统中Service启动时Anr机制如何通过监控并设置延时消息来检测服务响应超时。从跨进程通信、Service创建流程到AMS中解除延时炸弹的过程都被深入剖析。
摘要由CSDN通过智能技术生成

return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

到此,Anr机制在监控Service startService时的第一步已经迈出了,那就是,埋入一个延时消息(延时炸弹)。那么埋入了延时炸弹干什么呢?当然是为了能够在有效时间内解除这个炸弹咯。如果这个炸弹最终在延时时间内没有被移除,则Handler最终被dispatch到该消息的时候,不就触发了Anr了吗。这就是我们Anr的一个整体猜想。下面咱就验证一下这个想法吧。

在AS中的scheduleCreateService()方法,执行了bumpServiceExecutingLocked()之后,调用了app.thread.scheduleCreateService()这句话,跨进程调用到了发起startService这个进程中的ApplicationThread类,它是ActivityThread的内部类,通过Binder机制负责跨进程通信。下面是该类执行scheduleCreateService()的内容:

//ApplicationThread 代码:
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;

sendMessage(H.CREATE_SERVICE, s);
}

//ActivityThread代码:
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);//类H,继承Handler,专门接收AMS调度过来的四大组件调度消息。
}

//H代码:
public void handleMessage(Message msg) {
switch (msg.what) {
。。。//省略
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
。。。//省略
}
}

//ActivityThread代码:
private void handleCreateService(CreateServiceData data) {
...//省略

try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);

Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();//调用onCreate()
mServices.put(data.token, service);
try {//跨进程传达service创建成功。
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}


由上面的核心代码片段可以看出,最终Service的创建流程会由sys_server进程中的AMS,跨进程调用ApplicationThread,在App进程通过Handler发送消息的形式,执行handleCreateService(),调用Service.onCreate()后,再跨进程通知AMSserviceDoneExecuting()。走到这里,Service在App进程就创建完毕了。onCreate()执行完成。

这一步的流程中,埋下的炸弹时间也在悄然流逝。我们继续往下看,接下来就看AMS中如何处理炸弹了。

AMS被调用到了serviceDoneExecuting() 方法后,会调用AS的serviceDoneExecutingLocked()。 在处理了Service.START_STICKY等各种乱七八糟的标记位之后,走到serviceDoneExecutingLocked()方法,这里真正执行到了“拆除炸弹"的过程,将此前埋入的延时消息remove。

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
//省略...
r.executeNesting--;
if (r.executeNesting <= 0) {
if (r.app != null) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"Nesting at 0 of " + r.shortName);
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortName);
//拆除炸弹!!!
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
} else if (r.executeFg) {
// Need to re-evaluate whether the app still needs to be in the foreground.
for (int i=r.app.executingServices.size()-1; i>=0; i--) {
if (r.app.executingServices.valueAt(i).executeFg) {
r.app.execServicesFg = true;
break;
}
}
}
if (inDestroying) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"doneExecuting remove destroying " + r);
mDestroyingServices.remove(r);
r.bindings.clear();
}
mAm.updateOomAdjLocked(r.app, true);
}
r.executeFg = false;
if (r.tracker != null) {
r.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
if (finishing) {
r.tracker.clearCurrentOwner(r, false);
r.tracker = null;
}
}
if (finishing) {
if (r.app != null && !r.app.persistent) {
r.app.services.remove(r);
if (r.whitelistManager) {
updateWhitelistManagerLocked(r.app);
}
}
r.app = null;
}
}
}


那么问题来了,如果在调用mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);的时候发现没有该消息了会发生什么呢? 那其实就说明,mAm.mHandler 已经处理了该消息。我们进入到mAm.mHandler中去一探究竟,它收到该消息会做什么事情。

final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//...省略
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
//...省略
}
//...省略
}
//...省略


它委托了AS去执行serviceTimeout(),



### 最后

简历首选内推方式,速度快,效率高啊!然后可以在拉钩,boss,脉脉,大街上看看。简历上写道熟悉什么技术就一定要去熟悉它,不然被问到不会很尴尬!做过什么项目,即使项目体量不大,但也一定要熟悉实现原理!不是你负责的部分,也可以看看同事是怎么实现的,换你来做你会怎么做?做过什么,会什么是广度问题,取决于项目内容。但做过什么,达到怎样一个境界,这是深度问题,和个人学习能力和解决问题的态度有关了。大公司看深度,小公司看广度。大公司面试你会的,小公司面试他们用到的你会不会,也就是岗位匹配度。

面试过程一定要有礼貌!即使你觉得面试官不尊重你,经常打断你的讲解,或者你觉得他不如你,问的问题缺乏专业水平,你也一定要尊重他,谁叫现在是他选择你,等你拿到offer后就是你选择他了。

另外,描述问题一定要慢!不要一下子讲一大堆,慢显得你沉稳、自信,而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide,所以会有个弊端,当我们在面试讲解很容易不知道某个方法怎么读,这是一个硬伤......所以一定要对常见的关键性的类名、方法名、关键字读准,有些面试官不耐烦会说“你到底说的是哪个?”这时我们会容易乱了阵脚。正确的发音+沉稳的描述+好听的嗓音决对是一个加分项!

最重要的是心态!心态!心态!重要事情说三遍!面试时间很短,在短时间内对方要摸清你的底子还是比较不现实的,所以,有时也是看眼缘,这还是个看脸的时代。

希望大家都能找到合适自己满意的工作!


**进阶学习视频**

![](https://img-blog.csdnimg.cn/img_convert/5336b350e1ef07e103e98c1653d61ee0.webp?x-oss-process=image/format,png)

**附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题** (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

![](https://img-blog.csdnimg.cn/img_convert/3a406e5e5f7b2378f4b59ce059fa2ca8.webp?x-oss-process=image/format,png)
**《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》**,**[点击传送门](https://bbs.csdn.net/topics/618165277),即可获取!**
droid扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中...(img-jvVw7pNG-1714699150882)]
**《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》**,**[点击传送门](https://bbs.csdn.net/topics/618165277),即可获取!**
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值