关于APP应用无响应的处理

什么是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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值