• Android 内存泄漏的一些情况。


    1. 静态 Activity

    2. 静态 View

    3. 非静态内部类

    4. 匿名类

    5. Handler

    6. Thread

    7. TimerTask

    8. SensorManager

    1.资源对象没关闭造成的内存泄漏

    2.构造Adapter时,没有使用缓存的convertView

    3.Bitmap对象不在使用时调用recycle()释放内存

    4.试着使用关于application的context来替代和activity相关的context

    5.注册没取消造成的内存泄漏

    6.集合中对象没清理造成的内存泄漏

     

     

     

    避免引用Context造成的内存泄露

    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    interface 放在fragment内或者放在外面单独存在,一般不会造成activity泄漏
    public interface DialogCallback {
    void showModifyInputDialog();
    void showTheMessageDialog();
    }



    其实一般的匿名内部类是不会导致activity释放不了的,只要你不在handle内进行奇怪的操作
    private Handler uiHandler = new Handler(){

    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

     

    内存泄漏全解析

    最近在维护代码,发现一个自定义View(这个View是在一个AsyncTask的工作线程doInBackground中新建的,在UI线程onPostExecute中添加进window中的)经常会泄漏内存,导致其引用的Activity一直得不到释放,每次退出再进去都会导致Activity的对象+1.

    package com.xxx.launcher.view;
    
    import android.content.Context;
    import android.util.Log;
    import android.view.View;
    
    public class WeatherTextView extends SkinTextView {
    
        public WeatherTextView (Context context) {
    super(context); postDelayed(mShowCityRunnable,
    200);//这一步有问题 } @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); if (visibility == View.VISIBLE) { post(mShowCityRunnable); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); onCancel(); }; public void onCancel(){ removeCallbacks(mShowCityRunnable); } private Runnable mShowCityRunnable = new Runnable() { @Override public void run() { Log.i("mShowCityRunnable-------TAG", "run"+mShowCityRunnable); setText(city); } }; }

    最后通过MAT工具查看内存快照的比较,发现了如下的情况,把内存泄露的地方锁定在了WeatherTextView$2的第二个内部类中mShowCityRunnable ,一开始始终都想不到这个内部类到底有什么地方泄露了,最后突然灵光一闪,是不是View的post()方法导致的,在网上一查,发现确实。

    public boolean post(Runnable action) {  
        Handler handler;  
        AttachInfo attachInfo = mAttachInfo;  
        if (attachInfo != null) {  
            handler = attachInfo.mHandler;  
        } else {  
            // Assume that post will succeed later  
            ViewRootImpl.getRunQueue().post(action);  
            return true;  
        }  
      
        return handler.post(action);  
    }  

    在post() 函数注释中,明确写着:This method can be invoked from outside of the UI thread only when this View is attached to a window.

    当View还没有attach到当前window时,mAttachInfo 值为 null,故而执行 else语句,再看一下getRunQueue()和其post() 方法:

    static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();  
      
    static RunQueue getRunQueue() {  
         RunQueue rq = sRunQueues.get();  
         if (rq != null) {  
             return rq;  
         }  
         rq = new RunQueue();  
         sRunQueues.set(rq);  
         return rq;  
     }  
     ……  
     static final class RunQueue {  
         private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();  
      
         void post(Runnable action) {  
             postDelayed(action, 0);  
         }  
      
         void postDelayed(Runnable action, long delayMillis) {  
             HandlerAction handlerAction = new HandlerAction();  
             handlerAction.action = action;  
             handlerAction.delay = delayMillis;  
      
             synchronized (mActions) {  
                 mActions.add(handlerAction);  
             }  
         }  
            
         void executeActions(Handler handler) {  
             synchronized (mActions) {  
                 final ArrayList<handleraction> actions = mActions;  
                 final int count = actions.size();  
      
                 for (int i = 0; i < count; i++) {  
                     final HandlerAction handlerAction = actions.get(i);  
                     handler.postDelayed(handlerAction.action, handlerAction.delay);  
                 }  
      
                 actions.clear();  
             }  
         }  
         ……  
     }  

    这样会把Runnable 插入到一个静态的ThreadLocal的RunQueue队列里(在工作线程中post,就会插入工作线程的RunQueue队列),针对本文开头给出的例子,那么插入的Runnable什么时候得到执行呢?

    调用RunQueue.executeActions()方法只有一处,即在ViewRootImpl类的如下非静态方法中

    private void performTraversals() {  
          
            if (mLayoutRequested && !mStopped) {  
                // Execute enqueued actions on every layout in case a view that was detached  
                // enqueued an action after being detached  
                getRunQueue().executeActions(attachInfo.mHandler);  
            }  
    }  

    该方法是在UI线程执行的(见ViewRootImpl.handleMessage()), 故当UI线程执行到该performTraversals() 里的 getRunQueue() 时,得到的是UI线程中的RunQueue,这样AsyncTask 线程中的 RunQueue永远不会被执行到, 并且AsyncTask的是用线程池实现的,AsyncTask启动的线程会长期存在,造成如下引用关系:

     

    AsyncTask线程 => 静态的ThreadLocal的RunQueue => Runnable => View=> Activity;

    如此即使activity finish 了,确始终存在一个静态引用链引用这该activity,而 Activity一般又引用着很多资源,比如图片等,最终造成严重资源泄漏。

    最后我是写改成

    package com.xxx.launcher.view;
    
    import android.content.Context;
    import android.util.Log;
    import android.view.View;
    
    public class WeatherTextView  extends SkinTextView {
    
        public WeatherTextView (Context context) {
            super(context);
        }
        
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            postDelayed(mShowCityRunnable, 200); //在onAttachedToWindow方法中执行post方法
        }
        
        @Override
        protected void onWindowVisibilityChanged(int visibility) {
            super.onWindowVisibilityChanged(visibility);    
            if (visibility == View.VISIBLE) {
                post(mShowCityRunnable);
            }
        }
        
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            onCancel();
        };
        
        public void onCancel(){
            removeCallbacks(mShowCityRunnable);
        }
        private Runnable mShowCityRunnable = new Runnable() {
    
            @Override
            public void run() {
                Log.i("mShowCityRunnable-------TAG", "run"+mShowCityRunnable);
                setText(city);
            }
        };
    }

    这样Activity就没有再被其他东西引用了,就不会发生Activity的泄漏了,Activity就可以被释放了。这样,不管进入退出进入这个MainMenuActivity多少次,MainMenuActivity的对象就只会保存一份。

    ps:至于为什么在两个Histogram(直方图)的比较图中还是显示MainMenuActivity+1,则是因为这是类名,类被加载之后,在进程结束之前不会被回收

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    ===============================================================================================================================

    ===============================================================================================================================

     

    这种泄漏一般是因为mStorageManager 注册了但是没有取消注册

    mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
    mStorageManager.registerListener(mStoragelistener);

    取消注册就可以了

    if (mStorageManager != null) {
        mStorageManager.unregisterListener(mStoragelistener);
    }

     

  • 相关阅读:
    书写高效的CSS
    _blank开新窗口不符合标准?
    IE6支持PNG透明(alpha通道)的4种方法
    jQuery插件支持天干地支阴历阳历万年历节假日红字显示记事等功能的日历插件(1)
    讓你的windowsXP支持四桌面,類似Ubuntu的效果
    MySql语句常见操作创建数据库,选择数据库,创建表,数据库中文乱码;
    解决<pre>标签里的文本换行(兼容IE, FF和Opera等)
    使用jquyer擴展方法定義屬於自己的氣泡提示
    jQuery插件通用input或textarea靜態ajax修改功能插件
    SEO的经验
  • 原文地址:https://www.cnblogs.com/lipeineng/p/5829848.html
Copyright © 2020-2023  润新知