目录
1、内存优化
2、ui优化
3、网络优化
4、启动优化
1、内存优化
1.1、解决所有的内存泄漏
1.1.1、内存泄漏:
堆上分配的对象已经不会再使用,但是GC收集器无法对其进行回收,此对象被强应用所引用 。
1.1.2、GC收集器原理:
可达性算法:从GCRoot对象为起点,向下搜索,可到达的对象是称为GC可达,GC收集器不会回收,不可到达的对象称为不GC不可达,是GC收集器回收的对象。
GCRoot对象:
(1)、虚拟机栈(栈帧找那个的局部变量表)中的对象;
(2)、方法区中类静态变量引用的对象;
(3)、方法区中常量引用的对象。
1.1.3、常见的内存泄漏实例
(1)、单例造成的内存泄漏
单例类的实例是静态的,如果需要其构造方法需要传递一个context,如果传入的是 Activity 的 Context,当传入 Activity 就会造成泄漏了。
解决方法:this.context = context.getApplicationContext();// 使用Application 的context。
(2)、内部类造成的内存泄漏
在Activity中有一个内部类,如果创建了这个内部类的静态对象,或者这个内部类被一个静态对象所引用,Activity关闭的时候,由于内部类会持有外部类的引用,内部类静态对象会持有外部类Activity的引用,导致Activity发生内存泄漏。
解决方法:将该内部类设为静态内部类,如果该内部类需要持有外部类的引用,则使用软引用/弱引用,在使用外部类的引用之前需进行空判断。
(3)、异步线程造成的内存泄漏
在Activity中有一个开启一个线程执行一个runnable,这个runnable是内部类就会持有外部类Activity的引用,如果线程的生命周期比Activity的生命周期长,就不会导致Activity内存泄漏。
解决方法:同上
(4)、Handler 造成的内存泄漏
在Activity中声明一个内部类handler,当使用这个handler发送一个延迟消息时,此消息执行前,Activity关闭会造成内存泄漏。
解决方法:同上
(5)资源未关闭造成内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
造成内存泄漏的总结:
造成内存泄漏的的本质是长生命周期的对象引用短生命中期的对象。
比如说
1、静态变量引用一个activity,最常见的就是单例模式造成的内存泄漏;
2、另外内部类也是造成内存泄漏的一个常见的点,内部类会持有外部类的引用,activity中声明一个内部类,如果这个内部类被声明成一个静态变量或者被一个静态变量持有,那么activity关闭的时候就会发生内存泄漏。
3、内部类造成的内存泄漏还有一些其他情况,比如说,activity中声明一个runnable或者handler的内部类,如果这个runnable的生命周期比activity长,或者handler发送的延时消息的生命周期比activity的生命周期长,那么就会造成内存泄漏。
4、另外我遇到一个比较特殊的是webview的内存泄漏,webview在attach的时候会(使用mcontext,对应activity)注册一个mComponentCallbacks的回调,(以便监听配置改变或者系统剩余内存较低),在Detach的时候会反注册,反注册的前提是webviwe没有destory,所有我们在调用webviwe的destory方法前要先将webview从其父容器移除。
(高版本在webviwe没有destory方法中会先检查onDetachedFromWindow方法是否执行,没有就先执行onDetachedFromWindow方法。)
Activity onPause -> onStop -> onDestroy ---->
View调用onDetachedFromWindow方法
public interface ComponentCallbacks {
void onConfigurationChanged(Configuration newConfig);
void onLowMemory();
}
5、另外资源未关闭也是造成内存泄漏的一个原因,比如说bitmap资源,steam资源等。
1.1.4、内存泄漏检测方案
LeakCanary原理:
LeakCanary在一个Fragment或者Activity onDestory的时候,创建一个弱引用
KeyedWeakReference
到要被监控的对象。然后在后台线程检查引用是否被清除,如果没有,调用GC。
如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个
.hprof
文件中。在另外一个进程中的
HeapAnalyzerService
有一个HeapAnalyzer
使用HAHA 解析这个文件。得益于唯一的 reference key,
HeapAnalyzer
找到KeyedWeakReference
,定位内存泄露。HeapAnalyzer
计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
简单版:
1、LeakCanary在一个Fragment或者Activity onDestory的时候,创建一个弱引用到要被监控的对象。
2、然后在后台线程检查引用是否被清除,如果没有,调用GC。
3、如果引用还是未被清除,把堆 内存 取出来,在另外一个进程中使用一个haha的库进行分析,确定是否内存泄漏,如果泄漏就建立泄漏对象到 GC roots 的最短强引用路径并输出。
1.2、避免内存抖动
内存抖动代表频繁GC,会占用程序执行时间,造成页面卡顿等性能问题。
1.2.1、避免内存抖动的方式:
(1)、避免在onDraw中创建Paint、Bitmap对象等;
(2)、避免在循环中创建临时对象;
(3)、避免在scrollListener中创建对象。
1.3、内存抖动检测方法
(1)、使用Android profiler 进行观察,如果发下内存剧增剧减则是GC时间,可查看GC时间段的内存,找出重复创建的大量对象,进行优化(例子:在recycleview中获取距离屏幕的距离时,创建大量对象)。
1.3、图片优化
1.3.1 一些结论
1,memorySize ≈ 分辨率* 每个像素需要的字节数
2,像素点大小就几种情况:ARGB_8888(4B)、RGB_565(2B) 等等。
3,jpg、png 只是图片的容器,图片文件本身的大小与它所占用的内存大小没有什么关系。
使用Tinypng深度压缩,使用web格式代替png、jpeg格式。都不能减少内存,只能缩减包大小或者提升网络传输速度,网络图片:使用网络裁剪服务,获取适当的图片加载。
4,图片来源是 res 内的不同资源目录时,系统会根据设备当前的 dpi 值以及资源目录所对应的 dpi 值,做一次分辨率转换,规则如下:新分辨率 = 原图横向分辨率 * (设备的 dpi / 目录对应的 dpi ) * 原图纵向分辨率 * (设备的 dpi / 目录对应的 dpi )。其他图片的来源,如磁盘,文件,流等,均按照原图的分辨率来进行计算图片的内存大小。
5,当使用 Glide 时,如果有设置图片显示的控件,那么会自动按照控件的大小,降低图片的分辨率加载。图片来源是 res 的分辨率转换规则对它也无效。
1.3.2 优化策略
(1)、降低分辨率
降低分辨率一般使用 Glide去加载图片就好了,它会按照控件大小降低图片分辨率。其内部也是使用了inSampleSize。
(2)降低图片像素点大小,没有透明通道,可使用RGB_565(2B)
(3)drawable适配
在做图片的兼容性时,如果只想使用一张图片,则应使用3倍甚至4倍的图片(3倍是主流机型,但在4倍手机上会被放大,图片可能失真),这样在低分辨率的手机上,不仅显示清晰,而且系统会自动进行缩放,从而确保占用较小的内存。
(4) 配合onTrimMemory或者onLowMemory,在系统内存紧张的时候使用glide清理图片缓存
1.3.2 inSampleSize
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 先将inJustDecodeBounds设置为true来获取图片的长宽属性
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 计算inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 加载压缩版图片
options.inJustDecodeBounds = false;
// 根据具体情况选择具体的解码方法
return BitmapFactory.decodeResource(res, resId, options);
}
1、定义一个BitmapFactory.Options的一个变量,然后把这个变量的inJustDecodeBounds属性设置为true,去加载图片,这个时候不会真正去加载图片,但是可以获取到图片的宽高;
2、然后结合我们需要的宽高,计算一个采样率;
3、最后将inJustDecodeBounds设置为false,按照采样率去真正的加载图片。
1.4、内存对象的重复利用
1)资源复用:通用的字符串、颜色定义、简单页面布局的复用。
2)视图复用:ListView/RecycleView替代scrollview。
3)Bitmap对象的复用,https://www.jianshu.com/p/45fbef7a58ba
4)使用memory profiler 去检测内存中是否可重复利用的对象;
(使用memory profiler 观察内存的使用情况,在内存上升和抖动的时间段可能会有大量重复对象的创建,可取出对应时间段的内存进行查找是否有大量重复对象的创建,并可结合代码分析是否可是重复利用这些对象)
1.5、使用优化的数据结构(内存和速度平衡)
1.6、线程池管理线程
1.7、多进程,比如拍摄模块
2、ui优化
2.1、分析布局,减少布局嵌套或者替换消耗性能少的布局(FrameLayout,constraintlayout)
2.2、使用include+merge减少布局层级
2.3、使用viewstub提供按需加载
2.4、复用系统自带的资源 winow background
3、网络优化
3.1、网络速度:
正常一条网络请求需要经过的流程是这样:
(1)、DNS 解析,请求DNS服务器,获取域名对应的 IP 地址;
(2)、与服务端建立连接,包括 tcp 三次握手,安全协议同步流程;
(3)、连接建立完成,发送和接收数据,解码数据。
这里有明显的三个优化点:
3.1、IP直连或者HTTPDNS;
3.2、开启 keep-alive进行连接复用;
3.3、减少请求和返回数据的大小;
(1)、请求和返回数据做Gzip压缩;
(2)、精简数据格式;
(3)、图片:使用webp图片格式代替png、jpeg;按需加载图片(图片裁剪,也可按网络状态返回不同分辨率的图片);
3.4、数据缓存(h5和na共享作品数据,视频缓存);
3.2、网络优化补充:
3.4、断点续传。
3.5、请求打包;(如埋点统计)
3.6、协议优化;(如使用优化的的更好的http2,请求头压缩)
对请求数据进行Gzip压缩
static class GzipRequestInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
return chain.proceed(originalRequest);
}
Request compressedRequest = originalRequest.newBuilder()
.header("Content-Encoding", "gzip")
.method(originalRequest.method(), gzip(originalRequest.body()))
.build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) {
return new RequestBody() {
@Override public MediaType contentType() {
return body.contentType();
}
@Override public long contentLength() {
return -1; // 无法知道压缩后的数据大小
}
@Override public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
}
4、启动优化
4.1、异步加载一:Application中加入异步线程
4.2、异步加载二:MainActivity中加入异步线程
4.3、 延迟加载功能:首屏绘制完成之后加载
4.4、动态加载布局:主布局文件优化
4.5、主布局文件深度优化
4.6、 功能代码深度优化
4.7、一个重点是天降红包(离线插件+数据预加载)
5.参考
1,https://juejin.cn/post/6901487965562732551
2,https://juejin.cn/post/6844903693230276616
3,https://www.jianshu.com/p/a9d861732445
4,https://juejin.cn/post/6844904093786308622
5,https://www.jianshu.com/p/9c897b41893d
6,启动优化,https://juejin.cn/post/6844904093786308622#heading-148
7,https://www.cnblogs.com/yongdaimi/p/11120590.html