美文网首页
Android 非静态内部类/匿名类/Handler引起的内存泄

Android 非静态内部类/匿名类/Handler引起的内存泄

作者: Rimson | 来源:发表于2018-10-30 17:27 被阅读0次

    一、内存溢出和内存泄漏

    • 内存溢出:Android系统会给每个安卓程序分配一定的内存,当程序所使用的内存超过最大值就会造成内存溢出,就是常说的OOM(Out Of Memory)
    • 内存泄漏:例如new了一个对象,这个对象是要消耗内存的,JVM会对没有引用的对象进行回收释放内存,如果一个对象已经没有引用了,但是JVM没有回收这个对象,就会造成内存泄漏,多次内存泄漏到最后就会变成内存溢出。
      我的理解:内存溢出相当于钱用完了;但是内存泄漏是把钱借出去,虽然算起来你还是有钱的,但是当你借出去比较多而大家都不还的时候,你就相当于没钱了。

    二、内存泄漏

    常见的内存泄漏:

    1. 非静态内部类/匿名内部类的静态实例容易造成内存泄漏
    2. 单例模式导致的内存泄漏
    3. 对该解注册、注销、清空的对象没有及时做这样操作导致的,比如说广播、服务、IO流等等。(其实我个人觉得这一条的最终原因还是第一条,因为说到底还是引用没有释放使JVM没有不能回收)

    三、举个例子

    public class MainActivity extends AppCompatActivity {
        private static Context context;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            context=this;
    
        }
    }
    

    此时static会警告:

    Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run) less... (Ctrl+F1)
    A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts. Issue id: StaticFieldLeak

    被static修饰的Context是持有MainActivity的引用的,它的生命周期是超过MainActivity的,并且这个本该被回收的Activty由于它还一直存在着,这就导致了内存泄漏。

    四、对于Handler

    public class MainActivity extends AppCompatActivity {
        private Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                //TODO
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            handler.sendMessage(new Message());
        }
    }
    

    Handler的生命周期与Activity不一致,非静态(匿名)内部类会默认隐性引用外部类对象,所以Handler对Activity的引用阻止了GC对Activity的回收。

    解决方法

    自定义静态类继承自Handler,静态类不持有外部类的对象,所以Activity可以随意被回收。
    但是使用了以上方法之后,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference),GC回收的时候会忽略弱引用。

    public class MainActivity extends AppCompatActivity {
        private Handler handler=new TestHandler(this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            handler.sendMessage(new Message());
        }
    
        static class TestHandler extends Handler{
            WeakReference<Activity> mActivityReference;
    
            TestHandler(Activity activity){
                mActivityReference=new WeakReference<>(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                final Activity activity=mActivityReference.get();
                if (activity!=null){
                    //TODO
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Android 非静态内部类/匿名类/Handler引起的内存泄

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