ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Android常见内存泄漏

2021-08-04 20:00:58  阅读:253  来源: 互联网

标签:泄漏 void private Override 内存 msg Android public


1、非静态内部类默认持有外部类的引用会导致内存泄漏

静态内部类与非静态内部类之间存在一个最大的区别,就是非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。 代码:

public class Outer {
    
    private void outerDo() {}
    
    class Inter {
        
        private void innerDo() {
            // 内部类可以直接访问外部类成员,原因在于隐式持有了一个外部类引用
            outerDo();
            // Outer.this 就是内部类隐式持有的外部类引用
            Outer.this.outerDo();
        }
    }
}

如果Inter的实例为静态的会导致内存泄漏。

解决方法:将Inter改成静态内部类

2、Handler持有当前类的Context对象,导致对象无法释放

例如:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start();
    }

    private void start() {
        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) {
                // 做相应逻辑
            }
        }
    };
}

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

解决办法:静态内部类+弱引用

例如:

public class MainActivity extends AppCompatActivity {

    private static class ParseHandler extends XyHandler<MainActivity> {

        private ParseHandler(MainActivity activity) {
            super(activity);
        }

        @Override
        protected void handleMessage(Message msg, MainActivity activity) {
            switch (msg.what) {
                case 0:
                    activity.doSomething();
                    break;
                default:
                    break;
            }
        }
    }

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new ParseHandler(this);
        start();
    }

    private void start() {
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }
    
    public void doSomething(){
        
    }
}

public abstract class XyHandler<T> extends Handler {

    private WeakReference<T> mWeak;

    public XyHandler(T t) {
        mWeak = new WeakReference<>(t);
    }

    @Override
    public void handleMessage(Message msg) {
        if (mWeak == null || mWeak.get() == null) {
            return;
        }
        handleMessage(msg, mWeak.get());
        super.handleMessage(msg);
    }

    protected abstract void handleMessage(Message msg, T t);

}

3、静态对象引用,无法释放对象导致内存泄漏

这种情况不常遇到,一般有些新手会犯这个错误。这种就是一些对象的引用是静态的,导致无法回收。
例如:

public class MainActivity extends AppCompatActivity {

    privite static MainActivity mMainActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMainActivity = this;
    }
}

还有自定义View或者ViewGroup中也是出现这种状况,导致内存无法释放,出现内存溢出。对于这种Context不能用静态引用,如果需要调用这些对象内的方法可以将对应方法代码提取出来。

4、线程内引用有生命周期的外部对象

线程一般是指Thread和AsyncTask
Thread,例如:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟相应耗时逻辑
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

AsyncTask,例如:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                // 模拟相应耗时逻辑
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.execute();
    }
}

我们在刚开始写代码的时候都是像上面一样写线程和异步任务,但是这种方式使用Thread和AsyncTask都是匿名内部类对象,默认持有外部类Activity的引用,在Activity回收时可能会有Thread和AsyncTask没有执行完的情况,所以可能会造成内存泄漏。

5、资源未关闭或者未释放造成的内存泄漏

IO流、File流或者数据库、Cursor等资源在使用完成后要及时关闭,如果没有及时关闭,会导致缓冲对象一直被占用,不能得到释放,发生内存泄漏。Bitmap未回收。

6、单例引用Context导致内存泄漏

例如:

public class AppSettings {

    private static AppSettings sInstance;
    private Context mContext;

    private AppSettings(Context context) {
        this.mContext = context;
    }

    public static AppSettings getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppSettings(context);
        }
        return sInstance;
    }
}

单例模式的生命周期会和整个应用的生命周期一样,如果在使用单例模式时传入的是Activity或者Service等声明周期短于Application的声明周期时,都会造成内存泄漏。因此我们传入的Context应该是Application的Context,这样生命周期就和Application的声明周期一样,避免造成内存泄漏

7、广播未及时注销造成内存泄漏

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);
    }
}

广播是非静态内部类,会持有Activity引用,而广播注册会将广播对象注册到系统内部,如果没有取消注册,那么系统中会存在Activity的应用,因此不能释放Activity,造成内存泄漏。
解决办法:就是在OnDestroy方法中取消注册广播。

8、集合类泄漏

集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。如果这个集合类是全局性的变量 (比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减。

9、WebView内存泄漏

原文里说的webview引起的内存泄漏主要是因为org.chromium.android_webview.AwContents 类中注册了component callbacks,但是未正常反注册而导致的。
org.chromium.android_webview.AwContents 类中有这两个方法 onAttachedToWindow 和 onDetachedFromWindow;系统会在attach和detach处进行注册和反注册component callback;
在onDetachedFromWindow() 方法的第一行中:

if (isDestroyed()) return;,

如果 isDestroyed() 返回 true 的话,那么后续的逻辑就不能正常走到,所以就不会执行unregister的操作;我们的activity退出的时候,都会主动调用 WebView.destroy() 方法,这会导致 isDestroyed() 返回 true;destroy()的执行时间又在onDetachedFromWindow之前,所以就会导致不能正常进行unregister()。
然后解决方法就是:让onDetachedFromWindow先走,在主动调用destroy()之前,把webview从它的parent上面移除掉。

ViewParent parent = mWebView.getParent();
if (parent != null) {
    ((ViewGroup) parent).removeView(mWebView);
}
mWebView.destroy();

完整的activity的onDestroy()方法:

@Override
protected void onDestroy() {
    if( mWebView!=null) {
        // 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
        // destory()
        ViewParent parent = mWebView.getParent();
        if (parent != null) {
            ((ViewGroup) parent).removeView(mWebView);
        }
        mWebView.stopLoading();
        // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
        mWebView.getSettings().setJavaScriptEnabled(false);
        mWebView.clearHistory();
        mWebView.clearView();
        mWebView.removeAllViews();
        mWebView.destroy();
    }
    super.on Destroy();
}

标签:泄漏,void,private,Override,内存,msg,Android,public
来源: https://blog.csdn.net/qq_40959750/article/details/119391658

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有