1.布局优化
常用的几种方式分别是:避免布局多层嵌套(可用工具hierarchyViewer帮助分析布局的嵌套关系)、过度绘制检查、viewStub(相对gone invisiable消耗资源更小 显示的时候调用 viewStub.inflate ) 、Android lint工具(Analyze—>Inspect Code)
2.绘制优化
onDraw方法中避免创建新对象 同时不要做耗时操作
3.内存泄漏优化
1.静态变量导致的内存泄漏
比如private static Context scontext; scontext在oncreate里面被引用
2.单例模式导致内存泄漏
3.属性动画导致内存泄漏
在activity中播放属性动画 ObjectAnimator animator=ObjectAnimator.ofFloat(mbutton,"rotation",0,360)
.setDuration(200); animator.start(); 但是没有在Activity的onDestory()中释放,即使ui上看不到了效果,但是Activity会被动画持有,最终activity无法释放,需要在onDestory方法中调用 animator.cancel()来停止动画
4.Handler 内存泄漏及解决方案?
在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,解决方案使用静态内部类,并有activity的弱应用
5.数据库的cusor没有关闭
6.bitmap的recycle没有关闭
7.BaseAdapter的getview的contentview没有合理复用
8.getview中没有用viewHolder
9.registerReceiver之后没有unRegisterReceiver
4.响应速度优化和ANR日志分析
进程发生ANR之后会在系统的/data/anr目录下有一个traces.txt的文件用来分析和查看ANR的具体原因
5.Listview的优化
1.首先不要在getview中做耗时操作
2.使用ContentView 复用view对象
if (null == convertView) {
//如果convertView为空,则表示第一次显示该条目,需要创建一个view
view = View.inflate(MainActivity.this, R.layout.listview_item,
null);
} else {
//否则表示可以复用convertView
view = convertView;
}
3.使用ViewHolder类,定义静态的控件变量,避免每一次创建新的控件对象,减少内存的消耗
4.listview滑动的时候避免加载图片,可以监听 onScrollListener 还有onScrollStateChanged ,然后判断列表是否是滑动状态,在getview中设置标识只有停止的时候才会加载图片
5.listview实在还有卡顿的问题,可以考虑开启硬件加速,设置android:hardwareAccelerated="true"
6.分页加载
6.bitmap的优化,防止内存溢出
1.主要思想是通过BitmapFactory.Options 来对图片进行采样,然后设置inSampleSize来减少控件实际加载图片的大小,避免内存溢出。其中涉及采样图片的实际大小,通过设置inJustDecodeBounds设置为true ,这个时候BitmapFactory只有解析图片的宽和高,并不会真正的去加载图片,BitmapFactory.decodeFile(path, opts);
获取到实际大小之后,再根据控件的大小分析出 inSampleSize的大小,最后设置inJustDecodeBounds为false 然后通过BitmapFactory.decodeStream() 来获取bitmap值。
2.回收内存 bitmap.recycle(); 在activity或者fragment的onDestory()方法中
3.不必要的时候避免加载完整图片,使用inJustDecodeBounds为true 然后通过BitmapFactory.decodefile来获取图片的大小信息
4.优化虚拟机的堆内存分配,内存分配不是固定不动的,可以通过配置自动调整,比如在oncreate方法中添加VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
5.避免使用setImageBitmap setImageResourse 来加载图片尽量使用BitmapFactory.decodeStream来加载,因为decodeStream是直接调用JNI的方法来加载图片,但是setImageBitmap setImageResourse是需要create bitmap, 无需java层的空间。
7.线程优化
线程优化是建议使用线程池,避免创建多个线程,线程池可以减少线程的创建还可以有效的控制线程的最大并非,以及线程只带导致的问题
ExculuterService newFixedThreadPool 固定数量的线程池 CachedThreadPool线程数量不一定的线程
ScheduledThreadPool SingleThreadExecutor 只有一个核心线程
8.使用内存缓存和磁盘缓存 比如Lrucache 和DiskLrucache
9.使用mat工具ddms分析 内存溢出
本人使用的工具是 Android studio
先下载 Memory Analyzer (jdk如果是64则下64的,保持一致)
-
在Android Studio打开Android Device Monitor or DDMS. (打开ddms:tools--android--android device monitor)
-
选中"com.example.etc.."
-
在DDMS上面的菜单中,选中Update Heap .
-
在右边的面板中, select the Heap tab.
-
Click in Cause GC.
-
在DDMS上面的菜单中选中Dump HPROF file .
-
这个时候我们打开 之前下载的.Memory Analyzer ,点击File--open---open heap dump 打开上面保存的 hprof文件,会提示Unknown HPROF Version 错误
-
Unknown HPROF Version 错误是android studio和eclipse虚拟机不一样大导致文件格式不一样,需要通过sdk自带的 hprof-conv 在命令行来转换下文件,hprof-conv 源文件 目标文件
-
再用Memory Analyzer 点击File-open-open heap dump来打开转换后的文件
10.AsyncTask的缺陷:
1.生命周期
关于AsyncTask存在一个这样广泛的误解,很多人认为一个在Activity中的AsyncTask会随着Activity的销毁而销毁。然后事实并非如此。AsyncTask会一直执行doInBackground()方法直到方法执行结束。一旦上述方法结束,会依据情况进行不同的操作。
•如果cancel(boolean)调用了,则执行onCancelled(Result)方法
•如果cancel(boolean)没有调用,则执行onPostExecute(Result)方法
AsyncTask的cancel方法需要一个布尔值的参数,参数名为mayInterruptIfRunning,意思是如果正在执行是否可以打断,如果这个值设置为true,表示这个任务可以被打断,否则,正在执行的程序会继续执行直到完成。如果在doInBackground()方法中有一个循环操作,我们应该在循环中使用isCancelled()来判断,如果返回为true,我们应该避免执行后续无用的循环操作。总之,我们使用AsyncTask需要确保AsyncTask正确地取消。
2.不好好工作的cancel()
简而言之的答案,有时候起作用。
如果你调用了AsyncTask的cancel(false),doInBackground()仍然会执行到方法结束,只是不会去调用onPostExecute()方法。但是实际上这是让应用程序执行了没有意义的操作。那么是不是我们调用cancel(true)前面的问题就能解决呢?并非如此。如果mayInterruptIfRunning设置为true,会使任务尽早结束,但是如果的doInBackground()有不可打断的方法会失效,比如这个BitmapFactory.decodeStream() IO操作。但是你可以提前关闭IO流并捕获这样操作抛出的异常。但是这样会使得cancel()方法没有任何意义。
3.内存泄露
还有一种常见的情况就是,在Activity中使用非静态匿名内部AsyncTask类,由于Java内部类的特点,AsyncTask内部类会持有外部类的隐式引用。由于AsyncTask的生命周期可能比Activity的长,当Activity进行销毁AsyncTask还在执行时,由于AsyncTask持有Activity的引用,导致Activity对象无法回收,进而产生内存泄露。
4.结果丢失
另一个问题就是在屏幕旋转等造成Activity重新创建时AsyncTask数据丢失的问题。当Activity销毁并重新创建后,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。导致onPostExecute()没有任何作用。
5.串行还是并行
AnsycTask执行任务时,内部会创建一个进程作用域的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask.execute()后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad。对于内部的线程池不同版本的Android的实现方式是不一样的:
3.0之前规定同一时刻能够运行的线程数为5个,线程池总大小为128。也就是说当我们启动了10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,当我们尝试去添加第129个任务时,程序就会崩溃。
因此在3.0版本中AsyncTask的改动还是挺大的,在3.0之前的AsyncTask可以同时有5个任务在执行,而3.0之后的AsyncTask同时只能有1个任务在执行,只能串行。为什么升级之后可以同时执行的任务数反而变少了呢?这是因为更新后的AsyncTask已变得更加灵活,如果不想使用默认的线程池,还可以自由地进行配置,同样可以并行。比如使用如下的代码来启动任务:
这样就可以使用我们自定义的一个Executor来执行任务,而不是使用SerialExecutor。上述代码的效果允许在同一时刻有15个任务正在执行,并且最多能够存储200个任务。
下面的两个任务时同时执行呢,还是AsyncTask1执行结束之后,AsyncTask2才能执行呢?实际上是结果依据API不同而不同。
关于AsyncTask时串行还是并行有很多疑问,这很正常,因为它经过多次的修改。如果你并不明白什么时串行还是并行,可以通过接下来的例子了解,假设我们在一个方法体里面有如下两行代码:在1.6(Donut)之前:
在第一版的AsyncTask,任务是串行调度。一个任务执行完成另一个才能执行。由于串行执行任务,使用多个AsyncTask可能会带来有些问题。所以这并不是一个很好的处理异步(尤其是需要将结果作用于UI试图)操作的方法。从1.6到2.3(Gingerbread)
后来Android团队决定让AsyncTask并行来解决1.6之前引起的问题,这个问题是解决了,新的问题又出现了。很多开发者实际上依赖于顺序执行的行为。于是很多并发的问题蜂拥而至。3.0(Honeycomb)到现在
好吧,开发者可能并不喜欢让AsyncTask并行,于是Android团队又把AsyncTask改成了串行。当然这一次的修改并没有完全禁止AsyncTask并行。你可以通过设置executeOnExecutor(Executor)来实现多个AsyncTask并行。关于API文档的描述如下
If we want to make sure we have control over the execution, whether it will run serially or parallel, we can check at runtime with this code to make sure it runs parallel
6.真的需要AsyncTask么
并非如此,使用AsyncTask虽然可以以简短的代码实现异步操作,但是正如本文提到的,你需要让AsyncTask正常工作的话,需要注意很多条条框框。推荐的一种进行异步操作的技术就是使用Loaders。这个方法从Android 3.0 (Honeycomb)开始引入,在android支持包中也有包含。可以通过查看官方的文档来详细了解Loaders。
11、提升我们的UI 体验
现在大家越来越注重性能问题,其实没必要那么在乎,但是既然大家在乎了,这里通过Cyril Mottier :master_android_drawables ppt中的一个例子来说明如果利用Drawable来提升我们的UI的性能。
大家看这样一个效果图:
布局文件:
可以看到最外层是FrameLayout仅仅是为了设置背景图和padding,这样的布局相信很多人也写过。
再看看这个布局作为APP启动时,用户的直观效果:
用户首先看到一个白板,然后显示出我们的页面。接下来,我们将利用Drawable改善我们的UI性能以及用户体验。
1、首先,我们去除我们最外层的FrameLayout,然后自定义一个drawable的xml,叫做logo.xml
ok,这个drawable是设置了我们的背景和logo;
2、将其作为我们当前Activity的windowBackground
3、设置到Activity上:
Ok,这样不仅最小化了我们的layout,现在我们的layout里面只有一个LinearLayout和两个按钮;并且提升了用户体验,现在用户的直观效果时:
是不是体验好很多,个人很喜欢这个例子~~