美文网首页
android常见的性能优化方面的总结

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

作者: 韩明泽 | 来源:发表于2019-02-12 22:14 被阅读183次

    年假即将结束,这篇文章也算是我自己梳理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);
    }
    
    public static void getTesLeak(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性能优化的浅谈

    相关文章

      网友评论

          本文标题:android常见的性能优化方面的总结

          本文链接:https://www.haomeiwen.com/subject/zleleqtx.html