阅读本文预计需要8分钟
时常有同学来问我这个错误怎么解决,那个错误怎么解决,所以才写这篇文章来阐述一下Android的一些错误日志如何去分析,有经验的工程师其实都知道,错误大同小异,根据步骤来解决即可,不过时下不比以前,现在的错误五花大门倒也是真的,有Android本身的错误,Gradle的编译错误,ANR的错误,以及一些so底层的错误,我们一个个来吧。
第一步:找到错误,这个相信不用去讲了,要是你连错误都找不到,我...我 .....
好吧,给你一个参照物
这是最普通的Exception了,这个错误是在Android Studio的底部有一个logcat菜单中查找,我们再来看下另外的错误吧,比如Gradle编译的错误,其实Gradle编译的错误很多,这里列举一个:
Gradle编译的错误在Android Studio的底部有一个build菜单中查找,分为两类,左边是错误发生的位置,此类错误还比较好,会告诉你发生的错误位置,有些错误就不会告诉你了,右边是具体的错误问题解释。
接下来还有ANR的错误,ANR的错误辨识度很高,因为屏幕上会弹出 应用已停止运行 的提示框。
好了,接下来我们就开始一个个教大家如何分析吧,我们主要分为Android运行时的错误,以及Gradle编译的错误,还有ANR的解决,至于so库的问题就不讲解了,开始吧。
一.AndroidRuntime
我们还是拿刚才的这个错误来对比:
这张错误的截图其实只是一部分,一看蓝色部分就知道他指向的是我们MainActivity的19行,那么如果不知道如何定位的同学如何去定位呢?那就要通过我们的AndroidRuntime了,实际上他的自身含义就是Android运行时发生的错误,我们来看下完整截图:
当错误发生的时候,首先AndroidRuntime会打印一个 ,说明应用已经挂掉了,然后会打印 来开始搜集我们的crash,再接着AndroidRuntime就会开始打印他认为的错误原因,最后在 中就是具体的问题了,如果他能追踪到行数,就会为你标记为蓝色可执行文本,而我们看到这个错误就是我们日常所见的一个很普通的错误: 空指针异常,那么我们定位到了这个异常的类型,就好办了,先来认识下一些常见的异常吧:
这张图中我们看到我列举了三个,当然,异常肯定有很多我就以这三个为例,给大家抛砖引玉。
1.NullPointerException
首先我们回到上面的错误,他既然是一个空指针异常,那么肯定是有一个对象为空的,我们来看下他对应的MainActivity第19行具体的代码:
这里是一个控件设置点击事件,那么既然我们怀疑有对象为空,那么我们来看这一行实际上就一个mBtn是一个引用对象,而OnclickListener是当场New出来的,那么mBtn我们来看,我们怀疑他是空对象,那么就得回忆下,如何初始化一个控件对象了,在Android中我们初始化一个控件对象是通过findViewById来实现的,到这你就会发现,自己没有findViewById来初始化,导致了空指针,例子很简单,一看就明白,但是要理解解题的思路。
2.ActivityNotFoundException
那么我们继续来看下其他错误吧,比如ClassNotFoundException,顾名思义,找不到类报的异常,我们先来看下他的Log:
这个错误是否符合我们之前分析的几个要素呢?实际上大部分都是有的,你可以看到Shutting down VM的打印,也可以看到 FATAL EXCEPTION ,他的错误是ActivityNotFoundException,也就是找不到Activity的异常,和我们所说的ClassNotFoundException不一样,但是列表差不多,我们以此来分析问题,第一眼看过去,我们看到他提示了这么一段话:
have you declared this activity in your
AndroidManifest.xml?
//您是否在AndroidManifest.xml中声明了此活动?
带着这个问题我们来到了他所指向的MainActivity第24行看到这样一段代码:
这段代码中指向的是一个startActivity,也就是我们想跳转到HelloActivity中,但是失败了,提示我们是否在清单文件中注册?这就很好理解了,我们的Activity需要在清单文件中注册才能跳转,而现在报了这个异常,说明我们没有注册,去注册下就好了。
接下来再看我们最后一个数组越界的异常吧。
3.ArrayIndexOutOfBoundsException
先来看下数组越界的异常打印的Log是什么:
这段错误,我们可以这样去分析,首先找到我们的几个关键点:
1.错误的解释:
java.lang.IndexOutOfBoundsException: Index: 8, Size: 5
2.错误的异常:IndexOutOfBoundsException
3.错误的定位:onCreate(MainActivity.java:38)
有了这三点就再简单不过了,我们可以确定是一个数组越界的异常,而给到我们的解释是:Index: 8, Size: 5 ,这要如何解释?下标:8 数量:5 ,这就需要结合实际的代码来看了,我们定位到的行数是MainActivity的38行,来看下:
这段代码中我们声明了一个list并且使用for循环遍历了5遍填充数据,然后打印他的第8个值,其中出现的数值就是5和8,这也验证了我们刚才的错误提示:Index: 8, Size: 5
也就是说,你的size只有5,但是你获取的index下标确是8,我肯定是没有的,就提示了这个异常了
所以你会发现其实这些错误,大部分出现的问题都是能顺藤摸瓜的,只要抓到问题,分析问题基本都有思路,不过AndroidRuntime的错误毕竟很多人都会解决,那么我们来看下,编译的时候的问题吧。
二.Gradle 编译问题
Gradle编译的问题很多,有一小部分还是很让人头疼的,我们一步步来,先解决Gradle版本的问题、
1.Gradle版本
你是否碰到过项目突然无法编译,出现一些奇奇怪怪的错误,或者说你去Github上Clone了一个项目打算导入Android Studio却死活无法运行,这些大部分都是Gradle版本的问题,如果你无法在线下载Gradle的话就需要本地离线去下载了。
这里你可以先打开Android Studio
File > Settings > Appearance & Behavior > System Settings > HTTP Proxy
再配置这个地址与端口:
127.0.0.1 1080
看看有没有效果,如果无效的话就自己手动下载吧。
先打开地址:
http://services.gradle.org/distributions/
选择你要下载的版本下载好之后,打开:
C:\Users\Administrator\.gradle\wrapper\dists\gradle-5.4.1-all\3221gyojl5jsh0helicew7rwx
dists目录下面是你具体的版本号,我的是5.4.1-all
然后把你下载的zip放入这个目录解压出来,重启AS即可。
2.Jar冲突
Jar冲突可以这样理解:
这张图中,如果一个APP引用了两个开源框架,A 和 B,然后他们内部又同时依赖了OkHttp,而且还有一种可能就是他们引用的OkHttp版本都不同,这就出现了Jar的冲突了,那么如何解决呢?
使用exclude标签来解决这个问题:
假设冲突的是zxing,那么通过exclude去除zxing,这样就可以解决了,如果你告诉我你不知道哪两个框架冲突了,这里可以告诉你一些办法:
1.gradlew -q app:dependencies 会列出所有的依赖关系
2.定位到冲突的类,双击Shift去查找
我一般是第二种,比如我知道OkHttp冲突了(可以从Gradle的Log中看到),然后去查到了
还有很多的错误,还是需要自己去挖掘和分析的,我们接着来看ANR吧。
三.ANR
应用无法响应,可以是死循环,线程休眠,也可能是某个机制超时,比如触摸超时,服务,广播,内容提供者等超时,他们都会弹出一个提示框
我们可以在logcat中看到这样的信息:
可以看到他与我们的AndroidRuntime很不一样,不过可以从中得到一段很关键的话:
Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 6. Wait queue head age: 6315.7ms.)
//输入超时
如果你有经验的话,你就能猜到是卡UI线程了,等待一定时间超时引起的,那么我们如何追踪呢?可以分析traces
1.
traces文件就是保存ANR信息的log,位于 data/anr 目录下,记录着堆栈的信息,可以通过如下命令获取
adb root
adeb remount
adb pull /data/anr/traces.txt c:\
这样就拉取到C盘根目录了,不过部分手机是没有权限的,所以我们也可以借助一些Carsh的SDK来帮助我们获取问题,比如Bugly
四.Bugly
官网:https://bugly.qq.com/v2/
集成起来还是很方便的,添加源:
implementation 'com.tencent.bugly:crashreport:2.2.0'
implementation 'com.tencent.bugly:nativecrashreport:3.0'
别忘了设置支持的架构:
再添加一些权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
最后在Application中初始化一下
CrashReport.initCrashReport(getApplicationContext(), "注册时申请的APPID", true);
initCrashReport方法中最后一个参数是一个debug开关,声明是否需要打印bugly的日志,建议debug的时候打开,release的时候关闭
然后就可以在应用后台中看到相关数据和Log了,不管是线上线下,都能提前清楚问题,尽快修复。
这就是本文的全部内容了
写这篇文章呢,其实没多少技术在其中,更多的是一种科普,让同学们对解决问题的思路有一个了解,也好提高自己的开发效率
有兴趣的可以加入我的知识星球哦,只要点击阅读原文即可哦~
关于知识星球的介绍可以在公众号点击右下角的【和我学习】或者公众号发送【Hi Android】【知识星球】都可以