ANR全称是application not responsing,也就是程序无响应。在android系统中,和程序崩溃一样,都是程序设计编码的问题。 android应用中,当你的touch、key等事件得不到及时响应时,在一定的延时之后,系统就会弹出ANR的警告。此时,用户可以选择结束程序或者继续等待。本文将主要从分析ANR原理,以及如何避免ANR。
一、ANR产生的原因
在android程序中,所有的输入(key和touch等)事件是由底层的InputDispatcher分发到上层的InputManagerService的,再通过InputManagerService内部的InputMonitor送入WindowManagerService的Policy(PhoneWindowManager)中。整个流程可以参考事件在native和jni中的流程和事件在java framework中的流程。而在事件分发的过程中,如果应用程序不能及时响应,就会产生ANR。
以下是一些主要的代码片段
/frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp(java和native的对接桥梁)
// java层的InputManagerService初始化的时候,会调用jni方法nativeInit(),而该方法会初始化
// NativeInputManager,并最终创建native层的InputManager对象。创建InputManager对象
// 时,传递了3个参数,分别是event hub、read policy和dispatch policy。而这个dispatch
// policy最终会传递到InputDispatcher中,并作为其mPolicy变量保存下来。所以,InputDispatcher
// 里的事件,最终通过这个NativeInputManager对象送入了java层的InputManagerService。
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj); // java层的Context
mServiceObj = env->NewGlobalRef(serviceObj); // java层的InputManagerService
{
AutoMutex _l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
sp<EventHub> eventHub = new EventHub();
// 三个参数分别是event hub、read policy、dispatch policy
mInputManager = new InputManager(eventHub, this, this);
}
/frameworks/base/services/input/InputDispatcher.cpp(底层事件分发到java层)
void InputDispatcher::onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
float dispatchLatency = (currentTime - eventTime) * 0.000001f;
float waitDuration = (currentTime - waitStartTime) * 0.000001f;
ALOGI("Application is not responding: %s. "
"It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
dispatchLatency, waitDuration, reason);
// Capture a record of the InputDispatcher state at the time of the ANR.
time_t t = time(NULL);
struct tm tm;
localtime_r(&t, &tm);
char timestr[64];
strftime(timestr, sizeof(timestr), "%F %T", &tm);
mLastANRState.clear();
mLastANRState.append(INDENT "ANR:\n");
mLastANRState.appendFormat(INDENT2 "Time: %s\n", timestr);
mLastANRState.appendFormat(INDENT2 "Window: %s\n",
getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());
mLastANRState.appendFormat(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
mLastANRState.appendFormat(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
mLastANRState.appendFormat(INDENT2 "Reason: %s\n", reason);
dumpDispatchStateLocked(mLastANRState);
// 将一个Command存入一个队列中,而该命令队列会在未来某个时间点执行,也就是执行
// doNotifyANRLockedInterruptible()方法了
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
commandEntry->inputApplicationHandle = applicationHandle;
commandEntry->inputWindowHandle = windowHandle;
}
void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
// 这个mPolicy就是NativeInputManager对象了,最终再由NativeInputManager调用到
// InputManagerService.java的notifyANR()
nsecs_t newTimeout = mPolicy->notifyANR(
commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle);
mLock.lock();
resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
commandEntry->inputWindowHandle != NULL
? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
// 一般是系统error时出现,比如无法找到焦点window等
if (applicationHandle == NULL && windowHandle == NULL) {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
// 记录事件超时的相关属性
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
}
} else { // 应用程序ANR时,一般情况下window和application都是存在的
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
nsecs_t timeout;
if (windowHandle != NULL) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); // 默认是5s
} else if (applicationHandle != NULL) {
timeout = applicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT); // 默认是5s
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
// 记录事件超时的相关属性
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
if (windowHandle != NULL) {
mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
}
if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
mInputTargetWaitApplicationHandle = applicationHandle;
}
}
}
if (mInputTargetWaitTimeoutExpired) {
return INPUT_EVENT_INJECTION_TIMED_OUT;
}
// 事件超时
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
*nextWakeupTime = LONG_LONG_MIN;
return INPUT_EVENT_INJECTION_PENDING;
} else {
// Force poll loop to wake up when timeout is due.
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime;
}
return INPUT_EVENT_INJECTION_PENDING;
}
}
/frameworks/base/services/java/com/android/server/input/InputManagerService.java
/frameworks/base/services/java/com/android/server/wm/InputMonitor.java
// =============== InputManager.java ===============
// Native callback.
private long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle) {
return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
}
// =============== InputMonitor.java ===============
/* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
*/
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle) {
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
synchronized (mService.mWindowMap) {
// 。。。。省略
// 存储ANR的相关信息
mService.saveANRStateLocked(appWindowToken, windowState);
}
if (appWindowToken != null && appWindowToken.appToken != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
if (! abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return appWindowToken.inputDispatchingTimeoutNanos;
}
} catch (RemoteException ex) {
}
} else if (windowState != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
windowState.mSession.mPid, aboveSystem);
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return timeout;
}
} catch (RemoteException ex) {
}
}
return 0; // abort dispatching
}
// =============== ActivityManagerService.java ===============
public long inputDispatchingTimedOut(int pid, boolean aboveSystem) {
if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.FILTER_EVENTS);
}
ProcessRecord proc;
// 。。。。省略
if (proc != null) {
appNotResponding(proc, null, null, aboveSystem, "keyDispatchingTimedOut");
if (proc.instrumentationClass != null || proc.usingWrapper) {
return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
}
}
return KEY_DISPATCHING_TIMEOUT;
}
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
// 。。。。省略
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap map = new HashMap();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
// 该消息最终会在mHandler中处理,发布一个android.intent.action.ANR的广播,
// 并且显示一个AppNotRespondingDialog
mHandler.sendMessage(msg);
}
二、如何避免ANR
在主线程里的尽可能少做事情。特别是在Acitivty的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作;或者使用AsyncTask、ThreadPool等方法。如果有必要,可以开启多进程的service,将一些耗时操作放到service里处理。另外,需要注意的,不管网络请求还是本地的IO,都是不允许在主线程里做的。