性能优化有哪些?如何对android应用进行性能分析以及优化。
首先先想一下内存泄漏的问题:什么是内存泄漏,内存泄漏就是 对象在内存(heap)堆中分配的空间,
当不在使用或没有引用指向的情况下,仍然不能被GC(垃圾回收机制)正常回收的情况下。
多数出现在不合理的编码情况下,就会造成内存泄漏,比如说在Activity中注册了一个广播接受者,
当Activity关闭的时候进行unRegister(),就会出现内存溢出的现象,
一般情况大量的内存溢出就会造成OOM.
什么是OOM OOM就是OutOfMemoery,顾明思意,就是内存溢出,
内存溢出是值App向系统申请超过最大的阀值的内存请求,系统不会在分配多余的空间,
就会造成OOM,多数情况下是出现在图片处理加载不合适的时候。
内存的管理之道,无非就先理解并找出内存泄漏的原因,进而去防范内存开销过大,
如何合理的管理内存,最好先了解内存分配的机制和原理,只有深层次的理解了内部的原理,
才能真正的避免OOM的发生。
下面我们逐条来分析Android内存优化的关键点:
一 内存优化 :(内存优化的几种方案)
1. static
static是个好东西,声明赋值调用非常简单好用,但是还有性能的问题要解决,
原因是static声明的变量的生命周期是和App的生命周期是一样的有点类似Application。
如果大量的使用就会占据内存不放,极少称多 也会OOM的。所以大家要合理的使用static,
一般用来修饰基本数据类型或者轻量级的对象,尽量避免大对象和修复集合,工具类,内部类等。
静态变量存在方法区,它的生命周期是从类的加载开始,到整个进程的结束。
一单静态变量初始化后,它说持有的引用只有等到进程结束才会释放。
作为Activity的静态成员,并且持有Activity的引用,但是作为静态变量。
生命周期坑定比Activity的生命周期厂,当Activity推出,
仍然引用了Activity,Activity不能被收回,这就导致了内存泄漏。
怎么避免那,因为很多时候都是因为生命周期比一直而导致内存泄漏,
说一我们在新建静态持有的变量的时候就要想一下他们之间的关系,
尽可能的少使用静态持有的变量,达到避免发生内存泄漏。
还有可以在适当的时候将静态变量重新赋值为null,不再持有引用,这样就可以避免内存泄漏。
内部类也可以导致内存泄漏。
内部类(包括匿名内部类) 默认就会持有外部类的引用,
内部类的生命周期坑定比外部类的生命周期长就会导致内存泄漏。
内部类导致内存泄漏在android 开发中有一种典型的场景就是使用Handler
mHandler会作为成员变量保存在发送的消息msg中,
即msg持有mHandler的引用而mHandler是Activity,
2. 谨慎使用Handler
在使用Handler+Thread的时候,大家都会遇到警告的情形,这就是Lint为开发者的提醒。
handler运行在UI线程,不断处理来自MessageQueue的消息,如果Handler还有消息要处理,
但是Activity页面已经关闭的请狂下,Activity的引用其实并不会被回收,这就造成了内存的泄漏。
解决:在Activity的onDestroy(方法)中调用handler.removeCallBacksAndMessages(null);
取消所有的消息处理,包括待处理的消息,二是生命handler的内部类为static静态的。
3. Bitmapd 使用
BitMap的使用不当,是最容易造成OOM的,很多时候就是因为这个原因。
由于Dalivk并不会主动的去回收,所以需要开发者在不使用BitMap的时候recycle掉,
这样可以及时的释放内存。同时如果需求准许,可以去BitMap进行一定的缩放,
通过BitMapFactory.Options的inSampleSiae的属性进行控制
如果只是想获取BitMap的属性只需在解析读取BitMap的时候使用
BitMapFactory.Options的inJustDesodeBounds属性。
还是建议大家在加载网络图片的时候使用软引用和弱引用,并进行本地缓存。
4. Cursor和I/O流的及时关闭
在SQLite数据库查询的时候会返回一个Cursor,当查询结束,
及时关闭,这样就可以及时的把查询的结果集合及时的回收掉。
I/O流操作完毕,读写结束,记得关闭。
5. 线程
线程不在需要的时候要记得及时关闭,开启线程的数量不易过多,一般和自己机器内核数一样最好,
推荐开启线程的时候,使用线程池。
二. 布局优化
(1)布局优化
在Android种系统对View进行测量、布局和绘制时,都是通过对View数的遍历来进行操作的。
如果一个View数的高度太高就会严重影响测量、布局和绘制的速度。
Google也在其API文档中建议View高度不宜哦过10层。
现在版本种Google使用RelativeLayout替代LineraLayout作为默认根布局,
目的就是降低LineraLayout嵌套产生布局树的高度,从而提高UI渲染的效率。
布局复用,使用<include>标签重用layout;
提高显示速度,使用<ViewStub>延迟View加载;
减少层级,使用<merge>标签替换父级布局;
注意使用wrap_content,会增加measure计算成本;
删除控件中无用属性;
三. 电池优化
(一)节省——耗电优化
在移动设备中,电池的重要性自然不言而喻,如果手机没电了应用功能技术实现再怎么牛逼,
用户也什么都干不成。对于Android操作系统和设备各大开发商来说,对手机耗电的优化从没有停止过,
不断地追求更长的待机时间。而对于开发一款应用来说,绝不可以忽略电量耗损的问题,
被归为“电池杀手”的应用,最终的结果无疑是走向被用户卸载的道路。比如,
有些应用为了保持应用进程长期在后台存活,使用各种不合理进程保活方案,破坏操作系统“生态平衡”,
导致用户电量严重耗损,虽然这种流氓开发行为并不违法吧,但是也属于不道德的行为,
也同样会被同行所被鄙视的行为。
在 Android5.0 以前,关于应用电量消耗的测试即麻烦又不准确,
而5.0 之后Google专门引入了一个获取设备上电量消耗信息的API—— Battery Historian。
Battery Historian 是一款由 Google 提供的 Android 系统电量分析工具,
直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况。
四. listView优化
每次更新 ListView Item 数据时,都要通过 View 的 findViewById() 方法定位每个子控件,
findViewById() 会沿着ListView Item 的控件布局结构遍历每个控件直到找到指定 id 的控件,
这是比较耗时的,尤其是布局比较复杂时。
1.复用,减少创建
优化方法很简单,在每次创建新的 ListView Item 时保存通过 findViewById() 找到的每个子控件的引用。
这些控件引用可以保存在一个单独的对象中,一般命名为 ViewHolder,
然后将 ViewHolder 对象存储在 ListView item 中(通过 View 的 setTag() 方法,
该方法可以在 View 中存储额外数据),下次可以直接从 ListView item 中取得。
adapter中getview方法会传进来一个convertView,convertView是指曾经使用过的view对象,
可以被重复使用,但是在使用前需要判断是否为空,不为空直接复用,并作为getview方法的返回对象。
2.限制 ListView 的滚动速度
ListView 默认的滚动速度是比较快的,如果 ListView 滚动速度慢一点,
那么每个 ListView Item 就有更多的加载时间,这也可以使 ListView 看上去更加流畅。
下面代码将 ListView 的滚动速度减慢为原来的 1/10:
listview.setFriction(ViewConfiguration.getScrollFriction() * 10);
五. Apk优化
(1)安装包——APK瘦身
其实APK大小对应用使用并没有影响,但应用的安装包越大,用户下载的门槛越高。
例如,应用版本的迭代更新,特别是当用户在移动网络情况下,又不得不去下载安装包,
才能使用产品满足自身需求。因此,开发应该减小安装包大小,使得让更多用户愿意下载产品和体验产品。
1.代码混淆。使用IDE 自带的 proGuard 代码混淆器工具 ,它包括压缩、优化、混淆等功能。
2.资源优化。比如使用 Android Lint 删除冗余资源,资源文件最少化等。
3.图片优化。比如利用 PNG优化工具 对图片做压缩处理。
如果应用在4.0版本以上,推荐使用 WebP图片格式。
4.可以使用微信开源资源文件混淆工具——AndResGuard 。一般可以压缩apk的1M左右大。
5.插件化热修复开发。比如功能模块放在服务器上,按需下载,可以减少安装包大小。
6.避免重复或无用功能的第三方库。例如,百度地图接入基础地图即可、
讯飞语音无需接入离线、图片库Glide\Picasso等
7 项目混淆打包
六.如何减少应用启动时的耗时
针对冷启动时候的一些耗时,可以采取以下策略:
1、Application
在Application的构造器方法、attachBaseContext()、
onCreate()方法中不要进行耗时操作的初始化,
一些数据预取放在异步线程中,可以采取Callable实现。
2、sp库
对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,
所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,
对于这个还是需要放在异步线程中处理。
3、布局
对于MainActivity,由于在获取到第一帧前,
需要对contentView进行测量布局绘制操作,尽量减少布局的层次,
在onCreate、onStart、onResume方法中避免做耗时操作。
4.windowBackground
使用Activity的windowBackground主题属性来为启动的Activity提供一个简单的drawable,
这样在启动的时候,会先展示一个界面,
这个界面就是Manifest中设置的Style,等Activity加载完毕后,
再去加载Activity的界面,而在Activity的界面中,我们将主题重新设置为正常的主题,
从而产生一种快的感觉。
5.初始化
避免在启动时做密集沉重的初始化(Heavy app initialization);
6.定位问题:
避免I/O操作、反序列化、网络操作、布局嵌套等。
七.页面切换的时候出现卡顿改怎么解决
比如从一个界面Activity1跳转到另外一个界Activity2。
应用必须在走完Activity1的onPause方法后才会跑Activity2的onCreate方法,
tActivity1的onStop和onDestory方法不会影响到进入Activity2的速度。
如果我们要优化从Activity1跳转到Activity2的速度,
需要从Activity1的onPause和Activity2的onCreate、onStart和onResume方法入手。
onStart方法通常干的事情比较少,
页面之间跳转慢主要是因为在Activity1的onPause和Activity2的onCreate、onResume方法耗时导致,
这个过程需要执行的操作主要有:
1.保存Activity1界面中的一些状态;
2.加载Activity2的布局;
3.初始化Activity2。
针对上面的分析我们可以从如下几个方面入手:
1 耗时任务异步处理;
除了Android明令禁止在UI线程中执行网络操作外,
还有一些耗时的操作也不能在UI线程中执行,
比如IO操作、耗时较长的逻辑操作(比如算法)
在子线程中做网络请求,
并在主线程中更新UI。(可以用第三方的网络框架来做这块的事情)
检查是否网络请求用多
可用如下方式来实现异步任务:
1.AsyncTask
2.Thread
3.Timer
4.TimerTask
5.Handler
如果是在执行异步任务后需要更新界面,优先考虑使用AsyncTask和Handler,
它们提供了刷新UI的方案;如果是定时任务可以考虑使用Handler和Timer,TimerTask;
如果是使用Thread和Timer,TimerTask,
更新UI时可以通过执行当前Activity的runOnUiThread方法实现更新UI操作。