Android記憶體回收機制解決記憶體泄露問題_Android

來源:互聯網
上載者:User

在android編碼中,會有一些簡便的寫法和編碼習慣,會導致我們的代碼有很多記憶體泄露的問題,在這裡做一個已知錯誤的總結:
1、編寫單例的時候常出現的錯誤。

錯誤方式:

 public class Foo{   private static Foo foo;   private Context mContext;   private Foo(Context mContext){    this.mContext = mContext;   }   // 普通單例,非安全執行緒   public static Foo getInstance(Context mContext){    if(foo == null)     foo = new Foo(mContext);    return foo;   }   public void otherAction(){    mContext.xxxx();    ….   }  }

錯誤原因:

     如果我們在Activity A中或者其他地方使用Foo.getInstance()時,我們總是會順手寫一個『this』或者『mContext』(這個變數也是指向this)。試想一下,當前我們所用的Foo是單例,意味著被初始化後會一直存在與記憶體中,以方便我們以後調用的時候不會在此次建立Foo對象。但Foo中的『mContext』變數一直都會持有Activity A中的『Context』,導致Activity A即使執行了onDestroy方法,也不能夠將自己銷毀。但『applicationContext』就不同了,它一直伴隨著我們應用存在(中途也可能會被銷毀,但也會自動reCreate),所以就不用擔心Foo中的『mContext』會持有某Activity的引用,讓其無法銷毀。

正確方式:   

 public class Foo{   private static Foo foo;   private Context mContext;   private Foo(Context mContext){    this.mContext = mContext;   }   // 普通單例,非安全執行緒   public static Foo getInstance(Context mContext){    if(foo == null)     foo = new Foo(mContext.getApplicationContext());    return foo;   }   public void otherAction(){    mContext.xxxx();    ….   }  }  

2、使用匿名內部類的時候經常出現的錯誤

錯誤方式:

 public class FooActivity extends Activity{   private TextView textView;      private Handler handler = new Handler(){    @override    public void handlerMessage(Message msg){         }   };   @override   public void onCreate(Bundle bundle){    super.onCreate(bundle);    setContextView(R.layout.activity_foo_layout);        textView = (TextView)findViewById(R.id.textView);        handler.postDelayed(new Runnable(){     @override     public void run(){       textView.setText(“ok”);     };    },1000 * 60 * 10);   }  }

錯誤原因:

     當我們執行了FooActivity的finish方法,被延遲的訊息會在被處理之前存在於主線程訊息佇列中10分鐘,而這個訊息中又包含了Handler的引用,而Handler是一個匿名內部類的執行個體,其持有外面的FooActivity的引用,所以這導致了FooActivity無法回收,進而導致FooActivity持有的很多資源都無法回收,所以產生了記憶體泄露。

     注意上面的new Runnable這裡也是匿名內部類實現的,同樣也會持有FooActivity的引用,也會阻止FooActivity被回收。

     一個靜態匿名內部類執行個體不會持有外部類的引用。

正確方式:    

 public class FooActivity extends Activity{   private TextView textView;      private static class MyHandler extends Handler {   private final WeakReference<FooActivity> mActivity;   public MyHandler(FooActivity activity) {    mActivity = new WeakReference<FooActivity>(activity);   }   @Override   public void handleMessage(Message msg) {    FooActivity activity = mActivity.get();     if (activity != null) {       // ...     }    }   }   private final MyHandler handler = new MyHandler(this);   @override   public void onCreate(Bundle bundle){    super.onCreate(bundle);    setContextView(R.layout.activity_foo_layout);        textView = (TextView)findViewById(R.id.textView);        handler.postDelayed(new MyRunnable(textView),1000 * 60 * 10);   }   private static class MyRunnable implements Runnable{    private WeakReference<TextView> textViewWeakReference;        public MyRunnable(TextView textView){     textViewWeakReference = new WeakReference<TextView>(textView);    }     @override     public void run(){       final TextView textView = textViewWeakReference.get();       if(textView != null){        textView.setText("OK");       }     };   }  }  

3、在使用handler後,記得在onDestroy裡面handler.removeCallbacksAndMessages(object token);

 handler.removeCallbacksAndMessages(null);  // removeCallbacksAndMessages,當參數為null的時候,可以清除掉所有跟次handler相關的Runnable和Message,我們在onDestroy中調用次方法也就不會發生記憶體流失了。

開發中需要注意的點以免記憶體流失:

  •      1.不要讓生命週期長於Activity的對象持有到Activity的引用
  •      2.盡量使用Application的Context而不是Activity的Context
  •      3.盡量不要在Activity中使用非靜態內部類,因為非靜態內部類會隱式持有外部類執行個體的引用(具體可以查看細話Java:”失效”的private修飾符瞭解)。如果使用靜態內部類,將外部執行個體引用作為弱引用持有。
  •      4.記憶體回收不能解決記憶體泄露,瞭解Android中記憶體回收機制

擷取context的方法,以及使用上context和applicationContext的區別:

  •      1.View.getContext,返回當前View對象的Context對象,通常是當前正在展示的Activity對象。
  •      2.Activity.getApplicationContext,擷取當前Activity所在的(應用)進程的Context對象,通常我們使用Context對象時,要優先考慮這個全域的進程Context。
  •      3,ContextWrapper.getBaseContext():用來擷取一個ContextWrapper進行裝飾之前的Context,可以使用這個方法,這個方法在實際開發中使用並不多,也不建議使用。
  •      4.Activity.this 返回當前的Activity執行個體,如果是UI控制項需要使用Activity作為Context對象,但是預設的Toast實際上使用ApplicationContext也可以。

 大家注意看到有一些NO上添加了一些數字,其實這些從能力上來說是YES,但是為什麼說是NO呢?下面一個一個解釋:

     數字1:啟動Activity在這些類中是可以的,但是需要建立一個新的task。一般情況不推薦。

     數字2:在這些類中去layout inflate是合法的,但是會使用系統預設的主題樣式,如果你自訂了某些樣式可能不會被使用。

     數字3:在receiver為null時允許,在4.2或以上的版本中,用於擷取黏性廣播的當前值。(可以無視)

     註:ContentProvider、BroadcastReceiver之所以在上述表格中,是因為在其內部方法中都有一個context用於使用。

     好了,這裡我們看下錶格,重點看Activity和Application,可以看到,和UI相關的方法基本都不建議或者不可使用Application,並且,前三個操作基本不可能在Application中出現。實際上,只要把握住一點,凡是跟UI相關的,都應該使用Activity做為Context來處理;其他的一些操作,Service,Activity,Application等執行個體都可以,當然了,注意Context引用的持有,防止記憶體流失。

以上就是本文的全部內容,希望對大家的學習有所協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.