一、UI布局优化
1.启动优化
冷启动分为三个步骤:创建进程、启动应用、绘制界面
2.创建进程分为三个步骤:
加载startingWindow、创建应用进程、启动App
3.启动应用分为三个步骤:
创建Application、创建主线程、创建MainActivity
4.绘制界面分为两个步骤:
加载布局、图像绘制
5.统计启动时间的方法:
a. TraceCompat.beginSection();
TraceCompat.endSection();
wall time : 是代码执行完花费的时间(包含cpu和IO的读写操作)
cpu time : 是cpu花费多少时间执行完这段代码(这里才是作为主要的优化时间点)
执行systrace.py这个参数,就可以把你加的日志生成一个html文件进行查看,它也可以底层的执行时间
b. 使用AOP切面编程:代码无侵略性
c. AS的profiler这个工具也可以查看耗时
6.UI常见问题以及优化
常见问题 : 过度绘制、布局复杂、层级过深
解决办法: 减少从xml文件创建为对象、减少过度绘制操作
1).xml文件上解决过度绘制 : 使用android:backgroud会增加一次过度绘制次数
2).自定义view过度绘制:
clipRect : 识别可见区域绘制,非可见部分就不进行绘制,俗称裁剪。注意:在调用cilpRect、scale(旋转)、translate(平移)、缩放等这些操作之前,需要调用canvas.save方法
3).可以采用ViewStub的方式去占位布局,使用时再去渲染
4).查看代码性能问题:AS->Analyze->Inspect Code 查看关键字Performance、Xml文件
5).常见的异步操作的手段
AsyncTask :UI线程和工作线程之间快速切换提供的简单机制
适用场景:立即需要启动,但是异步执行的生命周期短暂
HanderThread : 为某些回调方法或者等待某些任务执行设置一个专属线程
ThreadPool : 把任务分解到各个不同的线程上,进行同时并发处理
IntentService : 适合执行由UI触发的后台service,可以把后台执行任务反馈给UI(适合在Application 的onCrate里面适用,操作简单,且自己可以销毁对象,其他方式小题大做)
6).ListView性能优化
a.复用converView
b.复用viewHolder
c.getView少做耗时的操作
d.滑动停止时候再去加载图片
e.使用异步线程加载图片
滑动时候为什么不会发生OOM的原因(RecycleBin的复用机制)
RecycleBin里面有两个存数据的数组,一个是ActivtyView数组,这个是给用户展示的view,另一个是ScrapView数组,它是不需要展示view,但是已经创建,为了二次展示作为复用来使用。展示的view会先从ScrapView数组去遍历查找是否存在,如果存在直接在复用给ActivityView来使用,如果不存在创建之后给ActivityView来展示。
7.Systrace去检查UI卡顿问题
在android-sdk/platform-tools/systrace/执行systrace.py
执行python systrace.py --time=10 -o mynewtrace.html 命令生成.html文件
W:界面放大
S:界面缩小
A:界面左移
D:界面右移
二、内存优化
1.OOM容易混淆的概念
内存溢出:我们申请的内存已经超出虚拟机的最大内存的限度就会抛出Out Of memory
内存抖动 : 短时间大量的对象被创建然后就释放,触发的GC机制,严重占用内存区。
内存泄露 : 我们要回收的对象无法进行GC的回收操作
造成OOM的原因:
Java堆内存溢出、无足够的连续空间、虚拟内存不足、线程数量超出限制
2.内存泄漏的常见案例
1).单例:长生命周期的对象(Application的对象)被短生命周期的对象持有
2). handler : 非静态内部类持有外部类的引用,导致于无法释放外部类的对象---->解决方法将handler至于为static的对象,然后将外部类改为弱引用来使用
3).线程引起: AsyncTask和Runnable使用匿名内部类,因为非静态内部类持有外部类的引用,和handler类似
4).文件的读写、数据库网络启用后没有关闭、三方的框架没有销毁
5).注册广播以及使用三方的库没有再销毁时候关闭
3.不同内存的类型
内存的类别如下:
Java:从 Java 或 Kotlin 代码分配的对象的内存。
Native:从 C 或 C++ 代码分配的对象的内存。
Graphics:图形缓冲区队列向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。
Stack:您的应用中的原生堆栈和 Java 堆栈使用的内存
Code:您的应用用于处理代码和资源(如 dex 字节码、经过优化或编译的 dex 代码、.so 库和字体)的内存。
Others:您的应用使用的系统不确定如何分类的内存。
4.内存优化方向
1).BitMap的图片优化,避免滥用Bitmap导致内存的浪费,使用完及时回收
2).减少对象重复创建,多采用复用方式,避免内存碎片化
3).包体的优化 : 混淆、删除多余文件和图片、压缩图片、语言和cpu架构的配置
4).使用适当的数据结构去存储数据
5.内存优化之垃圾对象的处理
FinalizerReference就会越来越大原因:
一旦用户创建对象(重写了finalize函数)的速度快于finalize队列移除各元素的速度,finalize里面回收的是弱引用和虚引用,虚引用主要记录对象的销毁,也因为JVM的垃圾回收、内存管理都是守护线程,他们优先级比用户线程低很多。设置守护线程方法是setDaemon(true)的方法。
解决FinalizerReference过大的办法:
1.不要重写finalize()方法,实在要释放资源,到destroy一类函数中处理
2.重复利用资源,可以采用对象池方式处理,防止发生内存抖动,避免反复创建
3.调用System.runFinalization()回收
System.gc()和System.runFinalization()区别:
System.gc():告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的
System.runFinalization():强制调用已经失去引用的对象的finalize方法
6.内存优化之碎片化
解决方式:
共享元设计模式或对象池去处理 : 创建一个读对象之后就不会再创建了,只是里面的数据会被清空,对象不会被销毁
对象池线程不安全的
Pools.SimplePool<Integer> pool = new Pools.SimplePool(10);
对象池线程安全的
Pools.SynchronizedPool<Integer> pool1 = new Pools.SynchronizedPool<>(10);
java内存的大小计算 : shallow size、retained size
shallow size : 自身占用的内存的大小
retained size :该对象能直接或间接访问到对象的shallow size之和
案例讲解:
从obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以左图的obj3不是蓝色节点;而在右图却是蓝色,因为它已经被包含在retained集合内。
对于左图,obj1的retained size是obj1、obj2、obj4的shallow size总和;右图的retained size是obj1、obj2、obj3、obj4的shallow size总和。
对于obj2,它的retained size是:在左图中,是obj2和obj4的shallow size的和;在右图中,是obj2、obj3和obj4的shallow size的和。
Linux内存管理:(做为了解,后续再补充)
优质的网址:https://www.kancloud.cn/zhangyi8928/kernel/531016
VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存),用处不大
RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存),用处不大
PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存),它是观察内存泄漏的最好参数
7.内存优化之图片优化
1). 一张图片加载到内存的计算
加载分辨率为1080*452的图片,文件本身大小是56kb
1080*452*4B = 1952640B -----> 1.86MB 这样计算会有问题
正确计算方式:
1080*452*4B*(设备的dpi(240)/目录的dpi(1.5))-------->这个就可以被放在对应目录下的正确的大小
2).BitMap的几种像素格式
ALPHA_8:每个像素都需要1(8位)个字节的内存,只存储位图的透明度,没有颜色信息
ARGB_4444:A(Alpha)占4位的精度,R(Red)占4位的精度,G(Green)占4位的精度,B(Blue)占4位的精度,加起来一共是16位的精度,折合是2个字节,也就是一个像素占两个字节的内存,同时存储位图的透明度和颜色信息。不过由于该精度的位图质量较差,官方不推荐使用
ARGB_8888:这个类型的跟ARGB_4444的原理是一样的,只是A,R,G,B各占8个位的精度,所以一个像素占4个字节的内存。由于该类型的位图质量较好,官方特别推荐使用。但是,如果一个480*800的位图设置了此类型,那个它占用的内存空间是:480*800*4/(1024*1024)=1.5M
RGB_565:同理,R占5位精度,G占6位精度,B占5位精度,一共是16位精度,折合两个字节。这里注意的时,这个类型存储的只是颜色信息,没有透明度信息
3).图片优化的方式 : 降低分辨率、减少每个像素点大小
如何去加载一个大图的方式:根本原因是降低图片的像素
图片的优化行为:
质量压缩 : 可以降低图片质量,不能降低图片的像素,不能起到图片优化效果
采样率压缩 : 可以降低图片的像素options.inSample 这个参数,起到图片优化效果
尺寸压缩 : 通过压缩图片宽、高来降低图片的像素,起到图片优化效果
改变图片的格式,从RGBA_8888(默认)转变为其他格式,这种不推荐使用
4). 图像的开辟和销毁
图像开辟:
图片需要缩放,都是开辟在native层的,如果不需要缩放,8.0以前是在java层开辟的
8.0以下放在 java的内存之中
8.0以上是放在native内存之中,有2G内存供使用
图片销毁:
bitMap.recycle() : 回收是像素数据,只是在8.0以及以上可以使用
6.0以上是通过监听GC触发机制去传递给底层使用,然后进行销毁,监听方式是ReferenceQueue队列去监听
5).BitMap的BitmapFactory.Options参数讲解
Scale(转换率) = inTargetDensity / inDensity
BitmapFactory.Options options =new BitmapFactory.Options();
//会根据drawable文件夹的分辨率来赋值
options.inDensity
//会根据屏幕的像素密度来赋值
options.inTargetDensity
//对图片进行压缩,对图片宽和高各压缩2倍
options.inSampleSize = 2
//设置图片是否会被压缩,它的参数就是由Scale(转换率)去计算
options.inScaled
//为true不会得到bitMap的对象,而是可以的高宽、高的信息
options.inJustDecodeBounds
//代表资源图片的的宽
options.outWidth;
//代表资源图片的的高
options.outHeight
//代表BitMap可以复用
options.inMutable
//设置图片
options.inBitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background, options);
8.内存优化之检测系统接口
OnLowMemory:在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory
OnTrimMemory:系统会根据不同的内存状态来回调。系统提供的回调有: Application/Activity/Fragement/Service/ContentProvider
TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理
TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。
TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。
TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。
TRIM_MEMORY_RUNNING_CRITICAL:内存不足(后台进程不足3个),并且该进程优先级比较高,需要清理内存
TRIM_MEMORY_RUNNING_LOW:内存不足(后台进程不足5个),并且该进程优先级比较高,需要清理内存
TRIM_MEMORY_RUNNING_MODERATE:内存不足(后台进程超过5个),并且该进程
OnLowMemory和OnTrimMemory的比较
a.OnLowMemory被回调时,已经没有后台进程;而onTrimMemory被回调时,还有后台进程。
b.OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。
c.通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次。
三、性能优化
冷启动优化方式:异步(IntentService)、懒加载(Handler处理)、延迟(IdleHandler,cpu空闲时间加载数据)
1.异步方式:
a.Thread : 不容易复用,频繁创建及开销大
b.HandlerThread : 自带消息循环,适用于长时间运行,不断从队列中获取任务
c.IntentService : 继承Service在内部创建一个HandlerThread ,异步,不占用主线程,优先级较高,不容易被系统kill
d. AsyncTask : 无需处理线程切换,使用简单
onPreExecute():异步任务开启之前回调,在主线程中执行
doInBackground():执行异步任务,在线程池中执行
onProgressUpdate():在主线程中执行
onPostExecute():在异步任务执行之后回调,在主线程中执行
onCancelled():在异步任务被取消时回调
e.线程池:容易复用,减少频繁创建、销毁时间, 定时、任务队列、并发数控制
2.设置优先级
Process.setThreadPriority() --------->这个可以设置优先级的高低
3. 采用IntentService启动。它是在异步线程中执行的,用完后它会自动释放对象的,它继承Serice也不容易被杀死。IntentService = Handler+HandlerThread,它的执行是有顺序的
4.BaseClassLoader里面有个Element的数组,里面就是所有的.dex的文件,它需要通过dexElement.findClass遍历整个数组,找到对应的.class才能启动,把我们要先启动的.class放在最前面,这样就可以加快启动速度。
5.SharedPreference的函数讲解
1).apply和commit的区别
apply是没有返回值的,在往磁盘写入时候,它是开启子线程不需要等待返回结果
commit是会返回boolean的,主线程一直等往磁盘写入成功后,才会去执行
2).SharedPreference是存储在xml文件中,它线程安全性。
有缓存机制原因,高并发情况袭多进程是不安全安全
3).fileobserver多进程安全的原因
startWatching和stopWatching里面都加了锁,并且锁的对象是修改的文件路径
四、ANR的产生和解决办法
1、卡顿原理
产生原因:主线程做耗时操作就会产生卡顿,卡顿超过阈值,触发ANR。
卡顿产生因素:UI层级嵌套过深、Handler处理消息太耗时(barrier--> msg.target == null,同步屏障消息)
2、卡顿监控
处理方式:Gradle Plugin+ASM,目前微信的Matrix 使用的卡顿监控方案就是字节码插桩
3、ANR 原理
1)、触发ANR的场景
输入事件分发超时5s,包括按键和触摸事件
比如前台广播在10s内未执行完成
servive后台在20s内没有完成
2)、servive、broadcast原理实现
a)、Service的onCreate方法被调用时候调用mHandler.sendMessageDelayed发送延迟时间来处理事情
b)、在任务完成前会调用 :mHandler.removeMessages把消息移除,这样剧不会产生ANR
c)、任务没有前就会调用mAppErrors.appNotResponding,来告诉产生的原因,弹ANR Dialog
3)、input原理实现
input来说即便某次事件执行时间超过timeout时长,只要用户后续在没有再生成输入事件,则不会触发ANR。
4、 ANR检测工具
一般来说有四种,分别为BlockCanay、ANR-WatchDog、SafeLooper和FileObserver
ANRWatchDog
原理: 开启一个子线程mThreadRunnable,每隔1s会执行一次mThreadRunnable,单独现场向主线程发送一个变量+1操作,休眠过后判断变量是否+1完成,如果未完成则警告
FileObserver
Android手机发生ANR后,会把信息存储在/data/anr/traces.txt文件,我们只需要监听这个文件的变化就可以知道是否发生了ANR。
五、APP瘦身优化
1.删:删除无用的代码和资源
2.移:实在不行就抽离出,动态加载,动态下发。
3.压:压缩代码和资源
buildTypes {
release {
// 源代码混淆开启
minifyEnabled true
// 启动资源压缩
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
4. 图片优化 : 除了用PNG格式外,图片可以使用SVG(缩图),在gradle需要配置
defaultConfig {
........
// 使用support-v7兼容5.0以上
vectorDrawables.useSupportLibrary = true
.........
}
SVG官方介绍:https://developer.android.google.cn/studio/write/vector-asset-studio#about
5. 资源配置打包优化
defaultConfig {
........
// 只保留指定和默认资源
resConfigs('zh-rCN', 'ko')
// 配置so库架构(真机:arm,模拟器:x86)
abiFilters('armeabi',armeabi-v7a')
.........
}
-----------------------------下面以后还需要再好好总结-----------------------------------------------------
RecycleView的优化之回收机制
1.一级优化,优先优化item的列表,不需要重新绑定ViewHodler
里面有两个ArryList的链表,一个是mAttachScrap,另一个是mChangedScrap,当调用notifyItemChanged方法,通知有新的item时候,之前显示保存在mAttachScrap里面会直接复用ViewHolder,数据不需要重新绑定,新加入放在mChangedScrap里面再数据绑定
2.二级和四级优化,Item列表优化缓存,mCacheView可以保存划出列表的item,调用setItemCacheSize方法可以确定缓存多少数据,超出这个数据会放在四级优化的recyclerPool这个缓存池里
OnLowMemory()是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory
一般来说有四种,分别为BlockCanay、ANR-WatchDog、SafeLooper和FileObserver
网友评论