ANR原理分析
ANR原理分析
造成ANR原因:
InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()
函数时10秒没有处理完成,后台为60秒。
Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
ContentProvider Timeout :ContentProvider的publish在10s内没进行完。
避免
尽量不在主线程(UI线程)中做耗时操作
分析方法
log
log中记录了ANR发生的时间以及具体提示。WaitingInMainSignalCatcherLoop
代表了主线程等待异常。The application may be doing too much work on its main thread.
同样表示处理时间过多。
traces
刚才的log有第二句Wrote stack traces to '/data/anr/traces.txt'
,说明ANR异常已经输出到traces.txt
文件,使用adb命令把这个文件从手机里导出来。
通过adb pull /data/anr/traces.txt
将traces文件导出
----- pid 23346 at 2017-11-07 11:33:57 ----- ----> 进程id和ANR产生时间
Cmd line: com.sky.myjavatest
Build fingerprint: 'google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=4681 post zygote classes=106
Intern table: 42675 strong; 137 weak
JNI: CheckJNI is on; globals=526 (plus 22 weak)
Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so
/system/lib64/libjavacrypto.so
/system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libsoundpool.so
/system/lib64/libwebviewchromium_loader.so libjavacore.so libopenjdk.so (9)
Heap: 22% free, 1478KB/1896KB; 21881 objects ----> 内存使用情况
...
"main" prio=5 tid=1 Sleeping ----> 原因为Sleeping
| group="main" sCount=1 dsCount=0 flags=1 obj=0x733d0670 self=0x74a4abea00
| sysTid=23346 nice=-10 cgrp=default sched=0/0 handle=0x74a91ab9b0
| state=S schedstat=( 391462128 82838177 354 ) utm=33 stm=4 core=3 HZ=100
| stack=0x7fe6fac000-0x7fe6fae000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep(Native method)
- sleeping on <0x053fd2c2> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:373)
- locked <0x053fd2c2> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:314)
at android.os.SystemClock.sleep(SystemClock.java:122)
at com.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20) ----> 产生ANR的包名以及具体行数
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(ActivityThread.java:-1)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
定位进程ID和包名:pid 23346com.sky.myjavatest
造成ANR原因:Sleeping
具体行数:at com.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20)
如果产生新的ANR,原来的 traces.txt 文件会被覆盖。
Java线程调用分析
通过JDK提供的命令可以帮助分析和调试Java应用,命令为:jstack {pid}
其中pid可以通过jps命令获得,jps命令会列出当前系统中运行的所有Java虚拟机进程
103440 Jps
51400
DDMS分析ANR问题
造成ANR的原因
- 主线程阻塞或主线程数据读取
解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS
- CPU满负荷,I/O阻塞
解决办法:文件读写或数据库操作放在子线程异步操作。
- 内存不足
解决办法:
AndroidManifest.xml
文件中可以设置android:largeHeap="true"
,以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。
- 各大组件ANR
各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。
ANR源码分析
Service造成的Service Timeout
Service Timeout是位于"ActivityManager"线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。
- 发送延时消息
Service进程attach到system_server进程的过程中会调用realStartServiceLocked
,紧接着mAm.mHandler.sendMessageAtTime()
来发送一个延时消息,延时的时常是定义好的,如前台Service的20秒。ActivityManager线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG
消息时会触发。
AS.realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
...
//发送delay消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
try {
...
//最终执行服务的onCreate()方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
mAm.appDiedLocked(app);
throw e;
} finally {
...
}
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
...
scheduleServiceTimeoutLocked(r.app);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
//当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
- 进入目标进程的主线程创建Service
经过Binder等层层调用进入目标进程的主线程 *handleCreateService(CreateServiceData data)*。
private void handleCreateService(CreateServiceData data) {
...
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = (Service) cl.loadClass(data.info.name).newInstance();
...
try {
//创建ContextImpl对象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//创建Application对象
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//调用服务onCreate()方法
service.onCreate();
//取消AMS.MainHandler的延时消息
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
...
}
}
这个方法中会创建目标服务对象,以及回调常用的Service的onCreate()
方法,紧接着通过serviceDoneExecuting()
回到system_server执行取消AMS.MainHandler的延时消息。
- 回到system_server执行取消AMS.MainHandler的延时消息
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
...
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//当前服务所在进程中没有正在执行的service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
...
}
...
}
此方法中Service逻辑处理完成则移除之前延时的消息SERVICE_TIMEOUT_MSG
。如果没有执行完毕不调用这个方法,则超时后会发出SERVICE_TIMEOUT_MSG
来告知ANR发生。
BroadcastReceiver造成的BroadcastQueue Timeout
BroadcastReceiver Timeout是位于**“ActivityManager”**线程中的BroadcastQueue.BroadcastHandler收到
BROADCAST_TIMEOUT_MSG
消息时触发。
- 处理广播函数 processNextBroadcast() 中 broadcastTimeoutLocked(false) 发送延时消息
//广播处理顺序为先处理并行广播,再处理当前有序广播。
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
...
// 处理当前有序广播
do {
r = mOrderedBroadcasts.get(0);
//获取所有该广播所有的接收者
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
//step 1\. 发送延时消息,这个函数处理了很多事情,比如广播处理超时结束广播
broadcastTimeoutLocked(false);
...
}
}
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
if (r.resultTo != null) {
//2\. 处理广播消息消息
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
}
//3\. 取消广播超时ANR消息
cancelBroadcastTimeoutLocked();
}
} while (r == null);
...
// 获取下条有序广播
r.receiverTime = SystemClock.uptimeMillis();
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
//设置广播超时
setBroadcastTimeoutLocked(timeoutTime);
}
...
}
}
上文的step 1. broadcastTimeoutLocked(false)函数:记录时间信息并调用函数设置发送延时消息
final void broadcastTimeoutLocked(boolean fromMsg) {
...
long now = SystemClock.uptimeMillis();
if (fromMsg) {
if (mService.mDidDexOpt) {
// Delay timeouts until dexopt finishes.
mService.mDidDexOpt = false;
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
if (!mService.mProcessesReady) {
return;
}
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
// step 2
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
上文的step 2.setBroadcastTimeoutLocked函数: 设置广播超时具体操作,同样是发送延时消息
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
ContentProvider的ContentProvider Timeout
ContentProvider Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。
mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
#### ContentProvider的ContentProvider Timeout
> ContentProvider Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。