Android的bitmap和優化

来源:http://www.cnblogs.com/sjm19910902/archive/2017/02/10/6386684.html
-Advertisement-
Play Games

記憶體管理是個永恆的話題! 記憶體溢出:就是分配的記憶體不足以放下數據項序列。如在一個域中輸入的數據超過了它的要求就會引發數據溢出問題,多餘的數據就可以作為指令在電腦上運行。就是你要求分配的記憶體超出了系統能給你的,系統不能滿足需求,於是產生溢出 記憶體泄漏:是指在堆上分配的記憶體沒有被釋放,從而失去對其控制 ...


記憶體管理是個永恆的話題!

記憶體溢出:就是分配的記憶體不足以放下數據項序列。如在一個域中輸入的數據超過了它的要求就會引發數據溢出問題,多餘的數據就可以作為指令在電腦上運行。就是你要求分配的記憶體超出了系統能給你的,系統不能滿足需求,於是產生溢出 記憶體泄漏:是指在堆上分配的記憶體沒有被釋放,從而失去對其控制。這樣會造成程式能使用的記憶體越來越少,導致系統運行速度減慢,嚴重情況會使程式宕掉。

1.在Android應用里,最耗費記憶體的就是圖片資源。而且在Android系統中,讀取點陣圖Bitmap時,分給虛擬機中的圖片的堆棧大小隻有    8M,如果超出了,就會出現OutOfMemory異常。
1) 要及時回收Bitmap的記憶體
Bitmap類有一個方法recycle(),從方法名可以看出意思是回收。這裡就有疑問了,Android系統有自己的垃圾回收機制,可以不定期的回收掉不使用的記憶體空間,當然也包括Bitmap的空間。那為什麼還需要這個方法呢?
Bitmap類的構造方法都是私有的,所以開發者不能直接new出一個Bitmap對象,只能通過BitmapFactory類的各種靜態方法來實例化一個Bitmap。仔細查看BitmapFactory的源代碼可以看到,生成Bitmap對象最終都是通過JNI調用方式實現的。所以,載入Bitmap到記憶體里以後,是包含兩部分記憶體區域的。簡單的說,一部分是Java部分的,一部分是C部分的。這個Bitmap對象是由Java部分分配的,不用的時候系統就會自動回收了,但是那個對應的C可用的記憶體區域,虛擬機是不能直接回收的,這個只能調用底層的功能釋放。所以需要調用recycle()方法來釋放C部分的記憶體。從Bitmap類的源代碼也可以看到,recycle()方法里也的確是調用了JNI方法了。
那如果不調用recycle(),是否就一定存在記憶體泄露呢?也不是的。Android的每個應用都運行在獨立的進程里,有著獨立的記憶體,如果整個進程被應用本身或者系統殺死了,記憶體也就都被釋放掉了,當然也包括C部分的記憶體。
Android對於進程的管理是非常複雜的。簡單的說,Android系統的進程分為幾個級別,系統會在記憶體不足的情況下殺死一些低優先順序的進程,以提供給其它進程充足的記憶體空間。在實際項目開發過程中,有的開發者會在退出程式的時候使用Process.killProcess(Process.myPid())的方式將自己的進程殺死,但是有的應用僅僅會使用調用Activity.finish()方法的方式關閉掉所有的Activity。
 
經驗分享:
Android手機的用戶,根據習慣不同,可能會有兩種方式退出整個應用程式:一種是按Home鍵直接退到桌面;另一種是從應用程式的退出按鈕或者按Back鍵退出程式。那麼從系統的角度來說,這兩種方式有什麼區別呢?按Home鍵,應用程式並沒有被關閉,而是成為了後臺應用程式。按Back鍵,一般來說,應用程式關閉了,但是進程並沒有被殺死,而是成為了空進程(程式本身對退出做了特殊處理的不考慮在內)。
Android系統已經做了大量進程管理的工作,這些已經可以滿足用戶的需求。個人建議,應用程式在退出應用的時候不需要手動殺死自己所在的進程。對於應用程式本身的進程管理,交給Android系統來處理就可以了。應用程式需要做的,是儘量做好程式本身的記憶體管理工作。
 
一般來說,如果能夠獲得Bitmap對象的引用,就需要及時的調用Bitmap的recycle()方法來釋放Bitmap占用的記憶體空間,而不要等Android系統來進行釋放。
下麵是釋放Bitmap的示例代碼片段。
// 先判斷是否已經回收
if(bitmap != null && !bitmap.isRecycled()){ 
        // 回收並且置為null
        bitmap.recycle(); 
        bitmap = null; 

System.gc();
從上面的代碼可以看到,bitmap.recycle()方法用於回收該Bitmap所占用的記憶體,接著將bitmap置空,最後使用System.gc()調用一下系統的垃圾回收器進行回收,可以通知垃圾回收器儘快進行回收。這裡需要註意的是,調用System.gc()並不能保證立即開始進行回收過程,而只是為了加快回收的到來。
如何調用recycle()方法進行回收已經瞭解了,那什麼時候釋放Bitmap的記憶體比較合適呢?一般來說,如果代碼已經不再需要使用Bitmap對象了,就可以釋放了。釋放記憶體以後,就不能再使用該Bitmap對象了,如果再次使用,就會拋出異常。所以一定要保證不再使用的時候釋放。比如,如果是在某個Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中進行回收。
2) 捕獲異常
因為Bitmap是吃記憶體大戶,為了避免應用在分配Bitmap記憶體的時候出現OutOfMemory異常以後Crash掉,需要特別註意實例化Bitmap部分的代碼。通常,在實例化Bitmap的代碼中,一定要對OutOfMemory異常進行捕獲。
以下是代碼示例。
Bitmap bitmap = null;
try {
    // 實例化Bitmap
    bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
    //
}
if (bitmap == null) {
    // 如果實例化失敗 返回預設的Bitmap對象
    return defaultBitmapMap;
}
這裡對初始化Bitmap對象過程中可能發生的OutOfMemory異常進行了捕獲。如果發生了OutOfMemory異常,應用不會崩潰,而是得到了一個預設的Bitmap圖。
 
經驗分享:
    很多開發者會習慣性的在代碼中直接捕獲Exception。但是對於OutOfMemoryError來說,這樣做是捕獲不到的。因為OutOfMemoryError是一種Error,而不是Exception。在此僅僅做一下提醒,避免寫錯代碼而捕獲不到OutOfMemoryError。
3) 緩存通用的Bitmap對象
有時候,可能需要在一個Activity里多次用到同一張圖片。比如一個Activity會展示一些用戶的頭像列表,而如果用戶沒有設置頭像的話,則會顯示一個預設頭像,而這個頭像是位於應用程式本身的資源文件中的。
如果有類似上面的場景,就可以對同一Bitmap進行緩存。如果不進行緩存,儘管看到的是同一張圖片文件,但是使用BitmapFactory類的方法來實例化出來的Bitmap,是不同的Bitmap對象。緩存可以避免新建多個Bitmap對象,避免記憶體的浪費。
 
經驗分享:
    Web開發者對於緩存技術是很熟悉的。其實在Android應用開發過程中,也會經常使用緩存的技術。這裡所說的緩存有兩個級別,一個是硬碟緩存,一個是記憶體緩存。比如說,在開髮網絡應用過程中,可以將一些從網路上獲取的數據保存到SD卡中,下次直接從SD卡讀取,而不從網路中讀取,從而節省網路流量。這種方式就是硬碟緩存。再比如,應用程式經常會使用同一對象,也可以放到記憶體中緩存起來,需要的時候直接從記憶體中讀取。這種方式就是記憶體緩存。
4) 壓縮圖片
如果圖片像素過大,使用BitmapFactory類的方法實例化Bitmap的過程中,需要大於8M的記憶體空間,就必定會發生OutOfMemory異常。這個時候該如何處理呢?如果有這種情況,則可以將圖片縮小,以減少載入圖片過程中的記憶體的使用,避免異常發生。
使用BitmapFactory.Options設置inSampleSize就可以縮小圖片。屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一。即如果這個值為2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片的大小就為原始大小的1/4。
如果知道圖片的像素過大,就可以對其進行縮小。那麼如何才知道圖片過大呢?
使用BitmapFactory.Options設置inJustDecodeBounds為true後,再使用decodeFile()等方法,並不會真正的分配空間,即解碼出來的Bitmap為null,但是可計算出原始圖片的寬度和高度,即options.outWidth和options.outHeight。通過這兩個值,就可以知道圖片是否過大了。

    BitmapFactory.Options opts = new BitmapFactory.Options();
    // 設置inJustDecodeBounds為true
    opts.inJustDecodeBounds = true;
    // 使用decodeFile方法得到圖片的寬和高
    BitmapFactory.decodeFile(path, opts);
    // 列印出圖片的寬和高
    Log.d("example", opts.outWidth + "," + opts.outHeight);
在實際項目中,可以利用上面的代碼,先獲取圖片真實的寬度和高度,然後判斷是否需要跑縮小。如果不需要縮小,設置inSampleSize的值為1。如果需要縮小,則動態計算並設置inSampleSize的值,對圖片進行縮小。需要註意的是,在下次使用BitmapFactory的decodeFile()等方法實例化Bitmap對象前,別忘記將opts.inJustDecodeBound設置回false。否則獲取的bitmap對象還是null。
 
經驗分享:
如果程式的圖片的來源都是程式包中的資源,或者是自己伺服器上的圖片,圖片的大小是開發者可以調整的,那麼一般來說,就只需要註意使用的圖片不要過大,並且註意代碼的質量,及時回收Bitmap對象,就能避免OutOfMemory異常的發生。
如果程式的圖片來自外界,這個時候就特別需要註意OutOfMemory的發生。一個是如果載入的圖片比較大,就需要先縮小;另一個是一定要捕獲異常,避免程式Crash。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前面的話 history對象保存著用戶上網的歷史記錄,從視窗被打開的那一刻算起。由於安全方面的考慮,開發人員無法得到用戶瀏覽器的URL,但藉由用戶訪問過的頁面列表,可以在不知道實際URL的情況下實現後退和前進。本文將詳細介紹BOM中的history對象 length history.length屬性 ...
  • 前言 今天上推特看見這篇文章,點進去發現是新貨。 正好最近想入Node的坑,又有一些Java基礎,所以希望翻譯出來給大家,同時也讓自己加深理解。 才疏學淺,如有不妥之處請指正。 原文鏈接: "Node for Java Developers" 這個 "Node University" 貌似也提供一些 ...
  • http://zhangzhaoaaa.iteye.com/blog/2123021 今天剛打個一個技術群,裡面有人問標題上的問題,嘿,我恰好遇過,現在大家至少也在用jquery1.9以上的版本,ajaxfileupload的版本早就不更新了,大家可以下載看:地址這裡,它例子里使用的Jquery是1 ...
  • 一、Unicode的一個小特性首先,Unicode規定了許多code point,每一個code point表示一個字元。如\u0033表示字元“3”,\u864e表示字元“你”。反過來,不是每一個字元都對應一個code point,每一個字元也不止有一個code point的表示方法。比如說,“�... ...
  • MainActivity.java main_activity.xml ...
  • 1.在代碼中添加這麼一行代碼 this.requestWindowFeature(Window.FEATURE_NO_TITLE); 註意:這段代碼需要寫在setContentView()之前,否則會報錯。還有一點,當你自己的Activity繼承自AppCompatActivity時,這種方法貌似並 ...
  • 服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都 可以啟動Service,但是它們的使用場合有所不同。使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服 務仍然運 ...
  • //懶載入 //頂部需要拉伸自定義視圖 lazy var headView:MyHeaderView = { //let hframe = CGRect(x: 0, y: 0, width: swidth, height: swidth/self.imgRation) // let hview = ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...