欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Android應用的內存優(yōu)化及Handler的內存泄漏問題

 更新時間:2016年02月01日 16:23:18   作者:iam_wingjay  
這篇文章主要介紹了Android應用的內存優(yōu)化及Handler的內存泄漏問題,文中對Activity無法被回收而造成的內存泄漏給出了通常的解決方案,需要的朋友可以參考下

一、Android內存基礎
物理內存與進程內存
物理內存即移動設備上的RAM,當啟動一個Android程序時,會啟動一個Dalvik VM進程,系統(tǒng)會給它分配固定的內存空間(16M,32M不定),這塊內存空間會映射到RAM上某個區(qū)域。然后這個Android程序就會運行在這塊空間上。Java里會將這塊空間分成Stack棧內存和Heap堆內存。stack里存放對象的引用,heap里存放實際對象數據。
在程序運行中會創(chuàng)建對象,如果未合理管理內存,比如不及時回收無效空間就會造成內存泄露,嚴重的話可能導致使用內存超過系統(tǒng)分配內存,即內存溢出OOM,導致程序卡頓甚至直接退出。

內存泄露(Memory Leak)
Java內存泄漏指的是進程中某些對象(垃圾對象)已經沒有使用價值了,但是它們卻可以直接或間接地引用到gc roots導致無法被GC回收。Dalvik VM具備的GC機制(垃圾回收機制)會在內存占用過多時自動回收,嚴重時會造成內存溢出OOM。

內存溢出OOM
當應用程序申請的java heap空間超過Dalvik VM HeapGrowthLimit時,溢出。
注意:OOM并不代表內存不足,只要申請的heap超過Dalvik VM HeapGrowthLimit時,即使內存充足也會溢出。效果是能讓較多進程常駐內存。

如果RAM不足時系統(tǒng)會做什么?
Android的Memory Killer會殺死優(yōu)先級較低的進程,讓高優(yōu)先級進程獲取更多內存。

Android系統(tǒng)默認內存回收機制

進程優(yōu)先級:Foreground進程、Visible進程、Service進程、Background進程、Empty進程;
如果用戶按Home鍵返回桌面,那么該app成為Background進程;如果按Back返回,則成為Empty進程
ActivityManagerService直接管理所有進程的內存資源分配。所有進程要申請或釋放內存都需要通過ActivityManagerService對象。
垃圾回收不定期執(zhí)行。當內存不夠時就會遍歷heap空間,把垃圾對象刪除。
堆內存越大,則GC的時間更長

201621162057205.png (439×249)

二、優(yōu)化
Bitmap優(yōu)化
Bitmap非常消耗內存,而且在Android中,讀取bitmap時, 一般分配給虛擬機的圖片堆棧只有8M,所以經常造成OOM問題。所以有必要針對Bitmap的使用作出優(yōu)化:

圖片顯示:加載合適尺寸的圖片,比如顯示縮略圖的地方不要加載大圖。
圖片回收:使用完bitmap,及時使用Bitmap.recycle()回收。
問題:Android不是自身具備垃圾回收機制嗎?此處為何要手動回收。
Bitmap對象不是new生成的,而是通過BitmapFactory生產的。而且通過源碼可發(fā)現是通過調用JNI生成Bitmap對象(nativeDecodeStream()等方法)。所以,加載bitmap到內存里包括兩部分,Dalvik內存和Linux kernel內存。前者會被虛擬機自動回收。而后者必須通過recycle()方法,內部調用nativeRecycle()讓linux kernel回收。
捕獲OOM異常:程序中設定如果發(fā)生OOM的應急處理方式。
圖片緩存:內存緩存、硬盤緩存等
圖片壓縮:直接使用ImageView顯示Bitmap時會占很多資源,尤其當圖片較大時容易發(fā)生OOM??梢允褂肂itMapFactory.Options對圖片進行壓縮。
圖片像素:android默認顏色模式為ARGB_8888,顯示質量最高,占用內存最大。若要求不高時可采用RGB_565等模式。圖片大小:圖片長度*寬度*單位像素所占據字節(jié)數
ARGB_4444:每個像素占用2byte內存
ARGB_8888:每個像素占用4byte內存 (默認)
RGB_565:每個像素占用2byte內存
對象引用類型

強引用 strong:Object object=new Object()。當內存不足時,Java虛擬機寧愿拋出OOM內存溢出異常,也不會輕易回收強引用對象來解決內存不足問題;
軟引用 soft:只有當內存達到某個閾值時才會去回收,常用于緩存;
弱引用 weak :只要被GC線程掃描到了就進行回收;
虛引用
如果想要避免OOM發(fā)生,則使用軟引用對象,即當內存快不足時進行回收;如果想盡快回收某些占用內存較大的對象,例如bitmap,可以使用弱引用,能被快速回收。不過如果要對bitmap作緩存就不要使用弱引用,因為很快就會被GC回收,導致緩存失敗。
關于java對象引用類型,具體可參加本人另一篇文章
池 pool

對象池:如果某個對象在創(chuàng)建時,需要較大的資源開銷,那么可以將其放入對象池,即將對象保存起來,下次需要時直接取出使用,而不用再次創(chuàng)建對象。當然,維護對象池也需要一定開銷,故要衡量。
線程池:與對象池差不多,將線程對象放在池中供反復使用,減少反復創(chuàng)建線程的開銷。

三、Handler內存泄漏分析及解決
1、介紹
首先,請瀏覽下面這段handler代碼:

public class SampleActivity extends Activity {
 private final Handler mLeakyHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   // ... 
  }
 }
}

在使用handler時,這是一段很常見的代碼。但是,它卻會造成嚴重的內存泄漏問題。在實際編寫中,我們往往會得到如下警告:

  In Android, Handler classes should be static or leaks might occur.
那么,handler是如何造成內存泄漏的呢?

2、分析
(1)、Android角度
當Android應用程序啟動時,framework會為該應用程序的主線程創(chuàng)建一個Looper對象。這個Looper對象包含一個簡單的消息隊列Message Queue,并且能夠循環(huán)的處理隊列中的消息。這些消息包括大多數應用程序framework事件,例如Activity生命周期方法調用、button點擊等,這些消息都會被添加到消息隊列中并被逐個處理。
另外,主線程的Looper對象會伴隨該應用程序的整個生命周期。

然后,當主線程里,實例化一個Handler對象后,它就會自動與主線程Looper的消息隊列關聯(lián)起來。所有發(fā)送到消息隊列的消息Message都會擁有一個對Handler的引用,所以當Looper來處理消息時,會據此回調[Handler#handleMessage(Message)](http://developer.android.com/reference/android/os/Handler.html#handleMessage(android.os.Message)方法來處理消息。

(2)、Java角度
在java里,非靜態(tài)內部類 和 匿名類 都會潛在的引用它們所屬的外部類。但是,靜態(tài)內部類卻不會。

(3)、泄漏來源
請瀏覽下面一段代碼:

public class SampleActivity extends Activity {

 private final Handler mLeakyHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   // ...
  }
 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Post a message and delay its execution for 10 minutes.
  mLeakyHandler.postDelayed(new Runnable() {
   @Override
   public void run() { /* ... */ }
  }, 1000 * 60 * 10);

  // Go back to the previous Activity.
  finish();
 }
}

當activity結束(finish)時,里面的延時消息在得到處理前,會一直保存在主線程的消息隊列里持續(xù)10分鐘。而且,由上文可知,這條消息持有對handler的引用,而handler又持有對其外部類(在這里,即SampleActivity)的潛在引用。這條引用關系會一直保持直到消息得到處理,從而,這阻止了SampleActivity被垃圾回收器回收,同時造成應用程序的泄漏。
注意,上面代碼中的Runnable類--非靜態(tài)匿名類--同樣持有對其外部類的引用。從而也導致泄漏。

3、泄漏解決方案
首先,上面已經明確了內存泄漏來源:

只要有未處理的消息,那么消息會引用handler,非靜態(tài)的handler又會引用外部類,即Activity,導致Activity無法被回收,造成泄漏;
Runnable類屬于非靜態(tài)匿名類,同樣會引用外部類。
為了解決遇到的問題,我們要明確一點:靜態(tài)內部類不會持有對外部類的引用。所以,我們可以把handler類放在單獨的類文件中,或者使用靜態(tài)內部類便可以避免泄漏。
另外,如果想要在handler內部去調用所在的外部類Activity,那么可以在handler內部使用弱引用的方式指向所在Activity,這樣統(tǒng)一不會導致內存泄漏。
對于匿名類Runnable,同樣可以將其設置為靜態(tài)類。因為靜態(tài)的匿名類不會持有對外部類的引用。

public class SampleActivity extends Activity {

 /**
  * Instances of static inner classes do not hold an implicit
  * reference to their outer class.
  */
 private static class MyHandler extends Handler {
  private final WeakReference<SampleActivity> mActivity;

  public MyHandler(SampleActivity activity) {
   mActivity = new WeakReference<SampleActivity>(activity);
  }

  @Override
  public void handleMessage(Message msg) {
   SampleActivity activity = mActivity.get();
   if (activity != null) {
    // ...
   }
  }
 }

 private final MyHandler mHandler = new MyHandler(this);

 /**
  * Instances of anonymous classes do not hold an implicit
  * reference to their outer class when they are "static".
  */
 private static final Runnable sRunnable = new Runnable() {
   @Override
   public void run() { /* ... */ }
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Post a message and delay its execution for 10 minutes.
  mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

  // Go back to the previous Activity.
  finish();
 }
}

4、小結
雖然靜態(tài)類與非靜態(tài)類之間的區(qū)別并不大,但是對于Android開發(fā)者而言卻是必須理解的。至少我們要清楚,如果一個內部類實例的生命周期比Activity更長,那么我們千萬不要使用非靜態(tài)的內部類。最好的做法是,使用靜態(tài)內部類,然后在該類里使用弱引用來指向所在的Activity。

相關文章

  • Android實現多維商品屬性SKU選擇

    Android實現多維商品屬性SKU選擇

    這篇文章主要為大家詳細介紹了Android實現多維商品屬性SKU選擇,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 淺談Android 指紋解鎖技術

    淺談Android 指紋解鎖技術

    這篇文章主要介紹了淺談Android 指紋解鎖技術,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 解決EditText不顯示光標的三種方法(總結)

    解決EditText不顯示光標的三種方法(總結)

    下面小編就為大家?guī)硪黄鉀QEditText不顯示光標的三種方法(總結)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Android復選框對話框用法實例簡析

    Android復選框對話框用法實例簡析

    這篇文章主要介紹了Android復選框對話框用法,結合實例形式簡單分析了Android復選對話框的創(chuàng)建與使用技巧,需要的朋友可以參考下
    2016-01-01
  • android getActivity.findViewById獲取ListView 返回NULL的方法

    android getActivity.findViewById獲取ListView 返回NULL的方法

    下面小編就為大家?guī)硪黄猘ndroid getActivity.findViewById獲取ListView 返回NULL的方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • Android RxJava異步數據處理庫使用詳解

    Android RxJava異步數據處理庫使用詳解

    RxJava是一種異步數據處理庫,也是一種擴展的觀察者模式。對于Android開發(fā)者來說,使用RxJava時也會搭配RxAndroid,它是RxJava針對Android平臺的一個擴展,用于Android 開發(fā),它提供了響應式擴展組件,使用RxAndroid的調度器可以解決Android多線程問題
    2022-11-11
  • Kotlin?掛起函數CPS轉換原理解析

    Kotlin?掛起函數CPS轉換原理解析

    這篇文章主要為大家介紹了Kotlin?掛起函數CPS轉換原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • android之SeekBar控件用法詳解

    android之SeekBar控件用法詳解

    下面小編就為大家?guī)硪黄猘ndroid之SeekBar控件用法詳解。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • Android 路徑查詢具體實現

    Android 路徑查詢具體實現

    可以通過RasterMap的getDirection()方法來查詢路徑,和查詢地址類似,路徑查詢的結果也是通過回調函數的方式來通知應用程序的,下面的例子返回南京到北京的路徑
    2013-10-10
  • Android之RecyclerView實現時光軸效果示例

    Android之RecyclerView實現時光軸效果示例

    本篇文章主要介紹了Android之RecyclerView實現時光軸效果,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02

最新評論