IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 21.9.17 ANR、File类 -> 正文阅读

[移动开发]21.9.17 ANR、File类

特别申明:本文仅供自己学习记录使用,所写内容来自各网页,如需转载自己去查找内容出处。如有侵权请联系在下,评论、私信等不论。

目录

一、ANR

1 ANR简介

2 ANR原因

3 分析ANR的重点

3.1 cpu占用率方面:

3.2 内存方面

4 ANR日志分析

5 版本声明

二、File类


一、ANR

1 ANR简介

ANR全称:Application Not Responding,也就是应用程序无响应。

ANR原因
Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。

首先,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。ANR由消息处理机制保证,Android在系统层实现了一套精密的机制来发现ANR,核心原理是消息调度和超时处理。

其次,ANR机制主体实现在系统层。所有与ANR相关的消息,都会经过系统进程(system_server)调度,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。 一旦应用程序处理消息不当,超时限制就起作用了,它收集一些系统状态,譬如CPU/IO使用情况、进程函数调用栈,并且报告用户有进程无响应了(ANR对话框)。

然后,ANR问题本质是一个性能问题。ANR机制实际上对应用程序主线程的限制,要求主线程在限定的时间内处理完一些最常见的操作(启动服务、处理广播、处理输入), 如果处理超时,则认为主线程已经失去了响应其他操作的能力。主线程中的耗时操作,譬如密集CPU运算、大量IO、复杂界面布局等,都会降低应用程序的响应能力。

ANR出现场景
发生ANR时会调用AppNotRespondingDialog.show()方法弹出对话框提示用户,该对话框的依次调用关系如下图所示:

AppErrors.appNotResponding(),该方法是最终弹出ANR对话框的唯一入口,调用该方法的场景才会有ANR提示,也可以认为在主线程中执行无论再耗时的任务,只要最终不调用该方法,都不会有ANR提示,也不会有ANR相关日志及报告;通过调用关系可以看出哪些场景会导致ANR,有以下四种场景:

InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件。
BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
ContentProvider Timeout :ContentProvider的publish在10s内没进行完。
?

2 ANR原因

  • 主线程慢代码
  • 主线程IO
  • 锁竞争
  • 死锁

如何避免ANR
1.UI线程尽量只做跟UI相关的工作;
2.耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理;
3.尽量用Handler来处理UI thread和别的thread之间的交互;
4.实在绕不开主线程,可以尝试通过Handler延迟加载;
5.广播中如果有耗时操作,建议放在IntentService中去执行,或者通过goAsync() + HandlerThread分发执行。

3 分析ANR的重点

3.1 cpu占用率方面:

可以通过分析各进程的CPU时间占用率,来判断是否为某些进程长期占用CPU导致该进程无法获取到足够的CPU处理时间,而导致ANR重点关注下CPU的负载,各个进程总的CPU时间占用率,用户CPU时间占用率,核心态CPU时间占用率,以及iowait CPU时间占用率。

3.2 内存方面

主要看当前应用native和dalvik层内存使用情况,结合系统给每个应用分配的最大内存来分析。

4 ANR日志分析

当app出现ANR时会在data/anr/目录下生成traces.txt日志文件。每次发生ANR时都会删除旧的traces文件,重新创建新文件。也就是说Android只保留最后一次发生ANR时的信息。

首先,我们可以使用adb命令导出traces文件:

adb pull /data/anr/traces.txt d:\

友情提示:traces.txt默认会被导出到Android SDK的\platform-tools目录。

开发中最方便的是在log里面就可以看到ANR的相关信息,以下面的日志为例,我们可以从Android studio logcat很明显的看出ANR发生的原因,用户的输入超时了,问题线程的PID:879。

同时我们还可以通俗易懂的看出来 CPU平均负载,CPU的使用情况:

4.67 ,3.32 ,1.49 分别表示 发生`ANR` 前一分钟,五分钟,十五分钟 `CPU`的平均负载 Load: 4.67 / 3.32 / 1.49 CPU usage from 6021ms to 79ms ago。

接下来还是回到进一步分析traces.txt文件上来,看文件里面的内容:

?
----- pid 879 at 2019-01-02 08:05:04 -----
Cmd line: com.sandiyu.lcd
?
JNI: CheckJNI is off; workarounds are off; pins=2; globals=273
?
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
?
"main" prio=5 tid=1 WAIT
? | group="main" sCount=1 dsCount=0 obj=0x4159cd68 self=0x414d6510
? | sysTid=879 nice=0 sched=0/0 cgrp=apps handle=1074020692
? | state=S schedstat=( 0 0 0 ) utm=602 stm=168 core=1
? at java.lang.Object.wait(Native Method)
? - waiting on <0x4159ce38> (a java.lang.VMThread) held by tid=1 (main)
? at java.lang.Thread.parkFor(Thread.java:1205)
? at sun.misc.Unsafe.park(Unsafe.java:325)
? at java.util.concurrent.locks.LockSupport.park(LockSupport.java:157)
? at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2017)
? at java.util.concurrent.LinkedBlockingQueue.put(LinkedBlockingQueue.java:318)
? at com.sandiyu.lcd.utils.DeviceCommandSender$CommandSendThread.send(DeviceCommandSender.java:156)
? at com.sandiyu.lcd.utils.DeviceCommandSender.displayNull(DeviceCommandSender.java:81)
? at com.sandiyu.lcd.DlpPrintActivity$PrintRunnable.clearImage(DlpPrintActivity.java:884)
? at com.sandiyu.lcd.DlpPrintActivity$PrintRunnable.access$1900(DlpPrintActivity.java:253)
? at com.sandiyu.lcd.DlpPrintActivity.onBackPressed(DlpPrintActivity.java:954)
? at android.app.Activity.onKeyUp(Activity.java:2193)
??
? ...


一般trace文件顶部的线程即为ANR的元凶,找到了犯罪线程我们就可以查看、分析一下犯罪现场。

line 1,2
----- pid 879 at 2019-01-02 08:05:04 -----
Cmd line: com.sandiyu.lcd
可以看到ANR 发生的进程id,时间,名称。

line 3,4,5
JNI: CheckJNI is off; workarounds are off; pins=2; globals=273
?
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
可以看到线程的基本信息(tll:thread list lock,tsl:thread suspend lock,tscl:thread suspend count lock,ghl:gc heap lock)。

line "main"
"main" prio=5 tid=1 WAIT
这一行说明了线程名称,优先级,线程锁id和线程状态。可以看到本次ANR 线程为WAIT状态。

额外补充一下线程状态有如下几种:

java thread 状态?? ?cpp thread状态?? ?说明
TERMINATED?? ?ZOMBIE?? ?线程死亡,终止运行
RUNNABLE?? ?RUNNING/RUNNABLE?? ?线程可运行或正在运行
TIMED_WAITING?? ?TIMED_WAIT?? ?执行了带有超时参数的wait、sleep或join函数
BLOCKED?? ?MONITOR?? ?线程阻塞,等待获取对象锁
WAITING?? ?WAIT?? ?执行了无超时参数的wait函数
NEW?? ?INITIALIZING?? ?新建,正在初始化,为其分配资源
NEW?? ?STARTING?? ?新建,正在启动
RUNNABLE?? ?NATIVE?? ?正在执行JNI本地函数
WAITING?? ?VMWAIT?? ?正在等待VM资源
RUNNABLE?? ?SUSPENDED?? ?线程暂停,通常是由于GC或debug被暂停
??? ?UNKNOWN?? ?未知状态
接着往下面的信息看

at com.sandiyu.lcd.utils.DeviceCommandSender$CommandSendThread.send(DeviceCommandSender.java:156)
? at com.sandiyu.lcd.utils.DeviceCommandSender.displayNull(DeviceCommandSender.java:81)
? at com.sandiyu.lcd.DlpPrintActivity$PrintRunnable.clearImage(DlpPrintActivity.java:884)
? at com.sandiyu.lcd.DlpPrintActivity$PrintRunnable.access$1900(DlpPrintActivity.java:253)
? at com.sandiyu.lcd.DlpPrintActivity.onBackPressed(DlpPrintActivity.java:954)
在这里我们就找到了原因,CommandSendThread.send需要等待网络资源来更新UI,连接中断了,这时候点击onBackPressed长时间得不到相应,它就报了ANR了。

5 版本声明

————————————————
版权声明:本文为CSDN博主「mysimplelove」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:链接

二、File类

    • Constructor and Description
      File(File?parent, String?child)

      从父抽象路径名和子路径名字符串创建新的 File实例。

      File(String?pathname)

      通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。

      File(String?parent, String?child)

      从父路径名字符串和子路径名字符串创建新的 File实例。

      File(URI?uri)

      通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。

    • booleancanExecute()

      测试应用程序是否可以执行此抽象路径名表示的文件。

      booleancanRead()

      测试应用程序是否可以读取由此抽象路径名表示的文件。

      booleancanWrite()

      测试应用程序是否可以修改由此抽象路径名表示的文件。

      intcompareTo(File?pathname)

      比较两个抽象的路径名字典。

      booleancreateNewFile()

      当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。

      static FilecreateTempFile(String?prefix, String?suffix)

      在默认临时文件目录中创建一个空文件,使用给定的前缀和后缀生成其名称。

      static FilecreateTempFile(String?prefix, String?suffix, File?directory)

      在指定的目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。

      booleandelete()

      删除由此抽象路径名表示的文件或目录。

      voiddeleteOnExit()

      请求在虚拟机终止时删除由此抽象路径名表示的文件或目录。

      booleanequals(Object?obj)

      测试此抽象路径名与给定对象的相等性。

      booleanexists()

      测试此抽象路径名表示的文件或目录是否存在。

      FilegetAbsoluteFile()

      返回此抽象路径名的绝对形式。

      StringgetAbsolutePath()

      返回此抽象路径名的绝对路径名字符串。

      FilegetCanonicalFile()

      返回此抽象路径名的规范形式。

      StringgetCanonicalPath()

      返回此抽象路径名的规范路径名字符串。

      longgetFreeSpace()

      返回分区未分配的字节数 named此抽象路径名。

      StringgetName()

      返回由此抽象路径名表示的文件或目录的名称。

      StringgetParent()

      返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。

      FilegetParentFile()

      返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。

      StringgetPath()

      将此抽象路径名转换为路径名字符串。

      longgetTotalSpace()

      通过此抽象路径名返回分区 named的大小。

      longgetUsableSpace()

      返回上的分区提供给该虚拟机的字节数 named此抽象路径名。

      inthashCode()

      计算此抽象路径名的哈希码。

      booleanisAbsolute()

      测试这个抽象路径名是否是绝对的。

      booleanisDirectory()

      测试此抽象路径名表示的文件是否为目录。

      booleanisFile()

      测试此抽象路径名表示的文件是否为普通文件。

      booleanisHidden()

      测试此抽象路径名命名的文件是否为隐藏文件。

      longlastModified()

      返回此抽象路径名表示的文件上次修改的时间。

      longlength()

      返回由此抽象路径名表示的文件的长度。

      String[]list()

      返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。

      String[]list(FilenameFilter?filter)

      返回一个字符串数组,命名由此抽象路径名表示的目录中满足指定过滤器的文件和目录。

      File[]listFiles()

      返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。

      File[]listFiles(FileFilter?filter)

      返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。

      File[]listFiles(FilenameFilter?filter)

      返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。

      static File[]listRoots()

      列出可用的文件系统根。

      booleanmkdir()

      创建由此抽象路径名命名的目录。

      booleanmkdirs()

      创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。

      booleanrenameTo(File?dest)

      重命名由此抽象路径名表示的文件。

      booleansetExecutable(boolean?executable)

      为此抽象路径名设置所有者的执行权限的便利方法。

      booleansetExecutable(boolean?executable, boolean?ownerOnly)

      设置该抽象路径名的所有者或每个人的执行权限。

      booleansetLastModified(long?time)

      设置由此抽象路径名命名的文件或目录的最后修改时间。

      booleansetReadable(boolean?readable)

      一种方便的方法来设置所有者对此抽象路径名的读取权限。

      booleansetReadable(boolean?readable, boolean?ownerOnly)

      设置此抽象路径名的所有者或每个人的读取权限。

      booleansetReadOnly()

      标记由此抽象路径名命名的文件或目录,以便只允许读取操作。

      booleansetWritable(boolean?writable)

      一种方便的方法来设置所有者对此抽象路径名的写入权限。

      booleansetWritable(boolean?writable, boolean?ownerOnly)

      设置此抽象路径名的所有者或每个人的写入权限。

      PathtoPath()

      返回从此抽象路径构造的java.nio.file.Path对象。

      StringtoString()

      返回此抽象路径名的路径名字符串。

      URItoURI()

      构造一个表示此抽象路径名的 file: URI。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-19 08:05:55  更:2021-09-19 08:08:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:02:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码