Logcat日志分析

发布于:2025-07-26 ⋅ 阅读:(19) ⋅ 点赞:(0)

1. AndroidRuntime关键字(跟整个系统代码相关)

1.1、AndroidRuntime的核心作用

AndroidRuntime是Android系统负责启动和运行应用程序的核心组件,当应用因未处理的异常(如空指针、数组越界等)导致崩溃时,AndroidRuntime会捕获这些异常,并在log中输出详细信息,帮助开发者定位问题。

1.2、AndroidRuntime日志的典型格式

AndroidRuntime日志通常包含以下关键信息,格式大致如下:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.myapp, PID: 12345
    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
        at com.example.myapp.MainActivity.updateText(MainActivity.java:42)
        at com.example.myapp.MainActivity.onClick(MainActivity.java:28)
        at android.view.View.performClick(View.java:7448)
        ...(系统调用栈)
关键信息解析:
  1. 日志级别(E/)E表示Error(错误),是最高级别之一,说明发生了严重问题。
  2. 关键字(AndroidRuntime):明确标识该日志由AndroidRuntime组件输出。
  3. 错误类型(FATAL EXCEPTION):表示发生了致命异常,导致应用强制终止。
  4. 进程信息(Process: … PID: …):显示崩溃的应用包名和进程ID,方便定位具体应用。
  5. 异常详情
    • 异常类型(如NullPointerExceptionIndexOutOfBoundsException等)。
    • 异常描述(如“Attempt to invoke virtual method on a null object reference”,说明对空对象调用了方法)。
  6. 调用栈(Stack Trace):从at ...开始,显示异常发生的代码位置(类名、方法名、文件名、行号),是排查问题的核心依据(例如上述MainActivity.java:42表示错误发生在该文件的第42行)。

总结

AndroidRuntime是Android日志中标识应用崩溃的核心关键字,其日志包含的异常类型、调用栈等信息是解决应用崩溃问题的“关键线索”。开发者在调试时,可通过Android Studio的Logcat工具搜索该关键字,快速定位并修复错误。























2. ANR关键字(跟主线程相关)

在Android开发中,ANR(Application Not Responding) 是指应用程序无响应,是影响用户体验的严重问题。当应用在主线程(UI线程)执行耗时操作,导致无法及时响应用户输入或系统请求时,就会触发ANR。下面从多个角度详细解析ANR:

2.1、ANR的触发条件(系统默认超时时间)

  1. 输入事件(如点击、触摸):5秒内未处理完成。(定义在ActivityManagerService的KEY_DISPATCHING_TIMEOUT
  2. BroadcastReceiver:前台广播10秒内、后台广播60秒内未处理完成。
  3. Service:启动超时20秒、绑定超时10秒。
  4. ContentProvider:publish超时10秒。

2.2、ANR的常见原因

  1. 主线程执行耗时操作(最常见):
    • 网络请求(如HTTP请求、数据库查询)。
    • 大量IO操作(如文件读写、大图片解码)。
    • 复杂计算(如加密、算法处理)。
  2. 死锁:两个或多个线程互相等待对方释放锁。
  3. Binder通信超时:跨进程通信(IPC)时,目标进程响应过慢。
  4. 系统资源耗尽:CPU、内存不足,导致应用无法正常执行。

2.3、ANR日志分析步骤

当ANR发生时,系统会生成日志并弹窗提示用户(如“应用无响应,是否关闭?”)。日志位置通常在:

  • /data/anr/traces.txt(需要root权限)
  • Android Studio Logcat:搜索关键字ANRActivityManager
第一步:查看Logcat日志

查看是否是受CPU影响

ANR in com.example.myapp
PID: 12345
Reason: Input dispatching timed out (Waiting because the touched window has not finished processing the input events that were previously delivered to it.)
Load: 1.2 / 0.8 / 0.4
CPU usage from 30000ms to 0ms ago (2025-07-25 10:14:30 to 10:15:30):
  98% 8765/com.example.myapp: 92% user + 6% kernel / faults: 2340 minor 12 major
  1.2% 8770/com.example.myapp: 1% user + 0.2% kernel / faults: 156 minor
  0.8% 8792/com.example.myapp: 0.7% user + 0.1% kernel / faults: 98 minor
  0.5% 8766/com.example.myapp: 0.3% user + 0.2% kernel / faults: 45 minor
  ...(系统进程CPU占用省略)
  0.1% TOTAL: 0% user + 0.1% kernel
第二步:查看traces.txt日志分析原因并找到产生ANR的部分,然后对代码进行修改

=====================================================================

以下针对 iowait过高线程阻塞(Block)内存泄漏(Memory Leak) 三种场景,分别给出对应的 traces.txt 日志示例,并分析问题根源与解决方案。

举例:

2.3.1、iowait过高(I/O等待导致的性能问题)
traces.txt 关键片段
----- pid 8765 at 2025-07-25 14:30:00 -----
Cmd line: com.example.myapp

DALVIK THREADS:
"main" prio=5 tid=1 TIMED_WAITING
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x72f4a3c0 self=0x7a0c2d0000
  | sysTid=8765 nice=0 cgrp=default sched=0/0 handle=0x7a0c39e1d0
  | state=S schedstat=( 543210000 456780000 2345 ) utm=50 stm=4 core=3 HZ=100
  | stack=0x7ff5d3e000-0x7ff5d40000 stackSize=8MB
  | held mutexes=
  at java.io.FileInputStream.readBytes(Native method)
  - waiting on <0x0f2a1b40> (a java.io.FileInputStream)
  at java.io.FileInputStream.read(FileInputStream.java:255)
  at java.io.BufferedInputStream.fill(BufferedInputStream.java:248)
  at java.io.BufferedInputStream.read(BufferedInputStream.java:267)
  at libcore.io.IoBridge.read(IoBridge.java:496)
  at java.io.FileInputStream.read(FileInputStream.java:232)
  at com.example.myapp.io.LargeFileParser.parse(LargeFileParser.java:42)
  at com.example.myapp.ui.MainActivity.loadData(MainActivity.java:65)
  at com.example.myapp.ui.MainActivity.onCreate(MainActivity.java:32)
  at android.app.Activity.performCreate(Activity.java:8000)
  ...

"FinalizerDaemon" daemon prio=5 tid=3 WAITING
  | group="system" sCount=1 dsCount=0 flags=1 obj=0x72f4e0d0 self=0x7a0c320000
  | sysTid=8768 nice=8 cgrp=default sched=0/0 handle=0x7a0c28b1d0
  | state=S schedstat=( 123456789 12345678 123 ) utm=10 stm=2 core=0 HZ=100
  | stack=0x7a0c18c000-0x7a0c18e000 stackSize=1037KB
  | held mutexes=
  at java.lang.Object.wait(Native method)
  - waiting on <0x0f2a1c80> (a java.lang.ref.ReferenceQueue$Lock)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155)
  ...

CPU usage from 30000ms to 0ms ago (2025-07-25 14:29:30 to 14:30:00):
  85% 8765/com.example.myapp: 5% user + 80% kernel / faults: 1200 minor 5 major
  0.5% 8768/com.example.myapp: 0% user + 0.5% kernel / faults: 15 minor
  0.2% 8769/com.example.myapp: 0% user + 0.2% kernel / faults: 10 minor
  ...
  0.1% TOTAL: 0% user + 0.1% kernel
问题分析
  • 关键线索

    • 主线程状态为 TIMED_WAITING,卡在 java.io.FileInputStream.readBytes() 本地方法,说明正在进行磁盘读取。
    • CPU 使用率中 kernel 占比极高(80%),表明大量时间消耗在内核态的 I/O 操作。
    • 调用链显示 MainActivity.onCreate()loadData()LargeFileParser.parse(),说明在 Activity 创建时同步读取大文件。
  • 问题根源
    在主线程执行耗时 I/O 操作(如读取 100MB+ 的文件),导致 CPU 长时间等待磁盘响应,iowait 升高,应用卡顿甚至 ANR。

解决方案
  • 异步化:将 I/O 操作移至子线程(如 ExecutorsCoroutine):
    // 主线程
    Executors.newSingleThreadExecutor().execute(() -> {
        // 子线程执行文件解析
        LargeFileParser.parse(filePath);
        // 解析完成后通过 Handler 切回主线程更新 UI
    });
    
  • 优化 I/O:使用 BufferedInputStream 减少磁盘访问次数,或分块读取大文件。

=====================================================================

2.3.2、线程阻塞(Block)

traces.txt 关键片段
----- pid 8765 at 2025-07-25 14:35:00 -----
Cmd line: com.example.myapp

DALVIK THREADS:
"main" prio=5 tid=1 BLOCKED
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x72f4a3c0 self=0x7a0c2d0000
  | sysTid=8765 nice=0 cgrp=default sched=0/0 handle=0x7a0c39e1d0
  | state=S schedstat=( 23456000 1234000 56 ) utm=2 stm=0 core=3 HZ=100
  | stack=0x7ff5d3e000-0x7ff5d40000 stackSize=8MB
  | held mutexes=
  at com.example.myapp.data.UserManager.getUser(UserManager.java:55)
  - waiting to lock <0x0f2a1b30> (a com.example.myapp.data.UserManager) held by thread 6
  at com.example.myapp.ui.HomeActivity.refreshUserInfo(HomeActivity.java:120)
  at com.example.myapp.ui.HomeActivity.onResume(HomeActivity.java:80)
  at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1456)
  ...

"NetworkThread" prio=5 tid=6 RUNNABLE
  | group="main" sCount=0 dsCount=0 flags=1 obj=0x72f5c6a0 self=0x7a0c310000
  | sysTid=8771 nice=5 cgrp=default sched=0/0 handle=0x7a0c29d1d0
  | state=R schedstat=( 187654000 15623000 876 ) utm=17 stm=1 core=1 HZ=100
  | stack=0x7a0c19e000-0x7a0c1a0000 stackSize=1037KB
  | held mutexes=
  at com.example.myapp.data.UserManager.updateUser(UserManager.java:90)
  - locked <0x0f2a1b30> (a com.example.myapp.data.UserManager)
  at com.example.myapp.network.ApiService$1.onResponse(ApiService.java:45)
  at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:89)
  at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1$$ExternalSyntheticLambda0.run(Unknown Source:6)
  at android.os.Handler.handleCallback(Handler.java:942)
  ...

CPU usage from 30000ms to 0ms ago (2025-07-25 14:34:30 to 14:35:00):
  30% 8765/com.example.myapp: 25% user + 5% kernel / faults: 345 minor 2 major
  25% 8771/com.example.myapp: 23% user + 2% kernel / faults: 210 minor
  0.5% 8768/com.example.myapp: 0% user + 0.5% kernel / faults: 15 minor
  ...
  0.1% TOTAL: 0% user + 0.1% kernel
问题分析
  • 关键线索

    • 主线程状态为 BLOCKED,卡在 UserManager.getUser(),等待锁 <0x0f2a1b30>
    • NetworkThread 状态为 RUNNABLE,持有锁 <0x0f2a1b30>,正在执行 UserManager.updateUser()
    • 调用链显示:主线程在 onResume() 时调用 getUser(),而子线程在网络请求回调中调用 updateUser(),两者争夺同一把锁。
  • 问题根源

    • 锁竞争UserManager 中的 getUser()updateUser() 使用同一把对象锁,导致线程互相等待。
    • 主线程风险:主线程参与锁竞争,若锁被长时间持有(如网络请求期间),会直接导致 ANR。
解决方案
  • 减小锁粒度:仅在必要代码块加锁,避免整个方法同步:
    public class UserManager {
        private final Object lock = new Object();
        
        public User getUser() {
            User result;
            synchronized (lock) {
                // 仅在读取共享资源时加锁
                result = cache.getUser();
            }
            return result;
        }
        
        public void updateUser(User user) {
            synchronized (lock) {
                // 更新操作加锁
                cache.saveUser(user);
            }
            // 锁外执行其他耗时操作(如网络请求)
        }
    }
    
  • 主线程异步化:将 getUser() 改为异步调用,避免主线程等待锁:
    // 主线程
    Executors.newSingleThreadExecutor().execute(() -> {
        User user = userManager.getUser();
        // 通过 Handler 切回主线程更新 UI
    });
    

=====================================================================

2.3.3、内存泄漏(Memory Leak)

traces.txt 关键片段
----- pid 8765 at 2025-07-25 14:40:00 -----
Cmd line: com.example.myapp

DALVIK THREADS:
"main" prio=5 tid=1 RUNNABLE
  | group="main" sCount=0 dsCount=0 flags=1 obj=0x72f4a3c0 self=0x7a0c2d0000
  | sysTid=8765 nice=0 cgrp=default sched=0/0 handle=0x7a0c39e1d0
  | state=R schedstat=( 345678900 234567800 1234 ) utm=32 stm=2 core=3 HZ=100
  | stack=0x7ff5d3e000-0x7ff5d40000 stackSize=8MB
  | held mutexes=
  at com.example.myapp.ui.NewActivity.onCreate(NewActivity.java:42)
  at android.app.Activity.performCreate(Activity.java:8000)
  at android.app.Activity.performCreate(Activity.java:7989)
  ...

"ReferenceQueueDaemon" daemon prio=5 tid=4 WAITING
  | group="system" sCount=1 dsCount=0 flags=1 obj=0x72f5e0e0 self=0x7a0c330000
  | sysTid=8769 nice=8 cgrp=default sched=0/0 handle=0x7a0c27a1d0
  | state=S schedstat=( 123456789 12345678 123 ) utm=10 stm=2 core=0 HZ=100
  | stack=0x7a0c17b000-0x7a0c17d000 stackSize=1037KB
  | held mutexes=
  at java.lang.Object.wait(Native method)
  - waiting on <0x0f2a1d80> (a java.lang.ref.ReferenceQueue$Lock)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155)
  ...

HEAP SUMMARY:
  Alloc = 125678KB, Used = 115678KB, Free = 10000KB
  Heap sizes: 128MB, max: 256MB, growth limit: 256MB
  Allocation spaces: [image: 1048KB, large_objects: 15678KB, normal: 110000KB]
  ...

Garbage collector: 15 collections, 0 failed, 42ms elapsed, 0% CPU time
  [ 2025-07-25 14:35:00 ] Heap before GC invocations=14 (full 1):
    ...
    - 2 instances of com.example.myapp.ui.OldActivity, 160B (160B retained)
    - 10 instances of com.example.myapp.data.User, 400B (400B retained)
    ...
  [ 2025-07-25 14:38:00 ] Heap after GC invocations=15 (full 1):
    ...
    - 2 instances of com.example.myapp.ui.OldActivity, 160B (160B retained)
    - 10 instances of com.example.myapp.data.User, 400B (400B retained)
    ...
问题分析
  • 关键线索

    • GC 后仍存在多个已销毁 Activity 实例:日志显示 GC 后仍有 2 个 OldActivity 实例未被回收(正常应被销毁)。
    • 内存占用高:Heap 中 Used = 115678KB,接近 max: 256MB,频繁 GC 但内存未释放。
    • 无显式阻塞线程:线程状态正常,但内存持续增长,说明存在对象无法被 GC。
  • 问题根源
    示例代码中静态集合 sUserList 持有 Activity 引用,导致 Activity 销毁后无法被回收。

解决方案
  • 避免静态集合持有 Activity 引用:使用弱引用(WeakReference):
    public class UserManager {
        private static final List<WeakReference<Activity>> sActivityRefs = new ArrayList<>();
        
        public void addActivity(Activity activity) {
            sActivityRefs.add(new WeakReference<>(activity));
        }
    }
    
  • 及时清理引用:在 Activity 销毁时移除引用:
    @Override
    protected void onDestroy() {
        super.onDestroy();
        userManager.removeActivity(this); // 从静态集合中移除
    }
    

总结对比

场景 traces.txt 关键特征 问题根源 解决方案
iowait 过高 主线程卡在 java.io 相关方法,kernel CPU 占比高 主线程执行耗时 I/O 操作 异步化 I/O,优化读写效率
线程阻塞 主线程状态为 BLOCKED,等待锁被其他线程持有 多线程争夺同一把锁,主线程参与竞争 减小锁粒度,主线程异步化
内存泄漏 GC 后 Activity 实例仍存在,内存持续增长 长生命周期对象(如静态变量)持有 Activity 引用 使用弱引用,及时清理引用

通过分析 traces.txt 中的线程状态、调用链和内存信息,可快速定位性能问题的根本原因。

=====================================================================

2.4、如何避免ANR

主线程执行耗时操作(最常见)

将耗时操作移至子线程
  • 网络请求:使用AsyncTaskKotlin协程RxJavaRetrofit等异步框架。
  • 文件操作:通过线程池或IntentService执行。
优化BroadcastReceiver
  • 避免在onReceive()中执行耗时操作,可通过startService()或发送Intent给Service处理。
使用Handler处理UI更新
  • 子线程完成耗时操作后,通过Handler切换到主线程更新UI。
避免死锁
  • 减少锁的使用,确保线程按相同顺序获取锁。
  • 使用ReentrantLocktryLock()避免无限等待。
检测ANR的工具
  • Systrace:分析系统性能瓶颈,定位耗时操作。
  • Android Profiler:监控CPU、内存使用情况,识别长时间运行的方法。

总结

ANR是Android开发中需要重点关注的问题,核心解决思路是避免主线程执行耗时操作,并通过工具监控和优化代码。遇到ANR时,优先分析traces.txt中的线程堆栈,定位耗时操作的具体位置,再针对性地优化代码逻辑或线程管理。













3. 系统核心进程相关

Android系统进程的日志通常涉及系统稳定性,常见关键字:

1. ActivityManager(AM)

系统组件管理的核心进程,日志中频繁出现,用于追踪Activity、Service、Broadcast等组件的生命周期:

  • 示例:ActivityManager: Start proc 1234:com.example.app/u0a123 for activity(启动应用进程)。
  • 问题场景:
    • ActivityManager: Force finishing activity com.example.app/.MainActivity(Activity被系统强制终止,可能因ANR或内存不足)。
    • ActivityManager: Low Memory: Killing proc 1234:com.example.app/u0a123(应用因低内存被系统杀死)。
2. PackageManager(PM)

应用包管理进程,涉及安装、卸载、权限等操作:

  • 示例:PackageManager: Installation error code: -103(安装失败,通常因权限或签名问题)。
  • 问题场景:PackageManager: Failed to grant permissions to package(权限授予失败)。
3. WindowManager(WM)

窗口管理进程,与UI显示、窗口切换相关:

  • 示例:WindowManager: android.view.WindowLeaked: Activity ... has leaked window ...(窗口泄漏,如对话框未关闭时Activity被销毁)。

4. 资源与性能相关

1. 内存相关
  • GC_FOR_ALLOC:因内存分配不足触发的垃圾回收(GC)。
    频繁出现可能预示内存紧张,需检查内存泄漏或大对象分配。
  • MemoryLeak:内存泄漏(部分工具如LeakCanary会显式标记)。
    日志中可能伴随LeakCanary: Found 1 memory leak
  • OOM:即OutOfMemoryError,内存溢出(前文已提及)。
2. CPU与线程相关
  • Thread:线程相关日志,如Thread-1: InterruptedException(线程中断异常)。
  • DeadLock:死锁,日志中可能出现Found a potential deadlock(需通过adb shell dumpsys threaddump分析)。
  • Systrace:系统跟踪工具生成的日志,用于分析CPU调度、帧渲染延迟(关键字如ChoreographerVSYNC)。
3. IO与网络相关
  • IOException:IO异常,如文件不存在(FileNotFoundException)、网络连接失败(ConnectException)。
  • NetworkOnMainThreadException:主线程网络操作异常(Android 3.0+禁止主线程直接请求网络)。

5. 权限与安全相关

  • PermissionDenied:权限被拒绝,如java.lang.SecurityException: Permission denied: ...
    场景:未申请WRITE_EXTERNAL_STORAGE却写入文件。
  • SELinux:安卓安全机制相关错误,如avc: denied { read } for ...(SELinux权限不足,多出现于系统应用或定制ROM)。
  • Signature check failed:签名验证失败,如应用签名与系统要求不匹配(多出现于系统级应用安装)。

6. 组件与生命周期相关

  • Activity/Service/BroadcastReceiver/Fragment:组件生命周期日志,如Activity.onPause()Service.onDestroy()
    异常场景:Activity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView(Activity销毁后窗口未关闭)。
  • Intent:意图相关错误,如ActivityNotFoundException(找不到匹配的Activity)。

7. 系统事件与状态

  • BootCompleted:系统启动完成事件(广播ACTION_BOOT_COMPLETED触发)。
  • LowBattery:低电量事件,系统可能触发应用后台限制。
  • ScreenOn/ScreenOff:屏幕亮屏/熄屏事件,影响应用唤醒状态。

8. 第三方库与工具相关

  • Retrofit/OkHttp:网络库日志,如OkHttp: --> GET https://api.example.com(请求日志)、OkHttp: <-- HTTP 500(服务器错误)。
  • Glide/Picasso:图片加载库错误,如Glide: Load failed for ...(图片加载失败)。
  • Firebase/Crashlytics:崩溃上报工具日志,如Crashlytics: Sending crash report

总结:关键日志定位思路

  1. 崩溃问题:优先搜索ExceptionErrorCrash,定位具体异常类型(如NPE、OOM)。
  2. 性能问题:关注ANRGC_iowaitBlock,结合Systrace分析。
  3. 系统交互问题:查看ActivityManagerPackageManager,判断组件生命周期或权限问题。
  4. 第三方库问题:根据库名(如OkHttpGlide)筛选日志,结合官方文档排查。

掌握这些关键字和场景,能快速缩小问题范围,提高调试效率。实际排查时,可结合logcat过滤命令(如adb logcat *:E查看所有错误日志)精准定位。


网站公告

今日签到

点亮在社区的每一天
去签到