性能优化目的:
| 优化目的 | 性能指标 | 优化的方向 |
|---|---|---|
| 更快 | 流畅性 | 启动速度 页面显示速度(显示和切换) 响应速度更稳定 |
| 更稳定 | 稳定性 | 避免出现 应用崩溃(Crash) 避免出现 应用无响应(ANR) |
| 更省 | 资源节省性 | 内存大小 安装包大小 耗电量 网络流量 |
AS的Profiler提供了CPU:卡顿优化、MEMORY:内存优化、NETWORK:网络、ENERGY:电量。
1 数据结构优化
Android中常用的数据结构有ArrayList、LinkedList、HashMap、SparseArray。
其中ArrayList查找快,增删慢,LinkedList增删快,查找慢。
HashMap使用数组+链表的结构,解决了rrayList增删慢、LinkedList查找慢的问题,单但是由于加载因子的存在,会造成内存浪费。
SparseArray是基于HashMap + 二分查找的思想实现的双数组结构,解决了HashMap内存浪费的问题。
根据应用场景,能使用SparseArray的时候尽量使用SparseArray。
2 OOM与内存优化
JVM 内存分布模型:
Java对象的声明周期:
2.1 Android内存三大问题
-
内存抖动
频繁创建大量、临时的小对象,会使程序频繁的分配内存,GC频繁回收内存,导致内存抖动。引起内存碎片化,app卡顿。
-
内存泄漏
内存泄漏是指本该回收的对象,因为某些原因不能被回收,从而继续停留在内存中,导致实际可使用的内存变小。引起app卡顿,耗电量增加。
根本原因是持有者的什么周期>被引用者的生命周期。
-
内存溢出 即OOM,OOM时会导致程序异常。Android设备出厂以后,java虚拟机对单个应用的最大内存分配就确定下来了,超出这个值就会OOM
OOM分类:
2.2 内存分析命令
dumpsys meminfo适用场景: 查看进程的oom adj,或者dalvik/native等区域内存情况,或者某个进程或apk的内存情况,功能非常强大;procrank适用场景: 查看进程的VSS/RSS/PSS/USS各个内存指标;cat /proc/meminfo适用场景: 查看系统的详尽内存信息,包含内核情况;free适用场景: 只查看系统的可用内存;showmap适用场景: 查看进程的虚拟地址空间的内存分配情况;vmstat适用场景: 周期性地打印出进程运行队列、系统切换、CPU时间占比等情况;
2.3 常见分析工具
-
Memory Analyzer Tools
incoming references:哪些对象持有该对象
outgoing references:持有哪些对象
Shallow Heap:浅堆 是指一个对象所消耗的内存
Retained Heap:深堆 一个对象被GC回收后,可以真实释放的内存大小
-
Memory Profiler
-
LeakCanary 内存泄漏检测工具,用于检测潜在的内存泄漏问题。
检测过程分两步:
- 通过RefWatch找出有泄露嫌疑的对象。
- haha库利用可达性分析,确定是否有内存泄漏。
原理:
使用LeakCanary只需添加依赖库,不用做其他任何工作。 LeakCanary初始化利用了ContentProvider,ContentProvider.onCreate方法会比Application.onCreate方法更早的执行,因此无需用户手动在 onCrate() 中进行初始化。ContentProvider在leakCanary中注册leakcanary.internal.LeakSentryInstaller。
LeakCanary初始化时,注册了ActivityLifecycleCallbacks,并重写了onActivityDestroyed,监听到 onDestroy() 之后,通过 refWatcher.watch 自动监测 Activity/Fragment。
其他需要LeakCanary的对象需要主动调用watch。
LeakCanary检测内存泄漏流程:
2.4 APP保活
可以在Activity中重写onTrimMemory、onLowMemory,在内存不足和低内存时,主动释放内存,保证APP不被杀死。
2.4 Android内存泄漏常见场景以及解决方案
1、资源性对象未关闭
对于资源性对象不再使用时,应该立即调用它的close()函数,将其关闭,然后再置为null。例如Bitmap等资源未关闭会造成内存泄漏,此时我们应该在Activity销毁时及时关闭。
2、注册对象未注销
例如BraodcastReceiver、EventBus未注销造成的内存泄漏,我们应该在Activity销毁时及时注销。
3、类的静态变量持有大数据对象
尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。
4、单例造成的内存泄漏
优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后,在使用到的地方从弱引用中获取Context,如果获取不到,则直接return即可。
5、非静态内部类的静态实例
该实例的生命周期和应用一样长,这就导致该静态实例一直持有该Activity的引用,Activity的内存资源不能正常回收。此时,我们可以将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,尽量使用Application Context,如果需要使用Activity Context,就记得用完后置空让GC可以回收,否则还是会内存泄漏。
6、Handler临时性内存泄漏
Message发出之后存储在MessageQueue中,在Message中存在一个target,它是Handler的一个引用,Message在Queue中存在的时间过长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。并且消息队列是在一个Looper线程中不断地轮询处理消息,当这个Activity退出时,消息队列中还有未处理的消息或者正在处理的消息,并且消息队列中的Message持有Handler实例的引用,Handler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。解决方案如下所示:
- 使用一个静态Handler内部类,然后对Handler持有的对象(一般是Activity)使用弱引用,这样在回收时,也可以回收Handler持有的对象。
- 在Activity的Destroy或者Stop时,应该移除消息队列中的消息,避免Looper线程的消息队列中有待处理的消息需要处理。
需要注意的是,AsyncTask内部也是Handler机制,同样存在内存泄漏风险,但其一般是临时性的。对于类似AsyncTask或是线程造成的内存泄漏,我们也可以将AsyncTask和Runnable类独立出来或者使用静态内部类。
7、容器中的对象没清理造成的内存泄漏
在退出程序之前,将集合里的东西clear,然后置为null,再退出程序
8、WebView
WebView都存在内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。我们可以为WebView开启一个独立的进程,使用AIDL与应用的主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,达到正常释放内存的目的。
9、Bitmap造成的内存泄漏
Android系统给每个应用分配的内存是有限的,而图片资源非常消耗内存(即Bitmap),对Bitmap的使用和内存管理稍有不慎就会引发内存溢出,最终导致OOM。
优化方向
-
使用完毕后释放资源
-
根据分辨率适配缩放图片、选择合适的解码方式
BitmapFacyory、Options提供了设置图片缩放比例和编码方式等接口,可更具实际需求做调整。
Bitmap默认解码方式为ARGB_8888。
- 设置图片缓存
3. 启动速度优化
3.1 启动流程
- 点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;
- system_server进程接收到请求后,向zygote进程发送创建进程的请求;
- Zygote进程fork出新的子进程,即App进程;
- App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
- system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
- App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
- 主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。
- 到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。
3.2 启动状态
-
冷启动
冷启动是指应用从头开始启动:系统进程在冷启动后才创建应用进程。发生冷启动的情况包括应用自设备启动后或系统终止应用后首次启动。
-
热启动
在热启动中,系统的所有工作就是将 Activity 带到前台。只要应用的所有 Activity 仍驻留在内存中,应用就不必重复执行对象初始化、布局加载和绘制。
-
温启动
用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行 onCreate() 从头开始重新创建 Activity。
系统将应用从内存中释放,然后用户又重新 启动它。进程和 Activity 需要重启,但传递到 onCreate() 的已保存的实例savedInstanceState对于完成此任务有一定助益。
3.3 启动耗时统计
- 系统日志统计
-
adb命令统计
adb shell am start -S -W [packageName]/[activityName]
-S:杀死应用,重新启动
APP启动时间:
- WaitTime:包括前一个应用Activity pause的时间和新应用启动的时间;
- ThisTime:表示一连串启动Activity的最后一个Activity的启动耗时;
- TotalTime:表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时。
3.4 CPU Profile
CPU Profile 记录启动过程中各方法的耗时时间。
按照下图选中Trace Java Methods,
点击profile app
直接使用AS的profile APP,启动app,启动过程会比较卡,并且Android8.0以下手机不支持,可通过以下方式使用,可使用以下方法获取启动过程信息(trace文件),根据具体业务进行分析:
- Application的构造方法中调用:Debug.startMethodTracing("test")、或Debug.startMethodTracingSampling;
- Launche Activity的onWindowFocusChanged中调用:Debug.stopMethodTracing();
3.5 卡顿分析
-
systrace掉帧卡顿分析
-
命令行 python systrace.py -t 5 -o F:\Lance\optimizer\lsn2_jank\a.html gfx input view am dalvik sched wm disk res -a com.enjoy.example
打开卡顿记录文件,分析frames,关注红点、黄点。
-
profile app
CPU-Trace System Calls
-
-
APP层面监控卡顿
-
Looper日志检测卡顿
利用UI线程的Looper打印的日志匹配;
BlockCanary:android中所有的方法都是在Looper.loop中执行的,loop循环中,handler.dispatchMessage前后,都会通过Printer打印信息,设置自定义的Printer,方法执行时间超过阈值,提示。可实现方法运行情况的检测。
-
使用Choreographer.FrameCallback。
Looper比较适合在发布前进行测试或者小范围灰度测试然后定位问题,ChoreographerHelper适合监控线上环境的 app 的掉帧情况来计算 app 在某些场景的流畅度然后有针对性的做性能优化。
-
3.6 布局优化
3.6.1 层级优化
3.6.1.1 layout Inspector
检查应用的视图层次结构。
减少布局层级,能减少布局mesure、layout的时间。
3.6.1.2 使用merge标签
用于替换FrameLayout或者当一个布局包含另外一个布局时,merge标签用于消除父子层次结构中多余的视图组。
使用merge标签的布局,inflater时,attachParent应为true,不需要显示调用addView。
3.6.1.3 使用ViewStub 标签
控件延迟加载,并且不会影响UI初始化的性能,在需要时通过inflate展示,但只能inflate一次。
- View.GONE:view设置gone时,view在layout布局文件中不占用位置,但是该view还是会创建对象,会被初始化,会占用资源。
- invisible:view设置为invisible时,view在layout布局文件中会占用位置,但是view为不可见,该view还是会创建对象,会被初始化,会占用资源。 用资源。
3.6.2 过度渲染
过度绘制是指系统在渲染单个帧的过程中多次在屏幕上绘制某一个像素。例如覆盖的布局,重复设置相同的背景。
3.6.2.1 GPU 过度绘制检查
- 进入开发者选项 (Developer Options)。
- 找到调试 GPU 过度绘制(Debug GPU overdraw)。
- 在弹出的对话框中,选择显示过度绘制区域(Show overdraw areas)。
Android 将按如下方式为界面元素着色,以确定过度绘制的次数:
3.6.2.1 解决过度绘制问题
- 移除布局中不需要的背景。
- 使视图层次结构扁平化。
- 降低透明度。
3.6.3 布局加载优化
-
异步加载
LayoutInflater加载xml布局的过程会在主线程使用IO读取XML布局文件进行XML解析,再根据解析结果利用反射创建布局中的View/ViewGroup对象。这个过程随着布局的复杂度上升,耗时自然也会随之增大。Android为我们提供了Asynclayoutinflater 把耗时的加载操作在异步线程中完成,最后把加载结果再回调给主线程。
- 使用异步 inflate,那么需要这个 layout 的 parent 的 generateLayoutParams 函数是线程安全的;
- 所有构建的 View 中必须不能创建 Handler 或者是调用 Looper.myLooper;(因为是在异步线程中加载的,异步线程默认没有调用 Looper.prepare );
- AsyncLayoutInflater 不支持设置 LayoutInflater.Factory 或者 LayoutInflater.Factory2;
- 不支持加载包含 Fragment 的 layout
- 如果 AsyncLayoutInflater 失败,那么会自动回退到UI线程来加载布局;
3.7 启动优化相关
- 合理的使用异步初始化、延迟初始化、懒加载机制。
- 启动过程避免耗时操作,如数据库 I/O操作不要放在主线程执行。
- 类加载优化:提前异步执行类加载。
- 合理使用IdleHandler进行延迟初始化(Handler空闲的时候执行)。
- 简化布局
- 启动黑白屏:launch Activity的theme中设置windowBackground,并且在onCreate的最开始重新设置Theme。
- 在debug时开启严苛模式,检查主线程IO操作、网络操作等。
实际例子:
Arouter gradle插件初始化
Eventbus apt
4 ANR问题分析
4.1 概念
ANR(Application Not responding),是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。
在 Android 里,应用程序的响应性是由 Activity Manager 和 WindowManager 系统服务监视的。当它监测到以下情况中的一个时,Android 就会针对特定的应用程序显示 ANR:
4.2 ANR类型
出现ANR的一般有以下几种类型:
-
KeyDispatchTimeout(常见)
input事件在
5S内没有处理完成发生了ANR。logcat日志关键字:
Input event dispatching timed out -
BroadcastTimeout
前台Broadcast:onReceiver在
10S内没有处理完成发生ANR。后台Broadcast:onReceiver在
60s内没有处理完成发生ANR。logcat日志关键字:
Timeout of broadcast BroadcastRecord -
ServiceTimeout
前台Service:
onCreate,onStart,onBind等生命周期在20s内没有处理完成发生ANR。后台Service:
onCreate,onStart,onBind等生命周期在200s内没有处理完成发生ANRlogcat日志关键字:
Timeout executing service -
ContentProviderTimeout
ContentProvider 在
10S内没有处理完成发生ANR。logcat日志关键字:timeout publishing content providers
-
Thread和HandlerThread
使用Thread和HandlerThread时, 为了使效果更好, 建议设置Thread的优先级偏低一点, 因为如果没有做任何优先级设置的话, 你创建的Thread默认和UI Thread是具有同样的优先级的 同样的优先级的Thread, CPU调度上还是可能会阻塞掉你的UI Thread, 导致ANR的
4.3 ANR怎么产生的
- 主线程频繁进行耗时的IO操作:如数据库读写
- 多线程操作的死锁,主线程被block;
- 主线程被Binder 对端block;
System Server中WatchDog出现ANR;service binder的连接达到上线无法和和System Server通信- 系统资源已耗尽(管道、CPU、IO)
4.4 ANR如何定位分析
- 分析log
- 从trace.txt文件查看调用strack
- 分析代码
- 仔细查看ANR成因
线上ANR监控方案:
- 继承FileObserver,监听/data/anr目录的变化。
- WatchDog
5 电量与网络优化
5.1 电量
减少操作、推迟操作、合并操作
5.2 网络
http流程:DNS解析——>获取IP——>建立socket连接——>发送http报文
5.2.1 DNS优化
HTTPDNS是面向多端应用(移动端APP,PC客户端应用)的域名解析服务,具有域名防劫持、精准调度、实时解析生效的特性。
HTTPDNS 几乎成为中大型 APP 的标配。解决了第一个问题, DNS 解析耗时的问题,顺便把DNS 劫持也解决了。
使用阿里HTTPDNS:
-
添加阿里HTTPDNS依赖。
-
创建自定义DNS解释,实现okhttp.Dns
AlibabaDns implements Dns
-
okhttp使用自定义的DNS OkHttpClient.Builder.dns(new AlibabaDns());
5.2.2 连接优化
HTTP 协议里有个 keep-alive,HTTP1.1默认开启,一定程度上缓解了每次请求都要进行TCP三 次握手建立连接的耗时。原理是请求完成后不立即释放连接,而是放入连接池中,若这时有另一个请求要发出,请求的域名和端口是一样的,就直接拿出连接池中的连接进行发送和接收数据,少了建立连接的耗时。 实际上现在无论是客户端还是浏览器都默认开启了keep-alive,对同个域名不会再有每发一个请求就进行一次建连的情况,纯短连接已经不存在了。
HTTP1.1不支持多路复用,即多个请求只能串行进行,并行多个请求,依然会创建多个长连接,HTTP2.0后,支持多路复用。
okhttp3已支持HTTP2,同时keep-alive需要服务器支持。
多路复用 多路复用把在连接里传输的数据都封装成一个个stream,每个stream都有标识,stream的发送和接收可以是乱序的,不依赖顺序,也就不会有阻塞的问题,接收端可以根据stream的标识去区分属于哪个请求,再进行数据拼接,得到最终数据。
5.2.3 数据压缩
-
protobuf 数据对请求速度的影响分两方面,一是压缩率,二是解压序列化反序列化的速度。目前最流行的两种数据格式是 json 和 protobuf,json 是字符串,protobuf 是二进制,即使用各种压缩算法压缩后,protobuf 仍会比 json 小,数据量上 protobuf 有优势,序列化速度 protobuf 也有一些优势 。
-
gzip压缩
以上需要与服务器配合。
5.3 其他
-
使用webp代替png/jpg。
-
不同网络的不同图片下发,如(对于原图是300x300的图片):
- 2/3G使用低清晰度图片:使用100X100的图片;
- 4G再判断信号强度为强则使用使用300X300的图片,为中等则使用200x200,信号弱则使用100x100图片;
- WiFi网络:直接下发300X300的图片
-
http开启缓存 / 关键数据加入缓存。
6 APK瘦身
6.1 APK的结构
APK 文件就是一个Zip格式的文件,其中包含构成应用的所有文件。这些文件包括 Java 类文件、资源文件和包含已编译资源的文件。
APK 包含以下目录:
- META-INF/:包含 CERT.SF 和 CERT.RSA 签名文件,以及 MANIFEST.MF 清单文件。
- assets/:包含应用的资源;应用可以使用 AssetManager 对象检索这些资源。
- res/:包含未编译到 resources.arsc 中的资源(图片、音视频等)。
- lib/:包含特定于处理器软件层的已编译代码。此目录包含每种平台类型的子目录,如 armeabi、armeabi-v7a、arm64-v8a、x86、x86_64 和 mips。
APK 还包含以下文件。在这些文件中,只有 AndroidManifest.xml 是必需的。
- resources.arsc:包含已编译的资源。此文件包含 res/values/ 文件夹的所有配置中的 XML 内容。打包工具会提取此 XML 内容,将其编译为二进制文件形式,并压缩内容。此内容包括语言字符串和样式,以及未直接包含在 resources.arsc 文件中的内容(例如布局文件和图片)的路径。
- classes.dex:包含以 Dalvik/ART 虚拟机可理解的 DEX 文件格式编译的类。
- AndroidManifest.xml:包含核心 Android 清单文件。此文件列出了应用的名称、版本、访问权限和引用的库文件。该文件使用 Android 的二进制 XML 格式。
6.2 Android Size Analyzer
Android Size Analyzer工具可轻松地发现和实施多种缩减应用大小的策略。例如将图片转化成webp等。
6.2 混淆、移除未使用资源
6.2.1 启用资源缩减(不打包)
如果在应用的 build.gradle 文件中启用了资源缩减: shrinkResources ,则 Gradle 在打包APK时可以自动忽略未使用资源。 资源缩减只有在与代码缩减: minifyEnabled 配合使用时才能发挥作用。在代码缩减器移除所有不使用的代码后,资源缩减器便可确定应用仍要使用的资源 。
minifyEnabled true
shrinkResources true
6.2.2 使用Lint分析器(物理删除)
lint 工具是 Android Studio 中附带的静态代码分析器,可检测到 res/ 文件夹中未被代码引用的资源。从菜单栏中依次选择 Analyze > Run Inspection By Name
6.2.3 自定义要保留的资源
如果有想要特别声明需要保留或舍弃的特定资源,在项目中创建一个包含 标记的 XML 文件,并在 tools:keep 属性中指定每个要保留的资源,在 tools:discard 属性中指定每个要舍弃的资源。这两个属性都接受以逗号分隔的资源名称列表。还可以将星号字符用作通配符。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />
6.2.4 移除未使用的备用资源(语言包资源)
Gradle 资源缩减器只会移除未由应用代码引用的资源,这意味着,它不会移除用于不同设备配置的备用资源。可以使用 Android Gradle 插件的 resConfigs 属性移除应用不需要的备用资源文件。
例如,如果使用的是包含语言资源的库(如 AppCompat ),那么 APK 中将包含这些库中所有已翻译语言的字符串。如果只想保留应用正式支持的语言,则可以使用 resConfig 属性指定这些语言。系统会移除未指定语言的所有资源。
android {
defaultConfig {
...
resConfigs "zh-rCN"
}
}
配置resConfigs 只打包默认与简体中文资源。
6.2.5 动态库打包配置
目前市面上手机设备绝大多数都是arm架构,因此armv7a几乎能兼容所有设备。大多数应用只会打包armv7a的so在Apk中。对于第三方服务,如百度地图、Bugly等会提供全平台的cpu架构。因此我们可以进行如下配置,指定只打包armv7a到apk,从而减少apk大小。
android{
defaultConfig{
ndk{
abiFilters "armeabi-v7a"
}
}
}
对于arm64架构的设备,如果使用armv7a也能够兼容,但是不如使用arm64的so性能。随着现在arm64架构设备渐渐成为主导。因此现在部分应用市场会根据设备提供不同架构的Apk安装。此时我们需要打包出针对arm64的apk与armv7a的apk,可以使用productFlavor也可以使用APK分包:splits。
flavorDimensions "default"
productFlavors {
arm32 {
dimension "default"
ndk {
abiFilters "armeabi-v7a"
}
}
arm64 {
dimension "default"
ndk {
abiFilters "arm64-v8a"
}
}
}
也可以使用: splits { abi { enable true reset() include 'arm64-v8a','armeabi-v7a' // exclude 'armeabi' universalApk true //是否打包一个包含所有so的apk } }
6.2 使用矢量图
矢量图可以创建与分辨率无关的图标和其他可伸缩媒体。使用这些图形可以极大地减少 APK 占用的空间。 矢量图片在 Android 中以 VectorDrawable 对象的形式表示。借助 VectorDrawable 对象,100 字节的文件可以生成与屏幕大小相同的清晰图片。
系统渲染每个 VectorDrawable 对象需要花费大量时间,而较大的图片则需要更长的时间才能显示在屏幕上。因此,建议仅在显示小图片时使用这些矢量图。
Tint着色器:重复使用资源
Tint与颜色选择器可以实现点击效果。
6.3 其他
- 主动移除无用代码(开启R8/Progurad自动移除);
- 不常用功能模块使用插件化加载;开启资源混淆:github.com/shwenzhang/…
- 支付宝删除Dex debugItem juejin.im/post/684490… ;
7 Crash监控
7.1 Java Crash
Java的Crash监控非常简单,Java中的Thread定义了一个接口: UncaughtExceptionHandler ;用于处理未捕获的异常导致线程的终止(注意:catch了的是捕获不到的),当我们的应用crash的时候,就会走UncaughtExceptionHandler.uncaughtException ,在该方法中可以获取到异常的信息,我们通过Thread.setDefaultUncaughtExceptionHandler 该方法来设置线程的默认异常处理器,我们可以将异常信息保存到本地或者是上传到服务器,方便我们快速的定位问题。
7.2 NDK Crash
Google breakpad是一个跨平台的崩溃转储和分析框架和工具集合,其开源地址是:github.com/google/brea… breakpad在Linux中的实现就是借助了Linux信号捕获机制实现的。因为其实现为C++,因此在Android中使用,必须借助NDK工具。
采集到的Crash信息记录在minidump文件中。minidump是由微软开发的用于崩溃上传的文件格式。我们可以将此文件上传到服务器完成上报,但是此文件没有可读性可言,要将文件解析为可读的崩溃堆栈需要按照breakpad文档编译minidump_stackwalk 工具,而Windows系统编译个人不会。不过好在,无论你是 Mac、windows还是ubuntu在 Android Studio 的安装目录下的bin\lldb\bin 里面就存在一个对应平台的minidump_stackwalk 。