什么是ANR
ANR,是“Application Not Responding”的缩写,即“应用程序无响应”。在Android中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会监测应用程序的响应时间,如果应用程序主线程(即UI线程)在超时时间内对输入事件没有处理完毕,或者对特定操作没有执行完毕,就会出现ANR。对于输入事件没有处理完毕产生的ANR,Android会显示一个对话框,提示用户当前应用程序没有响应,
用户可以选择继续等待或者关闭这个应用程序(也就是杀掉这个应用程序的进程)。
产生ANR的原因
ANR的产生需要同时满足三个条件:
主线程:只有应用程序进程的主线程响应超时才会产生ANR;
超时时间:产生ANR的上下文不同,超时时间也会不同,但只要在这个时间上限内没有响应就会ANR;
输入事件/特定操作:输入事件是指按键、触屏等设备输入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各个函数,产生ANR的上下文不同,导致ANR的原因也会不同;
其实很多情况下产生ANR,是系统卡顿导致的手机超时,app程序等待超时。如果需要找到根本原因,一般非常困难,但是我们可以考虑一些规避的方法。
手机一般发生anr都会跑到frameworks/base / services/core/java/com/android/server/am/AppErrors.java函数中,一般anr有几种表现形式,一个是静默ANR,现象是手机应用一直停在
一个界面,类似假死状态,另外一种情况是有弹出对话框,显示应用程序无反应,但是有些程序发现的anr一个是后台进程,对用户体验不太好,可以做一些优化。
//sw james.fan 20180305 add background and blacklist process anr killed
import java.util.List;
//#ifdef 1
//james.fan,20180523,third party app directly kill app
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
//#endif
//#ifdef 1
//james,20180523,third party app directly kill app
boolean isThirdPartyProcess = isThirdPartyProcess(app);
if ((crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) || isThirdPartyProcess) {
//#endif
// The process crashed again very quickly. If it was a bound foreground service, let's
// try to restart again in a while, otherwise the process loses!
Slog.w(TAG, "Process " + app.info.processName
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
mService.mStackSupervisor.handleAppCrashLocked(app);
//#ifdef 1
//james,20180523,third party app directly kill app
if (!app.persistent || isThirdPartyProcess) {
//#endif
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
// need to keep it running. If a persistent process is actually
// repeatedly crashing, then badness for everyone.
EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
app.info.processName);
if (!app.isolated) {
// XXX We don't have a way to mark isolated processes
// as bad, since they don't have a peristent identity.
mBadProcesses.put(app.info.processName, app.uid,
new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
mProcessCrashTimes.remove(app.info.processName, app.uid);
}
app.bad = true;
app.removed = true;
// Don't let services in this process be restarted and potentially
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
mService.removeProcessLocked(app, false, tryAgain, "crash");
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
//#ifdef 1
//james,20180523,third party app directly kill app
if (!showBackground || isThirdPartyProcess) {
//#endif
return false;
}
//sw james.fan 20180305 add background and blacklist process anr killed
public void forceStopPackageAndDisableComponent(final ArrayList<String> pkgList) {
final String PACKAGE_NAME = "com.android.lava.powersave";
final String CLASS_NAME = "com.android.lava.powersave.service.LavaPowerSaveService";
final String ACTION_KILL_DIABLE_APP = "action_kill_diable_app";
final String KEY_KILL_DIABLE_APP = "key_kill_diable_app";
Intent intent = new Intent();
intent.setAction(ACTION_KILL_DIABLE_APP);
intent.setClassName(PACKAGE_NAME, CLASS_NAME);
intent.putStringArrayListExtra(KEY_KILL_DIABLE_APP, pkgList);
mContext.startService(intent);
}
public final static List<String> PROCEE_WHITE_LIST = new ArrayList<String>() {
{
add("com.baidu.android.pushservice.PushInfoProvider");
}
};
final boolean isWhiteListprocess(ProcessRecord app) {
String packageName = app.info.packageName;
Slog.i(TAG, "james--packageName="+packageName);
for (int i = 0; i < PROCEE_WHITE_LIST.size(); i++) {
String whiteListpacakgeName = PROCEE_WHITE_LIST.get(i);
Slog.i(TAG, "james--whiteListpacakgeName="+whiteListpacakgeName);
if (whiteListpacakgeName.equals(packageName)) {
return true;
}
}
return false;
}
final boolean isNeedKillBackgrondprocess(ProcessRecord app) {
boolean showforeground = app.foregroundActivities;
String packageName = app.info.packageName;
Slog.i(TAG, "james--packageName="+packageName);
Slog.i(TAG, "james--foregroundActivities="+app.foregroundActivities);
if (app != null && app.pid > 0 && app.pid != MY_PID && !showforeground ) {
if (isWhiteListprocess(app)) {
return false;
} else {
app.kill("james--kill background and balcklist anr process", true);
ArrayList<String> pkgList = new ArrayList<String>();
pkgList.add(app.info.packageName);
forceStopPackageAndDisableComponent(pkgList);
Slog.i(TAG, "james--app is killed="+app.info.packageName);
return true;
}
}
return false;
}
boolean isThirdPartyProcess(ProcessRecord app)
{
String packageName = app.info.packageName;
try {
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(packageName, 0);
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {
return true;
}
} catch (NameNotFoundException e) {
}
return false;
}
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
boolean isSetupWardComplete = Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
//isSetupWardComplete=true;
Slog.i("james", "isSetupWardComplete="+isSetupWardComplete);
if (!isSetupWardComplete) {
// #ifdef LAVA_EDIT
// james.2017-3-14,directly kill app(exception system_server).
if (app != null && app.pid > 0 && app.pid != MY_PID) {
app.kill("kill before setupward", true);
}
// #endif
Slog.e(TAG, "can't report anr info before setupward complete");
return;
} else {
//sw james.fan 20180305 add background and blacklist process anr killed
/*
if (isNeedKillBackgrondprocess(app)) {
return;
}*/
}
if (mService.mController != null) {
try {
// 0 == continue, -1 = kill process immediately
int res = mService.mController.appEarlyNotResponding(
app.processName, app.pid, annotation);
if (res < 0 && app.pid != MY_PID) {
app.kill("anr", true);
}
} catch (RemoteException e) {
mService.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
long anrTime = SystemClock.uptimeMillis();
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
}
// Unless configured otherwise, swallow ANRs in background processes & kill the process.
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
/// M: ANR Debug Mechanism
if (mService.mAnrManager.startAnrDump(mService, app, activity, parent, aboveSystem,
annotation, showBackground))
return;
boolean isSilentANR;
synchronized (mService) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mService.mShuttingDown) {
Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
return;
} else if (app.notResponding) {
Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
return;
} else if (app.crashing) {
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
} else if (app.killedByAm) {
Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);
return;
} else if (app.killed) {
Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation);
return;
}
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
app.notResponding = true;
// Log the ANR to the event log.
EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
app.processName, app.info.flags, annotation);
// Dump thread traces as quickly as we can, starting with "interesting" processes.
firstPids.add(app.pid);
// Don't dump other PIDs if it's a background ANR
isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
if (!isSilentANR) {
int parentPid = app.pid;
if (parent != null && parent.app != null && parent.app.pid > 0) {
parentPid = parent.app.pid;
}
if (parentPid != app.pid) firstPids.add(parentPid);
if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mService.mLruProcesses.get(i);
if (r != null && r.thread != null) {
int pid = r.pid;
if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
if (r.persistent) {
firstPids.add(pid);
if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
} else if (r.treatLikeActivity) {
firstPids.add(pid);
if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
} else {
lastPids.put(pid, Boolean.TRUE);
if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
}
}
}
}
}
}
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(app.processName);
if (activity != null && activity.shortComponentName != null) {
info.append(" (").append(activity.shortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(app.pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parent != null && parent != activity) {
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
// don't dump native PIDs for background ANRs unless it is the process of interest
String[] nativeProcs = null;
if (isSilentANR) {
for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {
nativeProcs = new String[] { app.processName };
break;
}
}
} else {
nativeProcs = NATIVE_STACKS_OF_INTEREST;
}
int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
ArrayList<Integer> nativePids = null;
if (pids != null) {
nativePids = new ArrayList<Integer>(pids.length);
for (int i : pids) {
nativePids.add(i);
}
}
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
File tracesFile = ActivityManagerService.dumpStackTraces(
true, firstPids,
(isSilentANR) ? null : processCpuTracker,
(isSilentANR) ? null : lastPids,
nativePids);
String cpuInfo = null;
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
synchronized (mService.mProcessCpuTracker) {
cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
}
info.append(processCpuTracker.printCurrentLoad());
info.append(cpuInfo);
}
info.append(processCpuTracker.printCurrentState(anrTime));
Slog.e(TAG, info.toString());
if (tracesFile == null) {
// There is no trace file, so dump (only) the alleged culprit's threads to the log
Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
}
mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
cpuInfo, tracesFile, null);
if (mService.mController != null) {
try {
// 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
int res = mService.mController.appNotResponding(
app.processName, app.pid, info.toString());
if (res != 0) {
if (res < 0 && app.pid != MY_PID) {
app.kill("anr", true);
} else {
synchronized (mService) {
mService.mServices.scheduleServiceTimeoutLocked(app);
}
}
return;
}
} catch (RemoteException e) {
mService.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
synchronized (mService) {
mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
if (isSilentANR) {
app.kill("bg anr", true);
return;
}
// Set the app's notResponding state, and look up the errorReportReceiver
makeAppNotRespondingLocked(app,
activity != null ? activity.shortComponentName : null,
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
mService.mUiHandler.sendMessage(msg);
}
}
private void makeAppNotRespondingLocked(ProcessRecord app,
String activity, String shortMsg, String longMsg) {
app.notResponding = true;
app.notRespondingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
activity, shortMsg, longMsg, null);
startAppProblemLocked(app);
app.stopFreezingAllLocked();
}
void handleShowAnrUi(Message msg) {
Dialog d = null;
synchronized (mService) {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
if (proc != null && proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.ALREADY_SHOWING);
return;
}
//,20180523,third party app directly kill app
boolean isThirdPartyProcess = isThirdPartyProcess(proc);
//#endif
Intent intent = new Intent("android.intent.action.ANR");
if (!mService.mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
}
mService.broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
//#ifdef 1
//,20180523,third party app directly kill app
if (!isThirdPartyProcess && (mService.canShowErrorDialogs() || showBackground)) {
//#endif
2:vendor/mediatek/proprietary/frameworks/base / services/core/java/com/mediatek/server/anr/AnrManagerService.java
synchronized (mService) {
Slog.i(TAG, "appNotResponding-got this lock: " + app + " "
+ annotation + ",shuttingDown: " + shuttingDown + ",notResponding: "
+ notResponding + ",crashing: " + crashing);
// PowerManager.reboot() can block for a long time,
// so ignore ANRs while shutting down.
if(shuttingDown || notResponding || crashing){
//#ifdef 1
//james,20180712,if notResponding = ture,force kill app process
if(notResponding)
{
isSilentANR = !showBackground && !app.isInterestingToUserLocked()
&& pid != mAmsPid;
if (isSilentANR) {
mKill.invoke(app, new Object[] { "bg anr", true });
return true;
}
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = (int) mShowNotRespondingUiMsgField.get(mAMS);
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
Handler mUiHandler = (Handler) mUiHandlerField.get(mService);
mUiHandler.sendMessage(msg);
}
//#endif
这个其实只是规避,改善用户体验,其实没有从根本上解决ANR。