Framework/性能优化

三、Framework

1.handler

handler机制
UI线程创建时,就创建了一个Looper,Looper内部维护这一个MessageQueue。Looper通过开启一个while(true)死循环来轮询MessageQueue中的Message。当Looper轮询到Message时,就分发此Message。Handler在子线程发送消息到MessageQueue,Message被Looper取出来后,分发给handler的handleMessage方法来处理。

handler内存泄漏怎么处理
在Activity内将Handler声明成非静态内部类或者匿名内部类,这样Handle默认持有外部类Activity的引用。如果Activity在销毁时,Handler还有未执行完或者正在执行的Message,而Handler又持有Activity的引用,导致GC无法回收Activity,导致内存泄漏。
解决方案:静态内部类 + 弱引用

Loop死循环会不会把主线程卡死???
不会被卡死。 主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
造成ANR的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI。

一个线程能否创建多个Handler,Handler跟Looper之间的对应关系
一个Thread只能有一个Looper,一个MessageQueen,可以有多个Handler

可以在子线程直接new一个Handler吗?怎么做?
不可以,因为在主线程中,Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。所以要在子线程开启Handler要先创建Looper,并开启Looper.prepare()循环

一个线程有几个looper以及怎么保证,怎么初始化
一个线程中只能有一个Looper,通过ThreadLocal来保证
Looper.prepare();
Looper.loop();

ThreadLocal的原理
ThreadLocal是一个关于创建线程局部变量的类,在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例。 承载一些线程相关的数据,避免在方法中来回传递参数。
在每个Thread中包含一个ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的对象,value是独享数据。
使用场景如下所示:
实现单个线程单例以及单个线程上下文信息存储,比如交易id等。
实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例。 承载一些线程相关的数据,避免在方法中来回传递参数。

多个Handler同时发消息,怎么辨别是谁
message.what用来识别不同的消息,多个handler发送消息时通过message.what加以区分

App的启动流程
①点击桌面App图标,Launcher进程采用Binder IPC向AMS进程发起startActivity请求;
②AMS接收到请求后,向zygote进程发送创建进程的请求;
③Zygote进程fork出新的子进程,即App进程;
④App进程,通过Binder IPC向AMS进程发起attachApplication请求;
⑤AMS进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
⑦主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。
⑧到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

四、性能优化

内存泄漏的本质,你是怎么排查的,内存泄露的场景以及解决方法,怎么优化。
内存泄漏(Memory Leak)是指某些对象已经不再使用了,但却无法被垃圾回收器回收内存,还一直占用着内存空间的现象,这就导致这一块内存泄露了。本质就是生命周期短的对象被生命周期长的对象引用了,导致生命周期短的对象无法及时被垃圾回收器回收。
工具:AndroidStudio Memory-profiler/LeakCanary
防止内存泄漏:
(1)资源性对象未关闭(例如Bitmap、IO)对于资源性对象不再使用时,应该立即将其关闭
(2)注册对象未注销(例如BraodcastReceiver、EventBus)应该在Activity销毁时及时注销。
(3)类的静态变量持有大数据对象。尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。
(4)单例、非静态内部类、Handler造成的内存泄漏,优先使用Application的Context,如需使用Activity的Context,考虑使用静态内部类和弱引用。
(5)WebView可能存在内存泄漏的问题。为WebView开启一个独立的进程,使用AIDL与应用的主进程进行通信,选择合适的时机进行销毁
防止内存抖动:
(1)避免频繁创建对象,如:避免在for语句中创建大量对象
(2)需要频繁使用的对象,可以通过缓存池复用,避免重复创建、释放
字符串相加或者拼接通过StringBuilder替代,较少创建String对象节省内存
使用SpareArray、ArrayMap替代HashMap
图片优化
使用Glide框架

常用优化
性能优化目的
流畅性
1.启动速度
对于APP启动来说,启动耗时包括Android系统启动
APP进程加上APP启动界面的耗时时长,我们可做的优化是APP启动界面的耗时,也就是说从Application的构建到
主界面的 onWindowFocusChanged 的这一段时间。
因此在这段时间内,我们的代码需要尽量避免耗时操作,检查的方向包括:主线程IO;第三方库初始化或程序需要
使用的数据等初始化改为异步加载/懒加载;减少布局复杂度与嵌套层级
工具:AndroidStudio CPU Profile

2.页面显示速度(显示和切换)
布局优化、避免过度绘制、onDraw方法中不要做耗时的任务、显示缓存子线程更新数据

3.响应速度
数据结构和算法:
ArrayList/LinkedList
HashMap的使用
SpareArray和ArrayMap
DiffUtil刷新RecyclerView
线程池

稳定性
1.避免出现 应用崩溃(Crash)
Java中的Thread定义了一个接口: UncaughtExceptionHandler ;用于处理未捕获的异常导致线程的终止(注意:catch了的是捕获不到的),当我们的应用crash的时候,就会走 UncaughtExceptionHandler.uncaughtException ,在该方法中可以获取到异常的信息,我们通过 Thread.setDefaultUncaughtExceptionHandler 该方法来设置线程的默认异常处理器,我们可以将异常信息保存到本地或者是上传到服务器,方便我们快速的定位问题。

2.避免出现 应用无响应(ANR)
ANR(Application Not responding),是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完 成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR Timeout时长
1、KeyDispatchTimeout(最常见类型)—— input事件5s内未处理完成导致ANR发生,主要为按键和触摸事件;
日志关键字:InputDispatching Timeout
2、BroadcastTimeout:—— BroadcastReceiver在特定时间内未处理完成导致ANR发生(限制:前台广播10s;后台广播60s);
日志关键字:Timeout of broadcast BroadcastRecord
3、ServiceTimeout —— Service在特定的时间内未处理完成导致ANR发生。(限制:前台服务20s;后台服务200s);
日志关键字:Timeout executing service
4、ContentProviderTimeout —— 内容提供者,在10s内未处理完成导致ANR发生;
日志关键字:Timeout publishing content providers
( 注意事项: Input的超时机制与其他的不同,对于input来说即便某次事件执行时间超过timeout时长,只要用 户后续在没有再生成输入事件,则不会触发ANR)

如何避免ANR发生

主线程尽量只做UI相关的操作,避免耗时操作,比如过度复杂的UI绘制,网络操作,文件IO操作;
BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。
将所有耗时操作,比如访问网络,Socket通信,查询大量sql 语句,复杂逻辑计算等都放在子线程中去

发生了ANR的情况,如何解决:
如果在已经尽量避免的情况下,还是发生了ANR的情况,我们可以根据以下的步骤进行问题定位与分析:
1、/data/anr/导出ANR日志信息,根据日志信息,判断确认发生ANR的包名类名,进程号,发生时间,导致ANR原因类型等。
2、关注系统资源信息,包括ANR发生前后的CPU,内存,IO等系统资源的使用情况。
3、查看主线程状态,关注主线程是否存在耗时、死锁、等锁等问题,判断该ANR是App导致还是系统导致的。
4、结合应用日志,代码或源码等,分析ANR问题发生前,应用是否有异常

资源节省性
内存大小
Android内存泄漏常见场景
1、资源性对象未关闭–例如cursor、IO、Bitmap等资源未关闭会造成内存泄漏
2、注册对象未注销–例如BraodcastReceiver、EventBus未注销造成的内存泄漏
3、单例、非静态内部类导致Activity的泄露–优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装。
4、内存抖动(频繁的分配和回收导致内存碎片,由于内存的不连续,可能无法分配导致OOM)–不要在onDraw等方法中创建对象

工具:AndroidStudio Memory-profiler/LeakCanary

安装包大小
1、移除未使用资源
2、启用资源缩减build.gradle 配置minifyEnabled、 shrinkResources ,在打包APK时混淆代码、自动忽略未使用资源。
3、图片使用webp格式
4、小图标使用矢量图(而较大的图片渲染需要大量时间)

工具: Android Studio lint 工具(可检测到 res/ 文件夹中未被代码引用的资源)/Analyze App Size(减应用大小的建议)

耗电量
1、减少操作:您的应用是否存在可删减的多余操作?例如,是否可以缓存已下载的数据,而不是每次重新下载
数据?
2、推迟操作:应用是否需要立即执行某项操作?例如,是否可以等到设备充电后或者Wifi连接时(通常情况下使
用移动网络要比WIFI更耗电 )再将数据备份到云端?
3、合并操作:工作是否可以批处理,而不是多次将设备置于活动状态?比如和服务器请求不同的接口获取数
据,部分接口是否可以合并为一个?

图片优化方案有哪些
(1)要选择合适的图片规格,通常我们优化Bitmap时,当需要做性能优化或者防止OOM,我们通常会使用RGB_565,因为ALPHA_8只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多。
ALPHA_8 每个像素占用1byte内存
ARGB_4444 每个像素占用2byte内存
ARGB_8888 每个像素占用4byte内存(默认)
RGB_565 每个像素占用2byte内存
(2)质量压缩 Bitmap.compress
(3)降低采样率 二次采样:BitmapFactory.Options 参数inSampleSize的使用,先把options.inJustDecodeBounds设为true,只是去读取图片的大小,在拿到图片的大小之后和要显示的大小做比较通过calculateInSampleSize()函数计算inSampleSize的具体值,得到值之后。options.inJustDecodeBounds设为false读图片资源。
(4)转码成Webp格式 Webp是谷歌提供的一种支持有损压缩和无损压缩的图片文件格式,而且可以提供比JPEG或PNG更好的压缩
(5)内存-文件-网络缓存
(6)Bitmap复用,即通过软引用(内存不够的时候才会回收掉),复用内存块,不需要再重新给这个bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。
(7)使用recycle()方法及时回收内存。

Android中如何实现加载大图
(1) 使用 BitmapRegionDecoder
BitmapRegionDecoder 是 Android 提供的一个类,可以用来加载大图并显示其中的一个区域。使用这种方法,可以避免一次性加载整张大图,从而降低内存占用。
需要注意的是,使用 BitmapRegionDecoder 加载大图需要在子线程中进行
(2) 使用 BitmapFactory.Options
可以通过 BitmapFactory.Options 中的 inSampleSize 属性来缩小图片的尺寸,从而减少内存占用。inSampleSize 的值应该是 2 的指数,表示缩小的倍数。需要注意的是,缩小图片的尺寸会降低图片的质量,需要根据实际需求选择合适的缩小倍数。
(3) 使用 Glide 或 Picasso 等第三方库
Glide 和 Picasso 等第三方库可以自动对加载的图片进行压缩和缩放,从而避免一次性加载整张大图。同时,这些库也提供了其他的图片加载功能,如图片缓存、图片旋转、图片裁剪等

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值