目录
ANR异常问题及解决方式OOM异常问题及解决方式BitMap相关问题UI卡顿问题内存泄露问题及解决方式内存管理冷启动优化问题其他优化介绍ANR异常问题及解决方式
什么是ANR
ANR全名Application Not Responding, 也就是”应用无响应”. 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.
造成ANR的主要原因
在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下两种情况下会弹出ANR对话框:
5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).BroadcastReceiver在10s内无法结束.
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等等.
那么Android中哪些操作是在主线程尼?
Activity的所有生命周期都是执行在主线程Service默认是执行在主线程BroadcastReceiver的onReceiver回调是执行在主线程的没有使用子线程的looper的Handler的handleMessage,post(Runnnable)是执行在主线程的。AsyncTask的回调中除了donInbackground,其他都在主线程中执行。
如何解决ANR
使用AsyncTask处理耗时操作使用Thread或者HandlerThread提高优先级使用handler来处理工作线程的耗时操作Activity的onCreate和onResume回调中尽量避免耗时的代码OOM异常问题及解决方式
什么是OOM?
当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出的Out of memory 异常。
一些容易混淆的概念
内存溢出:即是OOM
内存抖动:因为短时间内创建大量的对象,然后瞬间释放,这段时间占有的内存区域,会产生抖动。
内存泄露:产生内存垃圾,无法被GC回收。
如何解决OOM
产生OOM有以下方式:
1.数据库的cursor没有关闭。2.构造adapter没有使用缓存contentview。3.调用registerReceiver()后未调用unregisterReceiver().4.未关闭InputStream/OutputStream。5.Bitmap使用后未调用recycle(),控制图像缩放大小,使用inBitmap属性,三级缓存6.Context泄漏。7.static关键字等。8.避免在onDraw方法里执行对象的创建9.谨慎使用多进程
BitMap相关问题
1.recycle2.LRU3.计算inSampleSize,使用缩略图4.三级缓存5.软引用虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要 及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”, 还有就是, 虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试表明它并没能立即释放内存。故我们还需手动设置为NULL这样 还能大大的加速Bitmap的主要内存的释放。。
如下:
if(!bitmap.isRecycled()){bitmap.recycle()}
对于LRU 请移步我的另一篇博客 Android中LruCache案例及实现原理分析
有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码:
/*** Return the sample size.** @param options The options.* @param maxWidth The maximum width.* @param maxHeight The maximum height.* @return the sample size*/private static int calculateInSampleSize(final BitmapFactory.Options options,final int maxWidth,final int maxHeight) {int height = options.outHeight;int width = options.outWidth;int inSampleSize = 1;while ((width >>= 1) >= maxWidth && (height >>= 1) >= maxHeight) {inSampleSize <<= 1;}return inSampleSize;}private static boolean isEmptyBitmap(final Bitmap src) {return src == null || src.getWidth() == 0 || src.getHeight() == 0;}/*** Return the compressed bitmap using sample size.** @param src The source of bitmap.* @param maxWidth The maximum width.* @param maxHeight The maximum height.* @param recycle True to recycle the source of bitmap, false otherwise.* @return the compressed bitmap*/public static Bitmap compressBySampleSize(final Bitmap src,final int maxWidth,final int maxHeight,final boolean recycle) {if (isEmptyBitmap(src)) return null;BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;ByteArrayOutputStream baos = new ByteArrayOutputStream();press(pressFormat.JPEG, 100, baos);byte[] bytes = baos.toByteArray();BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);options.inJustDecodeBounds = false;if (recycle && !src.isRecycled()) src.recycle();return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);}
附上关于BitMap工具类下载地址
巧妙的运用软引用(SoftRefrence)
有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。如下例:
private class MyAdapter extends BaseAdapter {private ArrayList> mBitmapRefs = new ArrayList>(); public View getView(int i, View view, ViewGroup viewGroup) { View newView = null; if(view != null) { newView = view; } else { newView =(View)mInflater.inflate(R.layout.image_view, false); } Bitmap bitmap = BitmapFactory.decodeFile(mValues.get(i).fileName); mBitmapRefs.add(new SoftReference(bitmap));//此处加入ArrayList ((ImageView)newView).setImageBitmap(bitmap); return newView; } }
通过使用三级缓存请移步我的另一篇博客 Android中三级缓存实现原理及案例分析
UI卡顿问题
UI卡顿原理
不卡顿情况要60帧操作需要在16ms时间完成,如果完成不了,那么就会造成卡顿现象,渲染失败,还有一种情况是overDraw情况,一般是重叠View,非必要重叠背景
UI卡顿原因分析
1、人为在UI线程中做轻微耗时操作,导致UI线程卡顿
2、布局Layout过于复杂,无法在16ms内存完成渲染
3、同一时间动画执行的次数过多,导致CPU或GPU负载过重
4、View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重
5、View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染
6、内存频繁触发GC过多,导致暂时阻塞渲染操作
7、冗余资源及逻辑等导致加载和执行缓慢
8、ANR
UI卡顿总结
1、布局优化:不使用太多嵌套,使用weight 代替长和宽的计算,使用Gone代替invisible
2、列表及Adapter优化
3、背景和图片等内存分配优化
4、避免ANR
内存泄露问题及解决方式
java内存泄露基础知识
1、java内存的分配策略
寄存器:我们在程序中无法控制
栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
堆:存放用new产生的数据
静态域:存放在对象中用static定义的静态成员
常量池:存放常量
非RAM(随机存取存储器)存储:硬盘等永久存储空间
2、java是如何管理内存的
java管理内存实际上就是对象的生成与释放的管理,对象的生成是在堆中生成的,释放是靠GC回收机制,前者是程序员控制,后者靠java虚拟机控制,下面是一个案例:
3、java中的内存泄露
内存泄露是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄露
单例引起的内存泄漏
使与应用生命周期一样长,持有该对象的引用,
内存泄露问题:
public class AppManager{private static AppManager instance;private Context mcontext;private AppManager(){}private AppManager(Context context){mcontext=context;}private static AppManager getInstance(Context context){if(instance==null){instance=new AppManager(context);}return instance;}}
public class AppManager{private static AppManager instance;private Context mcontext;private AppManager(){}private AppManager(Context context){mcontext=context.getApplicationContext();//使用Application的context}private static AppManager getInstance(Context context){if(instance==null){instance=new AppManager(context);}return instance;}}
匿名内部类
非静态内部类持有外部类的引用,而使用非静态内部类创建一个静态实例。正确解决办法是将该类变为静态类
handler
原因:非静态内部类持有外部类的引用。
handler造成的内存泄漏解决办法:
private MyHandler mHandler = new MyHandler(this);private TextView mTextView;private static class MyHandler extends Handler{private WeekReference<Context> reference;public MyHandler(Context context){reference=new WeekReference<>(context);}@Overridepublic void handleMessage(Message msg){MainActivity activity=(MainActivity)reference.get();if(activity!=null){activity.mTextview.setText("");}}}
避免使用static变量资源未关闭造成的内存泄露。AsyncTask造成的内存泄露
原因:非静态内部类持有外部类引用;
解决办法:
在Activity的onDestory方法中调用AsyncTask的cancel方法。
4、内存优化方法
当Service完成任务后,尽量停止它;在UI不可见的时候,释放掉一些只有UI使用的资源;在系统内存紧张的时候,尽可能多的释放掉一些非重要的资源;避免滥用BitMap导致的内存浪费使用针对内存优化过的数据容器 SpareArray代替HashMap 枚举类型常量是一般常量类型占用内存的两倍避免使用依赖注入的框架使用ZIP对齐的APK使用多进程,比如通过开启进程 解决开启定位,开启推送,开启webview加载url
冷启动优化
冷启动的定义及与热启动的区别
冷启动:就是在启动应用前,系统中没有该应用的任何进程信息。会初始化Application类
热启动:用户使用返回键退出应用,然后马上又重新启动应用。不会初始化Application类
冷启动的时间计算:
这个时间从应用启动(创建进程)开始计算,到完成视图的第一次绘制(即Activity内容对用户可见)为止。
冷启动流程:
1、Zygote进程中fork创建出一个新的进程,
2、创建和初始化Application类、创建MainActivity类,inflate布局;
3、当onCreate/onStart/onResume方法走完,contentView的measure/layout/draw显示在桌面上
冷启动流程-总结:
Application的构造方法–>attachBaseContext()—>onCreate()–>Activity的构造方法–>onCreate()–>配置主题中背景等属性–>onStart()–>onResume()–>测量布局绘制显示在桌面上。
如何对冷启动的时间进行优化
1、减少onCreate()方法的工作量
2、不让Application参与业务的操作
3、不要在Application进行耗时操作
4、不要以静态变量的方式在Application中保存数据
5、布局/mainThread
其他优化:
android不要静态变量存储数据
1、静态变量等数据由于进程已经被杀死而被初始化
2、使用其他数据传输方式:文件/sp/contentProvider
有关Sharepreference问题
1、不能跨进程同步数据读写操作
2、存储Sharepreference的文件过大问题,可能引起ui卡顿,
内存对象序列化
1、实现Serializeble接口
2、Parcelable (不适用磁盘数据序列化)
总结:Serializeble是java的序列化方式,Parcelable是Android特有的序列化方式,在使用内存的时候,Parcelable比Serializable性能高
Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC操作,Parcelable不能使用在要将数据存贮在磁盘的情况
如果觉得《Android异常与性能优化相关问题及解决办法》对你有帮助,请点赞、收藏,并留下你的观点哦!