美文网首页
Android常见的内存泄漏

Android常见的内存泄漏

作者: HappyGhh | 来源:发表于2018-06-12 20:55 被阅读0次
    1、内部类导致内存泄漏

    1、内部类实例会隐式持有外部类的引用。
    例如在Activity中创建一个内部类实例,然后在内部类实例中执行耗时操作,在执行任务时,关掉Activity,这时Activity对象不会被释放,因为内部类还持有对Activity的引用,但此时Activity已经没用了,所以这时就出现了内存泄漏。

    1.1 Thread线程
    在Activity中创建一个内部类继承Thread,在该Thread中执行一些后台任务,未执行完时,关闭Activity,此时会出现内存泄漏。

    如何解决:避免内部类隐式调用外部类Activity即可。
    解决方案:
    把这个内部类声明为静态类,这时内部类将不再隐式持有外部类Activity的引用。此时内部类也无法使用外部类中的方法、变量(只能引用静态的方法和变量)

    问题:如何在防止内存泄漏的情况下,去访问外部类中的非静态方法、变量??
    解决办法:
    通过软引用或者弱引用的方式引用外部类Activity
    Java强引用,软引用,弱引用,虚引用

    1.2 Handler

    public class MainActivity extends AppCompatActivity {
    
        private static final int MESSAGE_DELAY = 0;
        private Button mButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mButton = (Button) findViewById(R.id.button);
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startDelayTask();
                }
            });
        }
    
        private void startDelayTask() {
            //发送一条消息,该消息会被延时10秒后才处理
            Message message = Message.obtain();
            message.obj = "按钮点击15秒后再弹出";
            message.what = MESSAGE_DELAY;
            mHandler.sendMessageDelayed(message, 15000);
        }
    
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MESSAGE_DELAY:
                        Toast.makeText(MainActivity.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
                        mButton.setText("延时修改了按钮的文本");
                        break;
                }
            }
        };
    }
    

    在sendMessageDelayed()发送消息时,延时时间过长,在中途退出Activity,会出现内存泄漏,出现的原因也是因为内部类持有外部类的引用。

    解决方案:
    1)将MyHandler声明为静态的
    2)将MyHandler作为一个独立的类,不作为内部类使用

    在onDestory()中移除所有的消息

     mHandler.removeCallbacksAndMessages(null);
    
    2、非静态内部类的静态实例

    非静态的内部类创建了一个静态实例。非静态内部类会持有外部类Activity的引用,后来又创建了一个这个内部类的静态实例。

    这个静态实例不会在Activity被关掉时一块被回收(静态实例的生命周期跟应用的生命周期一样长,Activity被销毁后,静态实例并不会被释放)。

    非静态内部类持有外部引用,而该内部类的静态实例不会及时回收,所以才导致了内存泄露。

    解决方案:
    将内部类申明为静态的内部类。

    3、Context导致内存泄露

    例如我们经常使用的工具类,需要传入Context对象,如果传入当前的Activity对象的话容易导致内存泄漏,因为工具类一般都是静态的,并且对其调用时机也不能很好的把控,这个时候需要传入该应用的Context来避免内存泄漏

    系统中的Context的具体实现子类有:Activity、Application、Service。
    虽然Context能做很多事,但并不是随便拿到一个Context实例就可以为所欲为,它的使用还是有一些规则限制的。在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。

    出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

    conext作用域.png

    上图中Application和Service所不推荐的两种使用情况:

    如果我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错

    javaandroid.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

    这是因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。所有这种用Application启动Activity的方式不推荐使用,Service的原因跟Application一致。

    在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。一句话总结:凡是跟UI相关的,都建议使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例Context都可以,当然了,注意Context引用的持有,防止内存泄漏。

    相关文章

      网友评论

          本文标题:Android常见的内存泄漏

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