android常见的性能优化方面的总结

年假即将结束,这篇文章也算是我自己梳理android知识的最后几篇了。文章中的整体思路是根据《android开发艺术》结合平时开发经验以及网上的资料完成的。内容用的源码都可以在GitHub上的项目中查看到,希望阅读完这篇文章能让你有所收获。

项目源码

目录

  • 布局优化
  • 绘制优化
  • 内存泄漏优化
  • ListView和Bitmap优化

布局优化

  • 减少布局文件的层级
  • 删除布局中无用的控件和布局
  • 尽量使用简单高效的ViewGroup,比如FrameLayoutLinaerLayout
  • 可以使用<include>标签复用布局,使用<merge>标签减少层级

<include>、 <merge>标签案例

在layout文件中创建layout/incloude_merge_memory.xml文件内容如下:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/mTV_incloud_merge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_red_light"
        android:gravity="center"
        android:padding="5dp"
        android:text="这是一个include的merge" />
</merge>

在Activity的layout布局引入:

<include layout="@layout/incloude_merge_memory" />

ViewStub

  • 它是一个轻量级的布局宽度、高度只有0,不参与绘制过程。
  • 按需加载,不占用空间。
  • 当显示ViewStub中的布局时候,ViewStub会被替换掉,并且会被从布局中移除。

xml代码:

<ViewStub
    android:id="@+id/mVS_layoutMemory"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:background="@android:color/holo_blue_bright"
    android:inflatedId="@id/mRL_viewStubMemory"
    android:layout="@layout/viewsutub_memory"
    android:padding="10dp" />

上面代码中id为mVS_layoutMemory为ViewStub的id,而inflatedId是引入布局@layout/viewsutub_memory跟布局的id。需要注意的是ViewStub中layout布局是不支持<merge>标签的,接下来看一下java代码的调用:

mVS_layoutMemory = findViewById(R.id.mVS_layoutMemory);
mVS_layoutMemory.setVisibility(View.VISIBLE);

绘制优化

  • 不要在onDraw中创建新的布局对象
  • 不要在onDraw中做大量的耗时操作

内存泄漏优化

  • 静态变量引起的泄漏
  • 单例模式引起的泄漏
  • 非静态内部类持有外部引用导致的泄漏
  • Handler引起的内存泄漏
  • 属性动画引起的泄漏

静态变量导致的内存泄漏

这种情况常见的是Context的使用,比如我们写了一个工具类,里面的方法需要用到Context。如果我们将Activity的this传给这个方法,那么Activity在被回收的时候由于这个静态变量持有Activity的引用,导致不能被回收从而引起内存泄漏。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layoyt_memory);
  AppUtil.getTesLeak(this);
}
static Context context;
public static void getTesLeak(Context context) {
    context=context;
    Toast.makeText(context, "您的内存泄漏啦", Toast.LENGTH_SHORT).show();
}

解决上面的问题也很多简单,如果我们的工具类不是一定需要Activity的Context,难么我们可以考虑使用getApplicationContext()。因为getApplicationContext()是和我们App的生命周期一样长,如果App不退出他就不会被回收。

单例模式导致的内存泄漏

单例引起的内存泄漏,大概思路上面差不多,也是因为静态变量的生命周期太长,如果程序不退出,系统就不会对其回收,这将导致本应该不用的对象不能回收,我们可以指定Context为getApplicationContext();来避免内存泄漏。

public class MemorySingle {
    //如果传入上下文
    private static Context context;
    private MemorySingle() {
    }

    public static MemorySingle getInstance(Context context) {
        //防止内存泄漏
        MemorySingle.context = context.getApplicationContext();
        return Menory.single;
    }
    
    static class Menory {
        private static final MemorySingle single = new MemorySingle();
    }
}

非静态内部类持有外部引用引起的泄漏

因为非静态内部类的生命周期是和外部类的生命周期绑定在一起的,非静态内部类会持有外部类的引用,如果我们在内部类中做一些耗时操作,如下面内部类sleepThread()方法让线程睡10秒,在这个时候如果Activy要销毁,但是因为内部类持有外部类的引用,它的sleepThread()方法还没执行完,所以导致Activy不能被回收,引起内存泄漏。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layoyt_memory);
    TestLeak testLeak = new TestLeak();
    testLeak.sleepThread();
}
 class TestLeak {
    private void sleepThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //睡10秒
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

解决方法将TestLeak改成静态内部类

static class TestLeak {
    private void sleepThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //睡10秒
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

Handler引起的内存泄漏

我们使用Handler做消息处理的时候可能不注意会用下面这种写法:

private Handler mHanlder = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 200:
                mTV_incloud_merge.setText((String) msg.obj);
                break;
        }
        super.handleMessage(msg);
    }
};

上面的mHanlder是Handler的非静态匿名内部类,上面我们提到过非静态匿名内部类会持有外部引用,所以如果使用上面的写法也会引起内存泄漏。下面有两种方式可以避免泄漏。

第一种方式: 使用静态内部类+弱引用方式

static class MyHanlder extends Handler {
    //弱引用
    WeakReference<Activity> mWeakRef;
    public MyHanlder(Activity activity) {
        mWeakRef = new WeakReference<>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 200:
                LayoutMemoryActivity activity = (LayoutMemoryActivity) mWeakRef.get();
                activity.mTV_incloud_merge.setText((String) msg.obj);
                break;
        }
    }
}

第二种: Handler.Callback方式处理消息

Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 200:
                mTV_incloud_merge.setText((String) msg.obj);
                break;
        }
        return false;
    }
})

属性动画导致的内存泄漏

当一个Activy中有一个无限循环的属性动画,在Activy销毁的时候没有停止动画也会引起内存泄漏

 ObjectAnimator oA = ObjectAnimator.ofFloat(mBnt_layoutMemory, "rotation", 0, 360).setDuration(20000);
               oA.start();

上面的是一个按钮选装动画,20秒后执行完,如果在动画还为执行完的时候销毁Activy,将会导致Activy无法释放引起内存泄漏。下面是解决办法

@Override
protected void onDestroy() {
    super.onDestroy();
    //取消动画
    oAnimator.cancel();
}

ListView和Bitmap优化

ListView优化

ListView的优化是一个很长见的问题,主要是通过ViewHolder实现对item的复用,这里不做过多的解释了。在这我推荐一篇文章感兴趣的可以看看,下面有一个例子:

@Override
public View getView(final int position, View convertView,        ViewGroup parent) {
    MyHolder myHolder = null;
    //判断是否有缓存布局
    if (convertView == null) {
        convertView =LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, null);
        myHolder = new MyHolder(convertView);
        convertView.setTag(position);
    } else {
        //得到缓存布局
        myHolder = (MyHolder) convertView.getTag();
    }
    myHolder.mTV_test1.setText(textContent);
}          
class MyHolder {
    TextView mTV_test1;

    MyHolder(View view) {
        mTV_test1 = view.findViewById(android.R.id.text1);
    }
}

Bitmap优化

我们大部分图片处理是使用glide、'picasso',这些框架在图片加载速度和性能优化方面已经很好了,但有些特殊情况可能需要我们自己实现图片的处理,主要注意下面几个方面。

  • 对图片进行压缩
  • 缓存策略
  • 图片不使用的时候要记得释放

总结

android的性能优化需要了解的方面还有很多比如电量的优化、包大小、启动速度的优化等等,上面列出的只是一部分常见的问题和解决办法。在开发过程中需要优化的放要远比上面写道的多,还需要我们自己多积累经验和结合实际考虑来优化。

参考

Android 内存泄漏总结
Android性能优化的浅谈

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

推荐阅读更多精彩内容