Android性能优化系列——内存优化

2024-01-08 23:43:33

内存,是Android应用的生命线,一旦在内存上出现问题,轻者内存泄漏造成App卡顿,重者直接crash,因此一个应用保持健壮,要做好内存的使用和优化。网上有很多讲JAVA内存虚拟机的好文章,我就不赘述了。今天主要总结下内存优化。

作为一个开发者,需要在平时的代码中就要多注意,如有不合理的地方,则需要进行优化。

一、内存泄漏(memory leak):

内存泄漏就是在当前应用周期内不再使用的对象被GC Roots引用,导致不能回收,使实际可使用内存变小,通俗点讲,就是无法回收无用对象。

二、内存溢出(out of memory,也叫OOM):

系统在申请内存空间时,没有总够的内存空间供其使用。简单来说就是系统不能再分配你所需要的内存空间,比如你申请需要100M的内存空间,但是系统仅剩90M了,这时候就会内存溢出。(溢出会导致应用Crash崩溃)

内存泄漏的本质原因:

本该被回收的对象没有被回,继续停留着内存空间中,导致内存被占用。其实是持有引用者的生命周期 > 被持有引用者的生命周期。过多内存泄漏会把内存空间占用完,最终会导致内存溢出。

三、内存泄漏的常见情况

1.非静态内部类创建静态实例

非静态内部类创建静态实例会导致内存泄漏,通常项目中,通常在Activity启动的时候创建单例数据,避免重复创建相同的数据,会在Activity内部创建一个非静态内部类的静态实例。

错误的例子:非静态内部类默认持有外部引用

public class NotStaticActivityextends AppCompatActivity {
    //非静态内部类实例引用,设置为静态
    private static InnerClass sInnerClass;
 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        //保证非静态内部类实例只有一个
        if (sInnerClass == null) {
            sInnerClass = new InnerClass();
        }
    }
 
    //非静态内部类
    private class InnerClass {
        //……
    }
}

非静态外部类(InnerClass)的生命周期 = 应用的生命周期,而且持有外部类Activity的引用,在Activity在销毁的时候无法被GC回收,从而导致内存泄漏。

优化后的代码1:将非静态内部类设置为静态内部类(静态内部类默认不持有外部类的引用)

  //静态内部类
    private static class InnerClass2 {
        //……
    }

优化后的代码2:将外部类抽取出来封装成一个单例

public class InnerClass  extends AppCompatActivity {
    private static final String TAG = InnerClass3.class.getSimpleName();
    private static InnerClass sInnerClass;
    //单例
    public InnerClass getInstance() {
        if (sInnerClass == null) {
            sInnerClass = new InnerClass();
        }
        return sInnerClass;
    }
}
2.注册对象未注销或资源对象未关闭

注册了像BraodcastReceiver,EventBus这种,没有在页面销毁时注销的话,会引发泄露问题,所以应该在Activity销毁时及时注销。

3.多线程造成内存泄漏

我们知道线程类属性非静态(匿名)内部类,多线程的使用主要是AsyncTask、实现Runnable接口,继承Thread类,在使用线程类时需要注意内存泄漏。

1).AsyncTask

我们常常使用AsyncTask来执行一些异步的耗时操作。

我们需要在界面销毁的时候调用AsyncTask.cancel(true)方法。

@Override
    protected void onDestroy() {
       //强制退出AsyncTask
        mAsyncTask.cancel(true);
        super.onDestroy();
    }

2).Thread和Handler的使用

Thread:线程类Thread属于非静态内部类,它的生命周期比ThreadActivity的生命周期长,运行时默认持有外部类的引用,如果ThreadActivity需要销毁时,线程类Thread持有ThreadActivity的引用,导致ThreadActivity无法回收利用,造成内存泄漏。

我们经常这样使用:


 
    //方式一:新建内部类
        new MyThread().start();
 
    //方式二:匿名Thread内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
 
    //自定义Thread
    private class MyThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

优化方法1;把?private class MyThread extends Thread改成静态内部类??private static class MyThread extends Thread;

优化方法2:在界面的ondestroy方法中,把线程结束掉.

Handler:也是一个内部类,我们在使用的时候也会提示他如果是非静态的,就会造成内存泄漏。

解决方法1:

??如果?Handler?不需要外部?Activity?的数据,直接使用使用静态内部类或外部类即可,避免使用成员内部类。如果需要,还可以使用外部类+弱引用的方法。

private static class MyHandler extends Handler {
     private WeakReference<MainActivity> ref;

     public MyHandler(Activity activity) {
         this.ref = new WeakReference<>(activity);
     }

     @Override
     public void handleMessage(@NonNull Message msg) {
         // TODO:ref.get()...
         
         super.handleMessage(msg);
     }
}

tips:使用成员内部类 + 弱引用的方法是不行的,内部类默认持有外部类引用的。

解决方法2:当外部类结束生命周期时,清空Handler内消息队列;

   @Override
    protected void onDestroy() {
        //当外部类结束生命周期时,清空Handler内消息队列;
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
4.static关键字修饰的成员变量

我们知道被static修饰的成员变量的生命周期等于app应用程序的生命周期。

所以,是否有些不需要的是否可以去掉,静态的改成非静态的。

如果静态的对象引用了activity,是否可以替换成App的上下文就足够了。

5.引用的对象是否回收,置空

(1)动画相关资源使用后,记得及时回收;

(2)IO流相关类:在使用IO流,File文件类或者Sqlite、Cursor等资源时要及时关闭,这些资源在读写操作时一般都行了缓冲,如果不及时关闭,这些缓冲对象就会一直被占用得不到释放,以致内存泄漏,所以在在它们不需要使用时,及时关闭,缓冲释放资源,避免内存泄漏。

  1. stream.close();

  2. cursor.close();

  3. bitmap.recycle();bitmap = null;

  4. arrayList.clear();arrayList = null;

6、内存抖动

当大量的对象创建又回收时,就会内存抖动,例如常见的在onDraw方法中创建对象,应把对象放在类中或者初始化方法中。

文章来源:https://blog.csdn.net/LoveFHM/article/details/135465061
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。