当前位置: 首页 > news >正文

Logcat日志分析

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

1.1、AndroidRuntime的核心作用

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

1.2、AndroidRuntime日志的典型格式

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

E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.example.myapp, PID: 12345java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object referenceat 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 major1.2% 8770/com.example.myapp: 1% user + 0.2% kernel / faults: 156 minor0.8% 8792/com.example.myapp: 0.7% user + 0.1% kernel / faults: 98 minor0.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.myappDALVIK 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 major0.5% 8768/com.example.myapp: 0% user + 0.5% kernel / faults: 15 minor0.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.myappDALVIK 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 6at 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 major25% 8771/com.example.myapp: 23% user + 2% kernel / faults: 210 minor0.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.myappDALVIK 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 = 10000KBHeap sizes: 128MB, max: 256MB, growth limit: 256MBAllocation 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查看所有错误日志)精准定位。

http://www.dtcms.com/a/298740.html

相关文章:

  • SpringBoot 获取请求参数的常用注解
  • 自由学习记录(73)
  • 地铁逃生
  • 注意力机制的使用说明01
  • RNN模型数学推导过程(笔记)
  • 散列表(哈希表)
  • SQL基础⑮ | 触发器
  • 亚德诺半导体AD8539ARZ-REEL7 超低功耗轨到轨运算放大器,自动归零技术,专为可穿戴设备设计!
  • Python 程序设计讲义(20):选择结构程序设计——双分支结构的简化表示(三元运算符)
  • 【linux】Haproxy七层代理
  • 电子基石:硬件工程师的器件手册 (八) - 栅极驱动IC:功率器件的神经中枢
  • 【自动化运维神器Ansible】Ansible常用模块之Copy模块详解
  • 程序代码篇---卡尔曼滤波与PID的组合应用
  • 2.Linux 网络配置
  • 【PyTorch】图像多分类项目部署
  • python基础:request模块简介与安装、基本使用,如何发送get请求响应数据,response属性与请求头
  • centOS7 yum安装新版本的cmake,cmake3以上怎么安装,一篇文章说明白
  • Java并发编程第十篇(ThreadPoolExecutor线程池组件分析)
  • 无印 v1.6 视频解析去水印工具,支持多个平台
  • Android悬浮窗导致其它应用黑屏问题解决办法
  • RocketMQ 5.3.0 ARM64 架构安装部署指南
  • J2EE模式---数据访问对象模式
  • C语言案例《猜拳游戏》
  • VSCode 报错 Error: listen EACCES: permission denied 0.0.0.0:2288
  • Java 笔记 interface
  • C#入门实战:数字计算与条件判断
  • Web攻防-业务逻辑篇密码找回重定向目标响应包检验流程跳过回显泄露验证枚举
  • 【PyTorch】图像多分类项目
  • 一些常见的网络攻击方式
  • CY5-OVA科研方向,星戈瑞荧光