美文网首页Android DebugAndroid优化
给解决问题ANR一个印象

给解决问题ANR一个印象

作者: 我叫王菜鸟 | 来源:发表于2017-11-01 22:01 被阅读1516次

    简介

    现在感觉自己做的工作,基本上脱离Android了就是用java写代码,而且可能试用期完了就会被刷,很多东西是公司自己的,完全不知道怎么下手研究,导师指导也就是几句话的说你看下哪里哪里的代码。要不是旁边的一个大牛,我是真的走不下去了。大佬们,要是被刷求介绍工作,不怕加班,只怕没有讨论没有指导。

    写这篇博客另外感谢之前小米的同事,她在北邮念完研究生,但是却很低调,她是很好的学姐,她当时工作就是处理ANR问题,所以一些资料是引用学姐的,当然也有没阐述清楚的问题,比如广播ANR和输入ANR这个我们以后专门在写。

    题外话说完了,该进入正题了,ANR:Application Not Responding 即应用无响应

    其实应用没响应,弹出那个框框,就是Android系统要让我们知道,可能某些错误的操作或者代码使得没有执行应用,提升用户体验,但是我们开发者应该避免ANR,如果你还想和IOS系统竞争的话。要不然就真输了。

    理解核心

    首先ANR分成四个类型:

    • ServiceTimeout-Service,(bind,create,start,unbind等等),超过前台20s,后台200s没有处理
      完成发生ANR
    • BroadcastTimeout- BroadcastReceiver,超过前台10S,后台60s没有处理完成发生ANR
    • KeyDispatchTimeout-主要类型按键或触摸事件,触摸开始计算 超过5S没有处理完成发生ANR
    • ProcessContentProviderPublishTimedOutLocked-ContentProvider publish在20s没有处理完成发生
      ANR

    Service-ANR

    我们如果了解service的声明周期我们可能知道生命周期中的有些方法在主线程中执行。所以我们就要从那些在主线程中执行的方法开始排查

    • onCreate(),
    • onStartCommand()等)有没有做耗时的操作

    如果看样子代码逻辑上没有什么问题,那就要从系统状态开始查:

    CPU的使用情况、系统服务的状态等,判断当时发生ANR进程是否
    受到系统运行异常的影响。
    

    错误打印的log举例:

    Reason: Executing service com.android.bluetooth/.btservice.AdapterService(包名)
    

    那么Service发生ANR的机制是什么,我们肯定能猜出来,在主线程中运行超过时间了就发生ANR了,那么如果让你设计会怎么设计,肯定是先埋下一个炸弹,然后到了时间如果没有人拆除那就爆炸。一个道理,Service-ANR就是这样做的。

    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_TIMEOUT_MSG,延时时长:

    • 对于前台服务,则超时为SERVICE_TIMEOUT,即timeout=20s;
    • 对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT,即timeout=200s;
    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
        r.executeNesting--;
        if (r.executeNesting <= 0) {
            if (r.app != null) {
                r.app.execServicesFg = false;
                r.app.executingServices.remove(r);
                if (r.app.executingServices.size() == 0) {
                    //移除服务启动超时的消息
                    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
                } else if (r.executeFg) {
                    ...
                }
                if (inDestroying) {
                    mDestroyingServices.remove(r);
                    r.bindings.clear();
                }
                mAm.updateOomAdjLocked(r.app);
            }
            r.executeFg = false;
            ...
            if (finishing) {
                if (r.app != null && !r.app.persistent) {
                    r.app.services.remove(r);
                }
                r.app = null;
            }
        }
    }
    

    handleCreateService()执行后便会移除服务启动超时的消息SERVICE_TIMEOUT_MSG。 Service启动过程出现ANR,”executing service [发送超时serviceRecord信息]”, 这往往是service的onCreate()回调方法执行时间过长。

    在哪里调用的呢?

    private void handleCreateService(CreateServiceData data) {
        //当应用处于后台即将进行GC,而此时被调回到活动状态,则跳过本次gc。
        unscheduleGcIdler();
        LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
    
        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();
            mServices.put(data.token, service);
            //调用服务创建完成
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (Exception e) {
            ...
        }
    }
    

    onCreate()方法调用完成之后调用的。

    Broadcast处理超时

    主线程在执行 BroadcastReceiver 的 onReceive 函数时10/60 秒内没有执行完毕

    错误打印log举例:

    Reason: Broadcast of Intent { act=android.net.wifi.WIFI_STATE_CHANGED flg=0x4000010
    cmp=com.android.settings/.widget.SettingsAppWidgetProvider (has extras) }
    

    和Service一样,广播的一些回调也在主线程中,当然onReceive()也可以调度在其他线程执行,通过Context.registerReceiver(BroadcastReceiver, IntentFilter,
    String, Handler)这个方法注册广播接收器, 可以指定一个处理的Handler,将onReceive()调度在非主线程执行

    final void processNextBroadcast(boolean fromMsg) {
        ...
            r = mOrderedBroadcasts.get(0); //获取串行广播的第一个广播
            boolean forceReceive = false;
            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))) {
                    broadcastTimeoutLocked(false); //当广播处理时间超时,则强制结束这条广播
                }
            }
            ...
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //处理广播消息消息,调用到onReceive()
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                }
        ...
        //part3: 获取下一个receiver
        r.receiverTime = SystemClock.uptimeMillis();
        if (recIdx == 0) {
            r.dispatchTime = r.receiverTime;
            r.dispatchClockTime = System.currentTimeMillis();
        }
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            setBroadcastTimeoutLocked(timeoutTime); //设置广播超时延时消息
        }
        ...
    }
    

    判定当前时间是否已经超过了r.dispatchTime + 2×mTimeoutPeriod×numReceivers:

    就是说,每次派发一个广播就更新一次时间,完了之后就会发出一个超时消。

    Input-ANR

    主线程对输入事件在 5 秒内没有处理完毕

    log打印举例:

    Reason: Input dispatching timed out (Waiting because the focused window has not finished
    processing the input events that were previously delivered to it.)
    
    • 1:UI线程尽量只做跟UI相关的工作
    • 2:耗时的工作(比如数据库操作, I/O,连接网络或 者别的有可能阻碍UI线程的操作)
      把它放入单独的线程处理
    • 3:尽量用Handler来处理UIthread和别的thread之间的交互

    InputDispatcherThread是一个线程,它处理一次消息的派发输入事件作为一个消息,需要排队等待派发,每
    一个Connection都维护两个队列: outboundQueue和waitQueue

    waitQueue: 已经发送给窗口的事件
    publishKeyEvent完成后,表示事件已经派发了,就将事件从outboundQueue挪到了waitQueue
    事件经过这么一轮处理,就算是从InputDispatcher派发出去了,但事件是不是被窗口收到了,还需要等待接
    收方的“finished”通知

    这部分目前我也不熟悉,以后专门会有个专题研究输入事件

    ContentProvider处理超时

    主线程在执行 ContentProvider 相关操作时没有在规定的时间内执行完毕

    log举例:

    Reason: timeout publishing content providers,不会报告 ANR弹框。
    

    产生这类ANR是应用启动,调用AMS.attachApplicationLocked()方法,发布启动进程的所有
    ContentProvider时发生

    
    private final void processContentProviderPublishTimedOutLocked(ProcessRecord app){
        cleanupAppInLaunchingProvidersLocked(app, true);
        removeProcessLocked(app, false, true, "timeout publishing content providers");
    }
    
    AMS.attachApplicationLocked()
        |-->mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIME_OUT_MSG)
        
    
    
    AMS.publishContentProviders()
        |-->mHandler.removeMessage(CONTENT_PROVIDER_PUBLISH_TIME_OUT)
    
    

    触发ANR的常见原因

    1. 在主线程执行耗时操作
      • layout层级太深
      • for循环内部代码太多
      • 主线程执行IO操作
      • 主线程执行文件操作等等
    2. 被Binder对端block
    3. 被子线程同步锁block
    4. Binder被沾满导致主线程无法和SystemServer通信
    5. 得不到系统资源(CPU,RAM,IO等)
    6. 等等

    log记录

    针对ANR我们最常规的就是看Log,然后针对log排查问题

    可以将anr目录下的文件放到电脑上进行查看
    adb pull data/anr .

    属性系统可以通过adb shell getprop dalvik.vm.stack-trace-file这种方式查找对应的属性值

    当触发ANR之后会调用AppErrors.appNotResponding()方法

    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);
            ...
            //记录ANR时间
            long anrTime = SystemClock.uptimeMillis();
            //更新CPU状态
            if (ActivityManagerService.MONITOR_CPU_USAGE) {
                mService.updateCpuStatsNow();
            }
            //特定场景下忽略ANR
            synchronized (mService) {
                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;
                }        
            }
            //为了防止多次对相同app的anr执行重复代码,在此处标注记录,属于上面的特定情况种的一种
            app.notResponding = true;
            //记录ANR信息到Event Log中
            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                    app.processName, app.info.flags, annotation);        
            //添加当前app到firstpids列表中
            firstPids.add(app.pid);
            //如果可能添加父进程到firstpids列表种
            int parentPid = app.pid;
            ...
            // 将ANR信息存在info变量中,后续打印到LOGCAT,这部分的信息会以ActivityManager为Tag打印出来,包含了ANR的进程,出现原因以及当时的CPU状态,这些对分析ANR是非常重要的信息
            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");
            }
            //将ANR信息输出到traces文件,分为两种,一种带native层信息,一种不带
            ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true;
            String[] nativeProcs = NATIVE_STACKS_OF_INTEREST;
            // don't dump native PIDs for background ANRs
            File tracesFile = null;
            if (isSilentANR) {
                //这里返回了一个文件,这里的文件路径是:`/data/anr/traces.txt`
                //查找方法:adb shell getprop dalvik.vm.stack-trace-file
                tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids,
                    null);
            } else {
                tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
                    nativeProcs);
            }
            
            //再次更新CPU信息,并且输出到SystemLog中
            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());
            //上面的信息已经对应的ANR信息写入/data/anr/traces.txt中
            
            //给底层发送信号Process.SIGNAL_QUIT=3
            if (tracesFile == null) {
                Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
            }
            //将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录
            //命名:system_server/system_app/data_app + type+...比如下面
            //data_app_anr@1501989621992.txt.gz 
            //data_app_crash@1501989671926.txt
            mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                    cpuInfo, tracesFile, null);        
            
            synchronized (mService) {
                mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
                //如果是后台ANR则直接杀掉结束
                if (isSilentANR) {
                    app.kill("bg anr", true);
                    return;
                }
                //设置app的not响应状态,并查找errorReportReceiver
                makeAppNotRespondingLocked(app,
                        activity != null ? activity.shortComponentName : null,
                        annotation != null ? "ANR " + annotation : "ANR",
                        info.toString());
    
                //弹出ANR对话框
                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);
                }
                //向ui线程发送,内容为SHOW_NOT_RESPONDING_MSG的消息
                mService.mUiHandler.sendMessage(msg);
            }
    }
    

    我们来小节一下上面发生了什么:

    • 立刻更新了CPU的信息
      /** 2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6) */
      public static final int CPU = 2721;
      给event_log中写入值
      
    • 忽略一些anr
    • 在event_log中打印am_anr的信息,这个是anr立刻发生的记录
    • 将ANR信息存在info变量中,后续打印到LOGCAT,这部分的信息会以ActivityManager为Tag打印出来,包含了ANR的进程,出现原因以及当时的CPU状态,这些对分析ANR是非常重要的信息
    • 将ANR信息输出到data/anr/traces文件
    • 没有输出到traces文件的时候,给底层发送一个rocess.SIGNAL_QUIT=3信号
    • 将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录
    • 如果是后台ANR则直接杀掉结束
    • 弹出ANR对话框

    细节

    怎么样就将信息保存到了/data/anr/traces.txt了

    1.AMS.dumpStackTraces

    public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids,
            ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) {
        //tracesPath = "data/anr/traces.txt"
        String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
        if (tracesPath == null || tracesPath.length() == 0) {
            return null;
        }
    
        File tracesFile = new File(tracesPath);
        try {
            if (clearTraces && tracesFile.exists()) tracesFile.delete();
            tracesFile.createNewFile();
            FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
        } catch (IOException e) {
            Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesPath, e);
            return null;
        }
        //[2]
        dumpStackTraces(tracesPath, firstPids, processCpuTracker, lastPids, nativeProcs);
        return tracesFile;
    }
    

    2.dumpStackTraces()

    private static void dumpStackTraces(String tracesPath, ArrayList<Integer> firstPids,
            ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs){
        FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
            @Override
            public synchronized void onEvent(int event, String path) { notify(); }
        };
    
        try {
            observer.startWatching();
    
            // 获取发生ANR进程的pid,然后遍历这些进程给进程发送Process.SIGNAL_QUIT=3的信号
            if (firstPids != null) {
                try {
                    int num = firstPids.size();
                    for (int i = 0; i < num; i++) {
                        synchronized (observer) {
                            final long sime = SystemClock.elapsedRealtime();
                            Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT);
                            observer.wait(1000);  // Wait for write-close, give up after 1 sec
                        }
                    }
                } catch (InterruptedException e) {
                    Slog.wtf(TAG, e);
                }
            }
    
            // 接下来收集本地pids的堆栈
            if (nativeProcs != null) {
                int[] pids = Process.getPidsForCommands(nativeProcs);
                if (pids != null) {
                    for (int pid : pids) {
                        final long sime = SystemClock.elapsedRealtime();
                        Debug.dumpNativeBacktraceToFileTimeout(pid, tracesPath, 10);//[3]输出native进程的trace并且限制超时时间
                    }
                }
            }
            if (processCpuTracker != null) {
                processCpuTracker.init();
                System.gc();
                processCpuTracker.update();
                try {
                    synchronized (processCpuTracker) {
                        processCpuTracker.wait(500); // measure over 1/2 second.
                    }
                } catch (InterruptedException e) {
                }
                processCpuTracker.update();
    
                //从lastPids中选取CPU使用率 top 5的进程,输出这些进程的stacks
                final int N = processCpuTracker.countWorkingStats();
                int numProcs = 0;
                for (int i=0; i<N && numProcs<5; i++) {
                    ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
                    if (lastPids.indexOfKey(stats.pid) >= 0) {
                        numProcs++;
                        try {
                            synchronized (observer) {
                                final long stime = SystemClock.elapsedRealtime();
                                Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);
                                observer.wait(1000);  // Wait for write-close, give up after 1 sec
                               
                            }
                        } catch (InterruptedException e) {
                            Slog.wtf(TAG, e);
                        }
                    } else if (DEBUG_ANR) {
                        Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
                                + stats.pid);
                    }
                }
            }
        } finally {
            observer.stopWatching();
        }
    }
    

    小结:

    • 收集发生anr进程的调用栈

      • 发生anr的进程
      • anr进程的父进程(anr进程是由于AMS生成,AMS在system_server进程中,system_server进程是anr的父进程)
      • mLruProcesses中所有的persistent进程
    • 收集Native进程的调用栈

      • "/system/bin/audioserver"
      • "/system/bin/cameraserver"
      • "/system/bin/drmserver"
      • "/system/bin/mediadrmserver"
      • "/system/bin/mediaserver"
      • "/system/bin/sdcard"
      • "/system/bin/surfaceflinger"
      • "media.codec" // system/bin/mediacodec
      • "media.extractor" // system/bin/mediaextractor
      • "com.android.bluetooth" // Bluetooth service
    • 收集lastPids进程的stacks

      • 收集前五名

    注意收集信息等待的时间

    3.Debug.dumpNativeBacktraceToFileTimeout()

    static void android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
        jint pid, jstring fileName, jint timeoutSecs)
    {
        if (fileName == NULL) {
            jniThrowNullPointerException(env, "file == null");
            return;
        }
        const jchar* str = env->GetStringCritical(fileName, 0);
        String8 fileName8;
        if (str) {
            fileName8 = String8(reinterpret_cast<const char16_t*>(str),
                                env->GetStringLength(fileName));
            env->ReleaseStringCritical(fileName, str);
        }
        //打开文件(data/anr/traces.txt)
        int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND, 0666);
        if (fd < 0) {
            fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
            return;
        }
    
        dump_backtrace_to_file_timeout(pid, fd, timeoutSecs);//[4]
    
        close(fd);
    }
    
    

    4.dump_backtrace_to_file_timeout()

    int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
      //发送dump请求得到sock_fd
      int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
      if (sock_fd < 0) {
        return -1;
      }
      int result = 0;
      char buffer[1024];
      ssize_t n;
      int flag = 0;
      //从sock_fd中读取信息写入data/anr/traces.txt中
      while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
        flag = 1;
        if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
          result = -1;
          break;
        }
      }
      close(sock_fd);
      ...
      return result;
    }
    
    

    主要是通过给底层发送DEBUGGER_ACTION_DUMP_BACKTRACE来请求dump的sock_fd句柄,底层调用dump_backtraces()来获取信息,从而写入data/anr/traces.txt文件中

    当发生anr的时候,距离ANR最近的时间是am_anr这个日志的时间,然后会打印各种信息有底层dump的,有进程的调用栈信息等等。最后将trances.txt写入data/system/dropbox目录下,并且重命名,规则见上文。

    补充

    其中Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);发出退出进程信号

    ANR的相关log小结

    • system.log 包含ANR发生时间点信息、 ANR发生前的CPU信息,还包含大量系统服务输出的信息。
    • main.log 包含ANR发生前应用自身输出的信息,可供分析应用是否有异常;此外还包含输出的GC信息,
      可供分析内存回收的速度,判断系统是否处于低内存或内存碎片化状态。
    • event.log 包含AMS与WMS输出的应用程序声明周期信息,可供分析窗口创建速度以及焦点转换情况。
    • kernel.log 包含kernel打出的信息, LowMemoryKiller杀进程、内存碎片化或内存不足, mmc驱动异常都
      可以在这里找到。

    比如:

    system log检索 ANR in 关键字
    09-16 00:50:10 820 907 E ActivityManager: ANR in com.android.systemui, time=130090695
    09-16 00:50:10 820 907 E ActivityManager: Reason: Broadcast of Intent { act=android.intent.action.TIME_TICK
    flg=0x50000114 (has extras) }
    09-16 00:50:10 820 907 E ActivityManager: Load: 30.4 / 22.34 / 19.94
    09-16 00:50:10 820 907 E ActivityManager: Android time :[2015-10-16 00:50:05.76] [130191,266]
    09-16 00:50:10 820 907 E ActivityManager: CPU usage from 6753ms to -4ms ago:
    09-16 00:50:10 820 907 E ActivityManager: 47% 320/netd: 3.1% user + 44% kernel / faults: 14886 minor 3 major
    09-16 00:50:10 820 907 E ActivityManager: 15% 10007/com.sohu.sohuvideo: 2.8% user + 12% kernel / faults: 1144
    minor
    09-16 00:50:10 820 907 E ActivityManager: 13% 10654/hif_thread: 0% user + 13% kernel
    09-16 00:50:10 820 907 E ActivityManager: 11% 175/mmcqd/0: 0% user + 11% kernel
    09-16 00:50:10 820 907 E ActivityManager: 5.1% 12165/app_process: 1.6% user + 3.5% kernel / faults: 9703 minor
    540 major
    09-16 00:50:10 820 907 E ActivityManager: 3.3% 29533/com.android.systemui: 2.6% user + 0.7% kernel / faults:
    8402 minor 343 major
    09-16 00:50:10 820 907 E ActivityManager: 3.2% 820/system_server: 0.8% user + 2.3% kernel / faults: 5120 minor
    523 major
    09-16 00:50:10 820 907 E ActivityManager: 2.5% 11817/com.netease.pomelo.push.l.messageservice_V2: 0.7% user +
    1.7% kernel / faults: 7728 minor 687 major
    09-16 00:50:10 820 907 E ActivityManager: 1.6% 11887/com.android.email: 0.5% user + 1% kernel / faults: 6259
    minor 587 major
    09-16 00:50:10 820 907 E ActivityManager: 1.4% 11854/com.android.settings: 0.7% user + 0.7% kernel / faults:
    5404 minor 471 major
    09-16 00:50:10 820 907 E ActivityManager: 1.4% 11869/android.process.acore: 0.7% user + 0.7% kernel / faults:
    6131 minor 561 major
    09-16 00:50:10 820 907 E ActivityManager: 1.3% 11860/com.tencent.mobileqq: 0.1% user + 1.1% kernel / faults:
    5542 minor 470 major
    ...
    09-16 00:50:10 820 907 E ActivityManager: +0% 12832/cat: 0% user + 0% kernel
    09-16 00:50:10 820 907 E ActivityManager: +0% 13211/zygote64: 0% user + 0% kernel
    09-16 00:50:10 820 907 E ActivityManager: 87% TOTAL: 3% user + 18% kernel + 64% iowait + 0.5% softirq
    

    调用堆栈中的信息

    
    main(线程名)、 prio(线程优先级,默认是5)、 tid(线程唯一标识ID)、 Sleeping(线程当前状态)
    "main" prio=5 tid=1 Sleeping
    | group="main" sCount=1 dsCount=0 obj=0x73132d10 self=0x5598a5f5e0
    //sysTid是线程号(主线程的线程号和进程号相同)
    | sysTid=17027 nice=0 cgrp=default sched=0/0 handle=0x7fb6db6fe8
    | state=S schedstat=( 420582038 5862546 143 ) utm=24 stm=18 core=6 HZ=100
    | stack=0x7fefba3000-0x7fefba5000 stackSize=8MB
    | held mutexes=
    // java 堆栈调用信息(这里可查看导致ANR的代码调用流程)(分析ANR最重要的信息)
    at java.lang.Thread.sleep!(Native method)
    - sleeping on <0x0c60f3c7> (a java.lang.Object)
    at java.lang.Thread.sleep(Thread.java:1031)
    - locked <0x0c60f3c7> (a java.lang.Object) // 锁住对象0x0c60f3c7
    at java.lang.Thread.sleep(Thread.java:985)
    at android.os.SystemClock.sleep(SystemClock.java:120)
    at org.code.ipc.MessengerService.onCreate(MessengerService.java:63) //导致ANR的代码
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877)
    at android.app.ActivityThread.access$1900(ActivityThread.java:150)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke!(Native method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
    
    线程状态 含义
    SUSPENDED 线程暂停,可能是由于输出Trace、 GC或debug被暂停
    NATIVE 正在执行JNI本地函数
    MONITOR 线程阻塞,等待获取对象锁
    WAIT 执行了无限等待的wait函数
    TIMED_WAIT 执行了带有超时参数的wait、 sleep或join函数
    VMWAIT 正在等待VM资源
    RUNNING/RUNNABLE 线程可运行或正在运行
    INITALIZING 新建,正在初始化,为其分配资源
    STARTING 新建,正在启动
    ZOMBIE 线程死亡,终止运行,等待父线程回收它
    UNKNOWN 未知状态

    辅助关键字

    Log关键字 含义
    am_proc_start 开始创建应用进程
    am_proc_bound 应用进程创建完毕
    am_restart_activity realActivityStart 创建进程完成后首次启动应用
    am_resume_activity 窗口Resume开始
    am_on_resume_called 窗口Resume完毕
    am_pause_activity 窗口Pause开始
    am_on_paused_called 窗口Pause完毕
    am_failed_to_pause 窗口Pause超时
    am_finish_activity 应用Finish开始
    am_proc_died 进程死亡( 比如被LowMemoryKiller杀死)

    日志的获取

    • logcat 通过adb logcat命令输出Android的一些当前运行日志,可以通过logcat的 -b 参数指定要输出的日志缓
      冲区,缓冲区对应着logcat的一种日志类型。
    • adb logcat –b all
    • adb logcat –b radio
    • adb logcat –b system
    • adb logcat –b events
    • adb logcat –b main

    ANR(Application Not Responding)日志: /data/anr/traces.txt

    相关文章

      网友评论

        本文标题:给解决问题ANR一个印象

        本文链接:https://www.haomeiwen.com/subject/nhrnpxtx.html