美文网首页
何止学习Andoid内存泄漏优化

何止学习Andoid内存泄漏优化

作者: 何止搬砖工 | 来源:发表于2020-07-15 00:54 被阅读0次

    一、Android内存泄露及优化

    1、单例模式造成内存泄漏

    单例的静态特性使得它的生命周期同应用的生命周期一样长,在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。

    举例:

    public class MySingleton {
    
        private Context context;
    
        private static MySingleton ourInstance ;
    
        public static synchronized MySingleton getInstance(Context context) {
    
            if (null == ourInstance){
    
                ourInstance = new MySingleton(context);
            }
            return ourInstance;
        }
    
        private MySingleton(Context context) {
            
            this.context = context;
    
        }
        
    }
    

    像上面代码中这样的单例,如果我们在调用getInstance(Context context)方法的时候传入的context参数是Activity、Service等上下文,就会导致内存泄露。

    优化方案

    (1)、使用全局的上下文Application Context就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏。
    (2)、不将context作为成员变量,而在用到的地方,用形参传入。

    2、静态变量导致内存泄露

    举例

    public class MainActivity extends AppCompatActivity {
    
        public static final String TAG = "MainActivity";
        private static Info sInfo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (sInfo != null) {
                sInfo = new Info(this);
            }
        }
    }
    
    class Info {
        public Info(Activity activity) {
        }
    }
    

    Info作为Activity的静态成员,并且持有Activity的引用,但是sInfo作为静态变量,生命周期肯定比Activity长。所以当Activity退出后,sInfo仍然引用了Activity,Activity不能被回收,这就导致了内存泄露。

    优化

    在Activity销毁之前置空。

      protected void onDestroy() {
            super.onDestroy();
            if(sInfo != null){
                sInfo = null;
            }
        }
    

    3、非静态内部类导致内存泄露(Handler,Thread,AsyncTask)

    非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

    举例

    public class MainActivity extends AppCompatActivity {
    
        public static final String TAG = "MainActivity";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            sendMessage();
        }
    
        private void sendMessage() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }
    
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 1) {
                    // 做相应逻辑
                }
            }
        };
    }
    

    mHandler会作为成员变量保存在发送的消息msg中,即msg持有mHandler的引用,而mHandler是Activity的非静态内部类实例,即mHandler持有Activity的引用,那么我们就可以理解为msg间接持有Activity的引用。msg被发送后先放到消息队列MessageQueue中,然后等待Looper的轮询处理(MessageQueue和Looper都是与线程相关联的,MessageQueue是Looper引用的成员变量,而Looper是保存在ThreadLocal中的)。那么当Activity退出后,msg可能仍然存在于消息对列MessageQueue中未处理或者正在处理,那么这样就会导致Activity无法被回收,以致发生Activity的内存泄露。

    优化

    通常在Android开发中如果要使用内部类,但又要规避内存泄露,一般都会采用静态内部类+弱引用的方式。

    public class MainActivity extends AppCompatActivity {
    
        public static final String TAG = "MainActivity";
        private Handler mHandler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mHandler = new MyHandler(this);
            sendMessage();
        }
    
        private void sendMessage() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }
    
        private static class MyHandler extends Handler {
    
            private WeakReference<MainActivity> activityWeakReference;
    
            public MyHandler(MainActivity activity) {
                activityWeakReference = new WeakReference<>(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                MainActivity activity = activityWeakReference.get();
                if (activity != null) {
                    if (msg.what == 1) {
                        // 做相应逻辑
                    }
                }
            }
        }
    }
    

    上面的做法确实避免了Activity导致的内存泄露,发送的msg不再已经没有持有Activity的引用了,但是msg还是有可能存在消息队列MessageQueue中,所以更好的是在Activity销毁时就将mHandler的回调和发送的消息给移除掉。

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
    

    非静态内部类造成内存泄露还有一种情况就是使用或者。在Activity中直接new Thread或AsyncTask
    这样的写法非常地不友好,这种方式新建的子线程和都是匿名内部类对象,默认就隐式的持有外部的引用,导致内存泄露。要避免内存泄露的话还是需要像上面一样使用静态内部类+弱应用的方式。

    4、未取消注册或回调导致内存泄露

    比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个刚播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            this.registerReceiver(mReceiver, new IntentFilter());
        }
    
        private BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // 接收到广播需要做的逻辑
            }
        };
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            this.unregisterReceiver(mReceiver);
        }
    }
    

    5、集合中的对象未清理造成内存泄露

    在循环中把引用o释放了,但是它被添加到了objectList中,所以objectList也持有对象的引用,此时该对象是无法被GC的。因此对象如果添加到集合中,还必须从中删除,最简单的方法,置空集合对象即可

    6、资源未关闭或释放导致内存泄露

    一些IO/File流、cursor没及时关闭,这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露。

    7、WebView造成内存泄露

    因为在加载网页后会长期占用内存而不能被释放,因此我们在销毁后要调用它的方法来销毁它以释放内存。
    下面的持有Activity引用,造成Webview内存无法释放,即使是调用了等方法都无法解决问题(Android5.1之后)。
    最终的解决方案是:在销毁之前需要先将从父容器中移除,然后在销毁。

    相关文章

      网友评论

          本文标题:何止学习Andoid内存泄漏优化

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