Android在加載大背景圖或者大量圖片時,常常致使內存溢出(Out of Memory Error),本文根據我處理這些問題的經歷及其它開發者的經驗,整理解決方案以下(部分代碼及文字出處沒法考證):
方案1、讀取圖片時注意方法的調用,適當壓縮 儘可能不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設置一張大圖,由於這些函數在完成decode後,最終都是經過java層的createBitmap來完成的,須要消耗更多內存。 所以,改用先經過BitmapFactory.decodeStream方法,建立出一個bitmap,再將其設爲ImageView的 source,decodeStream最大的祕密在於其直接調用JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap,從而節省了java層的空間。html
InputStream is = this.getResources().openRawResource(R.drawable.pic1);java
BitmapFactory.Options options = new BitmapFactory.Options();android
options.inJustDecodeBounds = false;算法
options.inSampleSize = 10; // width,hight設爲原來的十分一app
Bitmap btp = BitmapFactory.decodeStream(is, null, options);函數
若是在讀取時加上圖片的Config參數,能夠跟有效減小加載的內存,從而跟有效阻止拋out of Memory異常。性能
/**優化
* 以最省內存的方式讀取本地資源的圖片ui
* @param contextthis
* @param resId
* @return
*/
public static Bitmap readBitMap(Context context, int resId){
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 獲取資源圖片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
另外,decodeStream直接拿圖片來讀取字節碼, 不會根據機器的各類分辨率來自動適應,使用了decodeStream以後,須要在hdpi和mdpi,ldpi中配置相應的圖片資源, 不然在不一樣分辨率機器上都是一樣大小(像素點數量),顯示出來的大小就不對了。
方案2、在適當的時候及時回收圖片佔用的內存 一般Activity或者Fragment在onStop/onDestroy時候就能夠釋放圖片資源:
if(imageView != null && imageView.getDrawable() != null){
Bitmap oldBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
imageView.setImageDrawable(null);
if(oldBitmap != null){
oldBitmap.recycle();
oldBitmap = null;
}
}
// Other code.
System.gc();
在釋放資源時,須要注意釋放的Bitmap或者相關的Drawable是否有被其它類引用。若是正常的調用,能夠經過Bitmap.isRecycled()方法來判斷是否有被標記回收;而若是是被UI線程的界面相關代碼使用,就須要特別當心避免回收有可能被使用的資源,否則有可能拋出系統異常: E/AndroidRuntime: java.lang.IllegalArgumentException: Cannot draw recycled bitmaps 而且該異常沒法有效捕捉並處理。
方案3、沒必要要的時候避免圖片的完整加載 只須要知道圖片大小的情形下,能夠不完整加載圖片到內存。 在使用BitmapFactory壓縮圖片的時候,BitmapFactory.Options設置inJustDecodeBounds爲true後,再使用decodeFile()等方法,能夠在不分配空間狀態下計算出圖片的大小。示例:
BitmapFactory.Options opts = new BitmapFactory.Options();
// 設置inJustDecodeBounds爲false
opts.inJustDecodeBounds = false;
// 使用decodeFile方法獲得圖片的寬和高
BitmapFactory.decodeFile(path, opts);
// 打印出圖片的寬和高
Log.d("example", opts.outWidth + "," + opts.outHeight);
(ps:原理其實就是經過圖片的頭部信息讀取圖片的基本信息)
方案4、優化Dalvik虛擬機的堆內存分配 堆(HEAP)是VM中佔用內存最多的部分,一般是動態分配的。堆的大小不是一成不變的,一般有一個分配機制來控制它的大小。好比初始的HEAP是4M大,當4M的空間被佔用超過75%的時候,從新分配堆爲8M大;當8M被佔用超過75%,分配堆爲16M大。倒過來,當16M的堆利用不足30%的時候,縮減它的大小爲8M大。從新設置堆的大小,尤爲是壓縮,通常會涉及到內存的拷貝,因此變動堆的大小對效率有不良影響。 Heap Utilization是堆的利用率。當實際的利用率偏離這個百分比的時候,虛擬機會在GC的時候調整堆內存大小,讓實際佔用率向個百分比靠攏。使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法能夠加強程序堆內存的處理效率。
private final static float TARGET_HEAP_UTILIZATION = 0.75f;
// 在程序onCreate時就能夠調用
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
方案5、自定義堆(Heap)內存大小 對於一些Android項目,影響性能瓶頸的主要是Android本身內存管理機制問題,目前手機廠商對RAM都比較吝嗇,對於軟件的流暢性來講RAM對性能的影響十分敏感,除了優化Dalvik虛擬機的堆內存分配外,咱們還能夠強制定義本身軟件的對內存大小,咱們使用Dalvik提供的 dalvik.system.VMRuntime類來設置最小堆內存爲例:
private final static int CWJ_HEAP_SIZE = 6 * 1024 * 1024 ;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); // 設置最小heap內存爲6MB大小。
可是上面方法仍是存在問題,函數setMinimumHeapSize其實只是改變了堆的下限值,它能夠防止過於頻繁的堆內存分配,當設置最小堆內存大小超過上限值(Max Heap Size)時仍然採用堆的上限值,對於內存不足沒什麼做用。
最後介紹一下圖片佔用進程的內存算法。android中處理圖片的基礎類是Bitmap,顧名思義,就是位圖。佔用內存的算法如:圖片的width*height*Config。 若是Config設置爲ARGB_8888,那麼上面的Config就是4。一張480*320的圖片佔用的內存就是480*320*4 byte。 在默認狀況下android進程的內存佔用量爲16M,由於Bitmap他除了java中持有數據外,底層C++的 skia圖形庫還會持有一個SKBitmap對象,所以通常圖片佔用內存推薦大小應該不超過8M。這個能夠調整,編譯源代碼時能夠設置參數。
參考資料:http://www.tuicool.com/articles/yemM7zf
方案六:在Manifest.xml文件裏面的<application 裏面添加Android:largeHeap="true"
簡單粗暴。這種方法容許應用須要耗費手機不少的內存空間,但倒是最快捷的解決辦法
https://www.jianshu.com/p/ab4a7e353076
若是一個無用對象(不須要再使用的對象)仍然被其餘對象持有引用,形成該對象沒法被系統回收,以至該對象在堆中所佔用的內存單元沒法被釋放而形成內存空間浪費,這中狀況就是內存泄露。
垃圾回收的機制主要是看對象是否有引用指向該對象。
java對象的引用包括:強(引用),弱(引用),軟(引用),虛(引用)
https://www.cnblogs.com/huajiezh/p/5835618.html
我理解的對象的引用和垃圾回收:
1,GC回收對象時,一看有強引用指向了這個對象,你強,我不吃(不回收)。
2,GC回收對象時,一看有弱引用指向了這個對象,你弱,我吃(回收)。
3,GC回收對象時,一看有軟引用指向了這個對象,看一下本身的內存,夠用不吃,哎呀內存不夠了,我吃。
4,虛引用在任什麼時候候均可能被GC回收,。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。