Android 性能优化(一)

Android作为一种移动设备,内存和CPU资源都是受限的. 过多地使用内存会导致内存溢出(OOM),过多的使用CPU会导致手机卡顿,甚至出现程序无法响应的情况(ANR). 本文介绍一系列优化方法.

1. 布局优化

1.1 尽量减少布局的层级.

布局的层级少了,那么Android绘制时的工作量就少了,自然能后提高性能.

1.2 ViewGroup的选择.

有选择的使用性能较低的ViewGroup如RelativeLayout的功能比较复杂绘制过程消耗更多的CPU资源 , 尽量使用性能较高的ViewGroup如LinearLayout 和FrameLayout.

1.3 <include> 标签

<include> 标签可以将指定的布局加载到当前布局 .

定义一个布局文件,layout_include.xml文件.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:background="#f00"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:text="测试按钮"
        android:textSize="30sp"
        android:layout_height="wrap_content"/>
</LinearLayout>

使用 <include>标签引用布局.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/activity_main"
    android:background="#0f0"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 使用 include 标签 -->
    <include
        android:layout_height="200dp"
        android:layout_width="200dp"
        layout="@layout/layout_include"/>
</LinearLayout>

效果图如下:

效果图

1.4 <merge> 标签

<merge> 标签主要和 <include> 标签一起使用,比如在上面的样例中当前布局使用了一个竖直方向的LinearLayout,在被引用的布局中也使用了一个竖直方向的LinearLayout此时内层的LinearLayout就显得多余了.可以使用<merge>标签替代.从而达到减少布局层级的目的.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <Button
        android:layout_width="wrap_content"
        android:text="测试按钮"
        android:textSize="30sp"
        android:layout_height="wrap_content"/>
</merge>

1.5 ViewStub

ViewStub继承自View并且宽高都是 0,它本身不参与任何布局和绘制过程. 他可以按需添加布局,有些布局一开始是不需要的,因此可以先不加载,在需要时再进行布局加载.

加载ViewStub前界面

加载ViewStub后界面

创建layout_viewstub.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/stub_import"
              xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
    <TextView
        android:background="#00f"
        android:layout_width="match_parent"
        android:text="View_Stub测试界面"
        android:textSize="30sp"
        android:layout_height="match_parent"/>
</LinearLayout>

activity_main.xml 中添加ViewStub节点

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/activity_main"
    android:background="#0f0"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 使用 include 标签 -->
    <include
        android:layout_height="200dp"
        android:layout_width="200dp"
        layout="@layout/layout_include"/>
    <!-- 使用ViewStub -->
    <ViewStub
        android:layout_width="200dp"
        android:id="@+id/panel_import"
        android:layout="@layout/layout_viewstub"
        android:inflatedId="@+id/stub_import"
        android:layout_height="200dp"/>
    <Button
        android:layout_width="wrap_content"
        android:onClick="onclick"
        android:text="加载布局"
        android:layout_height="wrap_content"/>
</LinearLayout>

修改MainActivity.java

public void onclick(View view) {
    // 加载ViewStub方式一
    // ((ViewStub)findViewById(R.id.panel_import)).setVisibility(View.VISIBLE);
    // 加载ViewStub方式二  
    ((ViewStub)findViewById(R.id.panel_import)).inflate();
}

android:id="@+id/panel_import" : 设置ViewStub的id.
android:id="@+id/panel_import" : ViewStub加载的布局的根布局的id.
android:layout="@layout/layout_viewstub" : 设置加载布局的资源id.
注意 : ViewStub不支持<merge>标签

2. 绘制优化

绘制优化主要是指View的onDraw方法要避免大量操作. 主要体现如下两方面 :

  • onDraw中不要创建新的局部对象, 这是应为onDraw方法会被频繁调用,这样会在一瞬间产生大量临时对象.这样会造成过多的内存效果,更多的GC,降低程序的执行效率.
  • onDraw方法中不要进行耗时操作,也不能执行大量的循环操作.虽然每次都是轻量级的但是大量循环还是会十分抢占CPU的时间片.造成View的绘制不流畅.按照Google给出的性能优化典范中的标准,View的帧率保证60fps是最好的.这就要求每一帧图像的绘制不要超过 1000 / 60 = 16ms .因此尽量减低onDraw的复杂度有利于提高性能.

3. 内存泄露优化

内存泄露的优化分为两部分.

  • 开发过程中避免写内存泄露的代码.
  • 通过一些工具如MAT找出潜在的内存泄露,然后解决掉.

3.1 静态变量导致内存泄露

public class MainActivity extends AppCompatActivity {
    private static Context sContext;
    private static View sView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 设置
        sContext = this;
        sView = new View(this);
    }
}

上面的代码容易造成内存泄露.

3.2 单例模式导致的内存泄露

单例模式造成内存泄露主要是单例对象保存的外部变量的引用没有被移除造成的.比如单例对象有添加监听者的方法但是没有移除监听者对象的方法.

3.3 属性动画导致的内存泄露

Android 3.0 增加了属性动画,这种动画需要在onDestory中取消动画animator.cancel()否则动画就会无限循环地执行,造成动画对象会持有View,View持有Activity,最终造成Activity无法释放.造成内存泄露.

属性动画持有关系

3.4 响应速度优化和ANR日志分析

Android系统规定:

  • Activity如果 5 秒内无法响应屏幕触摸或者键盘输入时间就会出现ANR.
  • BroadcastReciever 10秒内没有执行完操作,就会出现ANR.

当系统发生ANR时会在 data/anr目录下生成traces.txt文件.通过分析这个文件就可以找到ANR的原因.

我们在onCreate方法中添加如下代码模模拟ANR

while(true);

查看traces.txt部分内容如下:

traces.txt 文件部分内容

便可以定位出问题所在了.

4. ListView 和 Bitmap优化.

4.1 ListView 优化主要有三个方面

  • 使用ViewHolder,避免在getView中进行耗时操作.
  • 根据列表的滑动状态来控制任务的执行频率,比如当列表快速滑动时不适合开启大量异步任务.
  • 开启硬件加速来使ListView更加流畅.

4.2 Bitmap优化

Bitmap优化主要是通过加载压缩后的图片来进行优化的.

5. 线程优化

尽量使用线程池代替开启新的线程.

6. 性能优化建议

  • 避免创建过多的对象.
  • 不要过多的使用枚举,枚举占用的空间比整形大.
  • 常量使用 static final 来修饰.
  • 使用一些Android 特有的数据结构,SparseArray 和 Pair 他们具有更好的性能.
  • 适当使用软引用和弱引用.
  • 采用内存缓存和磁盘缓存.
  • 尽量使用静态内部类,避免潜在的由于内部类导致的内存泄露.

参考 : <Android开发艺术探索>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容