一、ANR(Application Not Responding)问题
1、什么是ANR(Application Not Responding)
ANR就是一个程序无响应的对话框
在一个Activity(Service)当中最长的执行时间是5秒,超出5秒无响应就会导致ANR
在一个BroadcastReceiver当中最长的执行时间是10秒,
原因:在主线程中做了耗时操作,所以才会导致ANR的弹框
2、造成ANR的主要原因
应用程序的响应性是由Activity Manager和WindowManager系统服务监视的,当它监测到Activity和BroadcastReceiver当中5秒、10秒没有执行完任务之后。安卓就会弹出ANR的对话框
- 主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞
- 主线程中存在耗时操作
3、造成ANR的主要原因-Android中哪些操作是在主线程呢?
- Activity的所有生命周期回调都是执行在主线程的
- Service默认是执行在主线程的
- BroadcastReceiver的onReceive回调是执行在主线程的
- 没有使用子线程的looper的Handler的handlerMessage,post(Runnable)是执行在主线程的
- AsyncTask的回调中除了doInBackground,其他都是执行在主线程
4、如何解决ANR
- 使用AsyncTask处理耗时IO操作
- 使用Thread或者HandlerThread提高优先级,不提高优先级 它的优先级和主线程的优先级是一样的仍然会造成ANR
- 使用handler来处理工作线程的耗时任务
- 在Activity的onCreate和onResume回调中尽量避免耗时的操作
二、OOM(Out of Memory)问题
1、什么是oom
当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出Out of memory异常,最常见的oom就是bitmap加载大图的时候。
2、一些容易混淆的概念
- 内存溢出:就是Out of memory
- 内存抖动:短时间内大量的对象被创建又被马上释放,瞬间产生的对象。会严重占用内存区域
- 内存泄露:进程中的某些对象,比如说垃圾对象。已经没有被其他对象引用到了,但是它们确可以直接或间接引用到GCRoots。导致GC无法直接产生作用,一旦内存泄露累积到一定程度,就会引起内存溢出。
3、如何解决oom
1、有关bitmap优化
- 图片的显示:比如ListView滑动的时候要显示缩略图不要去网络请求下载图片,只有监听到ListView停止滑动的时候再去加载网络大图
- 及时释放内存
- 图片压缩
- inBitmap属性
- 捕获异常
2、ListView
- ConverView/Lru机制缓存bitmap
- 避免在onDraw方法里面执行对象的创建,频繁的创建对象容易引起内存抖动
- 谨慎使用多进程
三、Bitmap相关问题
bitmap是存在native内存和Java内存当中的,当被回收的时候分两部分回收。一是回收Java内存当中的内存二是回收native内存当中的内存。
1、Recycle
recycle释放bitmap内存的时候,会释放和这个bitmap有关的native内存,同时会清理有关数据对象的引用。但不是立即清理,它会给垃圾回收器发送消息指令。让它在没有其他对象引用这个bitmap对象的时候,进行垃圾回收。
当bitmap调用recycle之后,bitmap会被标记为“dead”。这个时候你再调用bitmap的其他方法就会引起异常。比如getPixels()或者setPixels(),同时recycle操作是不可逆的。所以你要确定被recycle之后不再调用这个bitmap对象以及它的任何方法,否则就会引起异常。官方建议我们不主动调用recycle方法,当没有对象引用这个bitmap的时候垃圾回收器会主动的回收这个对象。
2、LRU算法
- lru算法是最近最少使用的对象,我们把它清除出缓存队列。
- LruCache是一个泛型类,lru算法内部使用了一个LinkedHashMap来实现的,并且提供了get和put方法来完成缓存的添加和获取操作,当缓存满的时候它内部提供了一个trimToSize()的方法。把较早和最少使用的的缓存对象移除并添加新的缓存对象。
3、计算inSampleSize
// 根据maxWidth, maxHeight计算最合适的inSampleSize
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// 图像的原始高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
4、缩略图
//缩略图
public static Bitmap thumbnail(String path,
int maxWidth, int maxHeight, boolean autoRotate) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// 获取这个图片的宽和高信息到options中, 此时返回bm为空
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false;
// 计算缩放比
int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inPurgeable = true;
options.inInputShareable = true;
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
bitmap = BitmapFactory.decodeFile(path, options);
return bitmap;
}
5、三级缓存
- 网络缓存
- 本地缓存
- 内存缓存
四、UI卡顿问题
1、UI卡顿原理
UI渲染系统做了太多的耗时操作,和执行了大量的动画。
60fps->16ms
过渡绘制:UI布局中有大量重叠的部分,多层次的UI结构中。
2.UI卡顿的原因分析
- 人为在UI线程中做轻微耗时操作,导致UI线程卡顿
- 布局Layout过于复杂,无法在16ms内完成渲染
- 同一时间动画执行的次数过多,导致CPU或GPU负载过重
- View过渡绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重
- View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染
- 内存频繁触发GC过多,导致暂时阻塞渲染操作
- 冗余资源及逻辑等导致加载和执行缓慢
- ANR
3、UI卡顿总结
- 布局优化:使用常见的include、merge、ViewStub标签。尽量不存在冗余嵌套或者复杂的布局
- 列表及Adapter优化
- 背景和图片等内存分配优化
- 避免ANR
五、内存泄露问题
1、java内存的分配策略
- 静态存储区(方法区)
存放静态数据、全局变量。程序编译的时候已经分配好了,在静态存储区存储的变量,在程序运行的整个期间都会存在 - 栈区
方法内的局部变量,会在栈上创建存储空间。并在方法结束后,这些在变量所持有的内存会被自动释放 - 堆区
又称动态内存分配,通常就是我们new对象出来的内存。这部分内存在不使用的时候会有java的内存回收器进行回收
2、java中的内存泄露
内存泄露是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄露。
六、Android内存管理机制
1、分配机制
操作系统会为每个进程分配一个合理大小的内存,从而保证每一个进程能够正常的运行。而不至于内存不够使用或者每个进程占用太多的内存
2、回收机制
在系统内存不够的时候,他会有一个合理的回收再分配机制从而保证新的进程能够正常的运行。
3、内存管理机制的特点
- 更少的占用内存
- 在合适的时候,合理的释放系统资源
- 在系统内存紧张的情况下,能释放大部分不重要的资源,来为Android系统提供可用的内存
- 能够很合理的在特殊生命周期,保存或者还原重要数据,以至于系统能够保证正确的重新恢复该应用
4、内存优化的方法
- 当Service完成任务后,尽量停止它。推荐使用Intentservice
- 在UI不可见的时候,释放掉一些只有UI使用的资源
- 在系统内存紧张的时候,尽可能多的释放掉一些非重要资源
- 避免滥用Bitmap导致的内存浪费
- 使用针对内存优化过的数据容器:SparseArray
- 避免使用依赖注入的框架
- 使用ZIP对齐的APK
- 使用多进程
七、冷启动优化
1、冷启动的定义
冷启动就是在启动应用前,系统中没有该应用的任何进程信息
2、冷启动和热启动的区别
热启动:用户使用返回键退出应用,然后马上又重新启动应用。热启动的应用的进程是保留在后台的
- 冷启动:每次启动的时候都会走Application这个类,
- 热启动:进程中保留留这个app的进行,它会直接走MainActivity这个类
3、冷启动的流程
- Zygote进程中fork创建出一个新的进程
- 创建和初始化Application类、创建MainActivity类inflate布局
- 当onCreate/onStart/onResume方法都走完
- contentView的measure/layout/draw显示在界面上
4、冷启动流程-总结
Application的构造方法->attachBaseContext()->onCreate()->Activity的构造方法->onCreate()->配置主题中背景等属性->onStart()->onResume()->测量布局绘制显示在界面上
5、 如何对冷启动的时间进行优化
- 减少onCreate()方法的工作量
- 不要让Application参与业务的操作
- 不要在Application进行耗时操作
- 不要以静态变量的方式在Application中保存数据
- 布局(减少布局的复杂性)、mainThread(资源的初始化放到子线程当中)
八、其他优化问题
1、Android不用静态变量储存数据
- 静态变量等数据由于进程已经被杀死而被从新初始化
- 使用其他数据传输方式:文件、SP、contentProvider,要对数据进行非空判断
2、有关SharePreference问题
- 不能跨进程同步
- 存储SharePreference的文件过大问题,文件过大获取值得时候,有可能阻塞主线程。解析很大的SharePreference文件的时候会产生大量的临时文件对象,导致垃圾回收机制频繁的进行垃圾回收。容易造成UI卡顿和内存抖动
3、内存对象的序列化
序列化:将对象的状态信息转换为可以储存或传输的形式的过程
- Serializeble:Serializeble在序列化的时候会产生大量的临时变量,从而引起频繁的垃圾回收。影响UI卡顿,容易引起内存抖动,造成oom
- Parcelable:不能使用存储在磁盘上的文件
4、 内存对象序列化-总结
- Serializeble是java的序列化方式,Parcelable是Android特有的序列化方式
- 在使用内存的时候,Parcelable比Serializeble性能高
- Serializeble在序列化的时候会产生大量的临时变量,从而引起频繁的GC
- Parcelable不能使用在要将数据存储在磁盘上的情况