性能优化的目的是为了让应用程序App 更快、更稳定 & 更省。具体介绍如下:
针对上述目的,APP优化的指标就是:流畅性、稳定性、资源节省性。
具体实现如下:
减少使用中的卡顿、响应时间久等问题,给用户一个操作流畅的体验,主要针对3个方面进行优化:
优化原因:初次加载应用,需要加载很多资源或者逻辑功能。
优化方案:采用异步加载(多线程)、分步加载、延期加载等策略,减少启动应用时的加载任务,从而提高启动速度。
优化原因:页面需绘制的内容(布局或控件)太多,导致页面测量时间太长;页面绘制效率过低,导致绘制时间过长。
优化方案:布局优化与绘制优化
优化原因:应用程序出现 ANR 情况,从而导致 应用程序响应速度慢
优化方案:使用多线程,将大量耗时操作放在工作线程中执行。
优化方案:使用多线程,将大量耗时操作放在工作线程中执行,比如AsyncTask,HandlerThread等等
影响Android 应用稳定性的原因有很多,主要是:应用崩溃(Crash)、应用无响应(ANR)
程序出现ANR(Application Not Responding,应用程序无响应),导致应用程序响应慢或者屏幕卡死在一个页面。
常见的ANR原因:
优化方案:使用多线程,将大量耗时操作放在工作线程中执行,比如AsyncTask,HandlerThread等等
实际开发中,当一个进程发生了ANR,系统会在 /data/anr目录下创建一个文件 traces.txt,通过分析该文件可定位出ANR的原因
程序出现Crash(崩溃)很多情况是因为内存溢出即OOM。
Android系统为每个应用程序分配的内存有限,当应用程序中出现内存泄露较多,不正常使用内存等情况时,容易导致应用程序出现所需的内存超出系统能够为其分配的内存限额(OOM),从而导致程序崩溃。
解决Crash就是解决OOM,涉及到的工作就是内存优化。
优化原因:由于移动设备的硬件性能有限,故减少应用程序的资源消耗显得十分重要
优化方向:内存大小、安装包大小、耗电量 & 网络流量
主要手段包含:内存优化、减少安装包大小、减少网络流量、减少应用耗电量。
布局性能的好坏主要影响Android中页面显示速度,布局影响性能的实质在于:页面的测量与绘制时间
FrameLayout、LinearLayout
RelativeLayout
注意:宁愿选择1个耗费性能高的布局,也不采用嵌套多个耗费性能低的布局
布局属性wrap_content 会增加布局测量时计算成本,应尽可能少用;特别是在已知宽高为固定值时,不使用wrap_content。
include>标签可以实现在一个layout中引用另一个layout的布局,这通常适合于界面布局复杂、不同界面有共用布局的APP中,比如一个APP的顶部布局、侧边栏布局、底部Tab栏布局、ListView和GridView每一项的布局等,将这些同一个APP中有多个界面用到的布局抽取出来再通过
标签引用,既可以降低layout的复杂度,又可以做到布局重用(布局有改动时只需要修改一个地方就可以了)。
merge标签是用来帮助在视图树中减少重复布局的,当一个layout包含另外一个layout时。
merge使用的要点:
实例:
不使用merge:
layout1.xml
<FrameLayout>
<include layout="@layout/layout2"/>
FrameLayout>
layout2.xml
<FrameLayout>
<TextView />
FrameLayout>
实际效果:
<FrameLayout>
<FrameLayout>
<TextView />
FrameLayout>
FrameLayout>
使用merge:
layout1.xml
<FrameLayout>
<include layout="@layout/layout2"/>
FrameLayout>
layout2.xml
<merge>
<TextView />
merge>
实际效果:
<FrameLayout>
<TextView />
FrameLayout>
从上边的例子中可以看到,include实际上是将另外一个layout完整的嵌入到需要使用的地方。由于所有的组件都必须在ViewGroup中,这就导致了,使用include标签,嵌入时,会多一层ViewGroup,而使用merge则相当于声明了一个虚拟的ViewGroup,使得在include时它可以完全融入merge到需要使用的地方。
ViewStup是一个轻量级的view,之所以说它是轻量级的view是因为它在页面加载渲染的过程中不会去绘制,只是在你需要的时候再去绘制。
ViewStub标签的作用是用于懒加载布局,当系统碰到ViewStub标签的时候是不进行绘制处理(如measure、layout等),比设置View隐藏、不可见更高效。
使用ViewStub的好处:
ViewStub的缺点在于不可使用
标签,它在使用上类似于
标签
从机制上讲:Java存在GC,理应不存在内存泄露,出现内存泄露的原因仅仅是外部人为原因,无意识地持有对象引用,使得持有引用者的生命周期 > 被引用者的生命周期
内存泄露原因:集合类 添加元素后,仍引用着 集合元素对象,导致该集合元素对象不可被回收,从而 导致内存泄漏
// 通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List
List<Object> objectList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Object o = new Object();
objectList.add(o);
o = null;
}
// 虽释放了集合元素引用的本身:o=null)
// 但集合List 仍然引用该对象,故垃圾回收器GC 依然不可回收该对象
解决方案:
由于1个集合中有许多元素,故最简单的方法 = 清空集合对象 & 设置为null
// 释放objectList
objectList.clear();
objectList=null;
被 Static 关键字修饰的成员变量的生命周期 = 应用程序的生命周期.
泄露原因:被 Static 关键字修饰的成员变量 引用耗费资源过多的实例(如Context),则容易出现该成员变量的生命周期 > 引用实例生命周期的情况,当引用实例需结束生命周期销毁时,会因静态变量的持有而无法被回收,从而出现内存泄露
public class ClassName {
// 定义1个静态变量
private static Context mContext;
//...
// 引用的是Activity的context
mContext = context;
// 当Activity需销毁时,由于mContext = 静态 & 生命周期 = 应用程序的生命周期,故 Activity无法被回收,从而出现内存泄露
}
解决方案:
非静态内部类持有外部类的引用,导致外部类无法被释放。
常见情况有:
泄露原因:对于资源的使用(如广播、文件流、数据库操作的游标、图片资源BitMap),若在Activity销毁时,没有及时关闭或注销这些资源,从而导致内存泄露。
解决方案:
在Activity销毁时:及时关闭 / 注销资源