無(wú)標(biāo)題文章

首先什么是OOM?為什么會(huì)出現(xiàn)OOM?

Out Of Memory音婶,一般是由于程序編寫(xiě)者對(duì)內(nèi)存使用不當(dāng),如對(duì)該釋放的內(nèi)存資源沒(méi)有釋放莱坎,導(dǎo)致其一直不能被再次使用而使計(jì)算機(jī)內(nèi)存被耗盡的現(xiàn)象衣式。重啟計(jì)算機(jī)即可,但根本解決辦法還是對(duì)代碼進(jìn)行優(yōu)化檐什。(摘自百度百科)

那么解決OOM的方法有哪些呢碴卧?或者說(shuō)常見(jiàn)的導(dǎo)致OOM的錯(cuò)誤有哪些?

1乃正、動(dòng)態(tài)回收內(nèi)存住册。

2、為應(yīng)用分配更多的內(nèi)存烫葬。

3界弧、自定義內(nèi)存大小。

4搭综、如果是因?yàn)閳D片引起的OOM,其實(shí)就可以從圖片下手划栓。(使圖片體積大小變卸医怼)

5、加載圖片時(shí)在內(nèi)存中做處理忠荞。(圖片的邊界壓縮)

6蒋歌、Context泄漏。

7委煤、使用eclipse?DDMS中的heap查看內(nèi)存堂油。

8、構(gòu)造Adapter時(shí)碧绞,沒(méi)有使用緩存的convertView府框。

9、資源對(duì)象沒(méi)關(guān)閉造成的內(nèi)存泄漏讥邻。

10迫靖、注冊(cè)沒(méi)取消造成的內(nèi)存泄漏。

11兴使、集合中對(duì)象沒(méi)清理造成的內(nèi)存泄漏系宜。

12、使用緩存技術(shù)发魄。

1盹牧、動(dòng)態(tài)回收內(nèi)存算是最簡(jiǎn)單的解決方法吧,就是手動(dòng)的調(diào)用System.gc();

例如:bit為Bitmap對(duì)象

if(bit !=null&& !bit.isRecycled()) {

? ? ? ?? bit.recycle();

? ? ? ?? bit =null;}

System.gc();

bitmap.recycle()方法用于回收該bitmap所占用的內(nèi)存,用System.gc()調(diào)用一下系統(tǒng)的垃圾回收器。

需要注意的是:回收內(nèi)存要及時(shí)汰寓,比如說(shuō)SurfaceView口柳,就應(yīng)該在onSurfaceDestroyed這個(gè)方法中回收。如果Activity使用了bitmap,就可以在onStop或者onDestroy方法中回收等等踩寇。

2啄清、為應(yīng)用分配更多的內(nèi)存。

在清單文件中的< application >節(jié)點(diǎn)下俺孙,添加如下代碼:android:largeHeap="true"辣卒。

android:largeHeap應(yīng)用程序的進(jìn)程是否會(huì)用較大的 Dalvik 堆來(lái)創(chuàng)建。 這將作用于所有為該應(yīng)用程序創(chuàng)建的進(jìn)程睛榄,但只對(duì)第一個(gè)被裝入進(jìn)程的應(yīng)用程序生效荣茫。?如果通過(guò)共享用戶 ID 的方式讓多個(gè)應(yīng)用程序公用一個(gè)進(jìn)程,那么這些應(yīng)用程序必須全部指定本選項(xiàng)场靴,否則將會(huì)導(dǎo)致不可預(yù)知的后果啡莉。

大部分應(yīng)用程序不需要用到本屬性,而是應(yīng)該關(guān)注如何減少內(nèi)存消耗以提高性能旨剥。?使用本屬性并不能確保一定會(huì)增加可用的內(nèi)存咧欣,因?yàn)槟承┰O(shè)備可用的內(nèi)存本來(lái)就很有限。

要在運(yùn)行時(shí)查詢可用的內(nèi)存大小轨帜,請(qǐng)使用getMemoryClass()getLargeMemoryClass()方法魄咕。

除上述方法外,還有一個(gè)方法蚌父。

使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強(qiáng)程序堆內(nèi)存的處理效率哮兰。

具體的使用如下:

privatefinalstaticfloatTARGET_HEAP_UTILIZATION = 0.75f;? //在程序onCreate時(shí)就可以調(diào)用 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); //即可

3、一直感覺(jué)自定義的這種方法實(shí)在是太暴力了苟弛。

強(qiáng)制定義自己軟件的對(duì)內(nèi)存大小喝滞,我們使用Dalvik提供的 dalvik.system.VMRuntime類來(lái)設(shè)置最小堆內(nèi)存為例:

privatefinalstaticintCWJ_HEAP_SIZE = 6* 1024* 1024 ;

VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //設(shè)置最小heap內(nèi)存為6MB大小。當(dāng)然對(duì)于內(nèi)存吃緊來(lái)說(shuō)還可以通過(guò)手動(dòng)干涉GC去處理

4膏秫、這也分為兩個(gè)方面:

1右遭、分辨率不變,圖片大小減小荔睹。? 2狸演、分辨率改變,圖片減小僻他。(用PS都很容易的)

需要注意的是:不要減小得太小而影響了人眼看上去的美感宵距。

5、這里給出一個(gè)簡(jiǎn)單的操作和一個(gè)封裝后的操作吨拗,可以對(duì)比看看满哪。

簡(jiǎn)單的操作:

//壓縮婿斥,用于節(jié)省BITMAP內(nèi)存空間--解決BUG的關(guān)鍵步驟 BitmapFactory.Options opts =new BitmapFactory.Options();

opts.inSampleSize = 2;//這個(gè)的值壓縮的倍數(shù)(2的整數(shù)倍),數(shù)值越小哨鸭,壓縮率越小民宿,圖片越清晰 //返回原圖解碼之后的bitmap對(duì)象Bitmap? bitmap = BitmapFactory.decodeResource(getResources(),

? ? ? ? ? ? ? ? ? ? R.drawable.begin_background, opts);

這里的bitmap就是壓縮后得到的圖片。

封裝后的操作:

privateBitmap imgUtis(Resources res,intimg,intreqWidth,int reqHeight) {

? ? ? ? BitmapFactory.Options options =new BitmapFactory.Options();

? ? ? ? options.inJustDecodeBounds =true;// 讓解析方法禁止為bitmap分配內(nèi)存,返回值也不再是一個(gè)Bitmap對(duì)象像鸡,而是null活鹰。? ? ? ? BitmapFactory.decodeResource(getResources(), img, options);

? ? ? ? // 在加載圖片之前就獲取到圖片的長(zhǎng)寬值和MIME類型,并返回壓縮的尺寸options.inSampleSize = calculateInSampleSize(options, reqWidth,

? ? ? ? ? ? ? ? reqHeight);

? ? ? ? // 使用獲取到的inSampleSize值再次解析圖片options.inJustDecodeBounds =false;

? ? ? ? return BitmapFactory.decodeResource(getResources(), img, options);

? ? }

? ? /**? ?? *

? ?? * @param options 操作對(duì)象

? ?? * @param reqWidth 目標(biāo)寬

? ?? * @param reqHeight 目標(biāo)高

? ?? * @return*/publicstaticint calculateInSampleSize(BitmapFactory.Options options,

? ? ? ? ? ? intreqWidth,int reqHeight) {

? ? ? ? // 源圖片的高度和寬度f(wàn)inalintheight = options.outHeight;

? ? ? ? finalintwidth = options.outWidth;

? ? ? ? intinSampleSize = 1;

? ? ? ? if(height > reqHeight || width > reqWidth) {

? ? ? ? ? ? // 計(jì)算出實(shí)際寬高和目標(biāo)寬高的比率finalintheightRatio = Math.round((float) height

? ? ? ? ? ? ? ? ? ? / (float) reqHeight);

? ? ? ? ? ? finalintwidthRatio = Math.round((float) width / (float) reqWidth);

? ? ? ? ? ? // 選擇寬和高中最小的比率作為inSampleSize的值只估,這樣可以保證最終圖片的寬和高

? ? ? ? ? ? // 一定都會(huì)大于等于目標(biāo)的寬和高志群。inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

? ? ? ? }

? ? ? ? return inSampleSize;

? ? }

這里得到的bitmap是按照規(guī)定的尺度比例來(lái)進(jìn)行壓縮的。

6.

所謂的Context泄漏蛔钙,其實(shí)更多的是指Activity泄露锌云,這是一個(gè)很隱晦的OutOfMemoryError的情況。

先看一個(gè)Android官網(wǎng)提供的例子:?

privatestatic Drawable sBackground;?

@Override? protectedvoid onCreate(Bundle state) {?

? super.onCreate(state);?

? TextView label =newTextView(this);?

? label.setText("Leaks are bad");?

? if(sBackground ==null) {?

? ? sBackground = getDrawable(R.drawable.large_bitmap);?

? }?

? label.setBackgroundDrawable(sBackground);?

? setContentView(label);?

}?

這段代碼效率很快吁脱,但同時(shí)又是極其錯(cuò)誤的桑涎; 在第一次屏幕方向切換時(shí)它泄露了一開(kāi)始創(chuàng)建的Activity。當(dāng)一個(gè)Drawable附加到一個(gè) View上時(shí)兼贡, View會(huì)將其作為一個(gè)callback設(shè)定到Drawable上攻冷。上述的代碼片段,意味著這個(gè)靜態(tài)的Drawable擁有一個(gè)TextView的引用遍希,

而TextView又擁有Activity(Context類型)的引用讲衫,換句話說(shuō),Drawable擁有了更多的對(duì)象引用孵班。即使Activity被 銷毀,內(nèi)存仍然不會(huì)被釋放招驴。?

另外篙程,對(duì)Context的引用超過(guò)它本身的生命周期,也會(huì)導(dǎo)致該Context無(wú)法回收别厘,從而導(dǎo)致內(nèi)存泄漏虱饿。所以盡量使用Application這種Context類型。?

這種Context擁有和應(yīng)用程序一樣長(zhǎng)的生命周期触趴,并且不依賴Activity的生命周期氮发。如果你打算保存一個(gè)長(zhǎng)時(shí)間的對(duì)象,?

并且其需要一個(gè) Context冗懦,記得使用Application對(duì)象爽冕。你可以通過(guò)調(diào)用Context.getApplicationContext()或 Activity.getApplication()輕松得到Application對(duì)象。?

最近遇到一種情況引起了Context泄漏披蕉,就是在Activity銷毀時(shí)颈畸,里面有其他線程沒(méi)有停乌奇。?

總結(jié)一下避免Context泄漏應(yīng)該注意的問(wèn)題:?

盡量使用Application這種Context類型。?

注意對(duì)Context的引用不要超過(guò)它本身的生命周期眯娱。?

慎重的對(duì)Context使用“static”關(guān)鍵字礁苗。?

Context里如果有線程,一定要在onDestroy()里及時(shí)停掉徙缴。

7.Android?開(kāi)發(fā)工具eclipse中的DDMS帶有一個(gè)內(nèi)存監(jiān)測(cè)工具Heap试伙,可以檢測(cè)一個(gè)進(jìn)程的內(nèi)存變化,根據(jù)這個(gè)工具我們大致可以測(cè)試某個(gè)應(yīng)用的內(nèi)存變化于样。

具體的操作方法如下:

1疏叨、打開(kāi)eclipse,切換到DDMS百宇,并確認(rèn)Devices視圖考廉、Heap視圖都是打開(kāi)的。

2携御、將手機(jī)通過(guò)USB鏈接至電腦昌粤,鏈接時(shí),選擇?“USB調(diào)試”模式啄刹。

3涮坐、鏈接成功后,在DDMS的Devices視圖中將會(huì)顯示手機(jī)設(shè)備的序列號(hào)誓军,以及設(shè)備中正在運(yùn)行的部分進(jìn)程信息袱讹。

4、在Devices?中昵时,點(diǎn)擊要監(jiān)控的程序捷雕。

5、點(diǎn)擊Devices視圖界面中最上方一排圖標(biāo)中的“Update?Heap”壹甥。

6救巷、點(diǎn)擊Heap視圖。

7句柠、點(diǎn)擊Heap視圖中的“Cause?GC”按鈕浦译。

8、到此為止需檢測(cè)的進(jìn)程就可以被監(jiān)視溯职。

操作如圖所示:

說(shuō)明:

1精盅、點(diǎn)擊“Cause?GC”按鈕相當(dāng)于向虛擬機(jī)請(qǐng)求了一次垃圾回收操作;?

2谜酒、當(dāng)內(nèi)存使用信息第一次顯示以后叹俏,無(wú)須再不斷的點(diǎn)擊“Cause?GC”,Heap視圖界面會(huì)定時(shí)刷新甚带,在對(duì)應(yīng)用的不斷的操作過(guò)程中就可以看到內(nèi)存使用的變化她肯;?

3佳头、內(nèi)存使用信息的各項(xiàng)參數(shù)根據(jù)名稱即可知道其意思,在此不再贅述晴氨。?

Heap視圖中有一個(gè)Type叫做data?object康嘉,即數(shù)據(jù)對(duì)象,也就是我們的程序中大量存在的類類型的對(duì)象籽前。在data?object一行中有一列是“Total?Size”亭珍,其值就是當(dāng)前進(jìn)程中所有Java數(shù)據(jù)對(duì)象的內(nèi)存總量,一般情況下枝哄,這個(gè)值的大小決定了是否會(huì)存在內(nèi)存泄漏肄梨。判斷方法如下:

進(jìn)入某應(yīng)用,不斷的操作該應(yīng)用挠锥,同時(shí)注意觀察data?object的Total?Size值众羡。

正常情況下Total?Size值都會(huì)穩(wěn)定在一個(gè)有限的范圍內(nèi)。

反之如果代碼中存在沒(méi)有釋放對(duì)象引用的情況蓖租,則data?object的Total?Size值在每次GC后不會(huì)有明顯的回落粱侣,隨著操作次數(shù)的增多Total?Size的值會(huì)越來(lái)越大,直到到達(dá)一個(gè)上限后導(dǎo)致進(jìn)程被kill掉蓖宦。

8.例如:ListView的工作原理簡(jiǎn)而言之是針對(duì)List中每個(gè)item齐婴,?adapter都會(huì)調(diào)用一個(gè)getView的方法獲得布局視圖。我們一般會(huì)Inflate一個(gè)新的View稠茂,填充數(shù)據(jù)并返回顯示柠偶。當(dāng)然如果我們的Item很多話(比如上萬(wàn)個(gè)),都會(huì)新建一個(gè)View嗎睬关?很明顯這樣內(nèi)存是接受不了的诱担。因此優(yōu)化就開(kāi)始了,我們?cè)趃etView()方法中使用了convertView?==?null的判斷电爹,這是Android已經(jīng)給我們提供了Recycler機(jī)制了该肴,我們就應(yīng)該利用此機(jī)制,而不是每次都去inflate一個(gè)View藐不。除此之外,我們還是從getView中的每一個(gè)方法調(diào)用去查看秦效,發(fā)現(xiàn)其實(shí)我們拿到convertView的時(shí)候雏蛮,每次都會(huì)根據(jù)這個(gè)布局去findViewById。因此阱州,應(yīng)使用一個(gè)靜態(tài)類挑秉,保存xml中的各個(gè)子View的引用關(guān)系,這樣就不必要每次都去解析xml了苔货,而這個(gè)靜態(tài)類就是代碼中的ViewText犀概。

其示例代碼:

publicclassExamDataAdapterextends BaseAdapter

{

? ? privateList exams =null;

? ? private LayoutInflater inflater;

? ? privateintresource;// 綁定的條目界面publicExamDataAdapter(List exam, Context context,int id)

? ? {

? ? ? ? this.resource = id;

? ? ? ? this.exams = exam;

? ? ? ? this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

? ? }

? ? publicint getCount()

? ? {

? ? ? ? return exams.size();

? ? }

? ? publicObject getItem(int position)

? ? {

? ? ? ? return exams.get(position);

? ? }

? ? publiclonggetItemId(int position)

? ? {

? ? ? ? return position;

? ? }

? ? publicView getView(int position, View convertView, ViewGroup parent)

? ? {

? ? ? ? TextView examCount =null;

? ? ? ? TextView examName =null;

? ? ? ? TextView examNumber =null;

? ? ? ? if(convertView ==null)

? ? ? ? {

? ? ? ? ? ? convertView = inflater.inflate(resource,null);

? ? ? ? ? ? examName = (TextView) convertView.findViewById(R.id.test_list_name);

? ? ? ? ? ? examCount = (TextView) convertView.findViewById(R.id.test_list_count);

? ? ? ? ? ? examNumber = (TextView) convertView.findViewById(R.id.test_list_id);

? ? ? ? ? ? ViewText viewText =new ViewText();

? ? ? ? ? ? viewText.examName = examName;

? ? ? ? ? ? viewText.examCount = examCount;

? ? ? ? ? ? viewText.examNumber = examNumber;

? ? ? ? ? ? convertView.setTag(viewText);

? ? ? ? } else? ? ? ? {

? ? ? ? ? ? ViewText viewText = (ViewText) convertView.getTag();

? ? ? ? ? ? examName = viewText.examName;

? ? ? ? ? ? examCount = viewText.examCount;

? ? ? ? ? ? examNumber = viewText.examNumber;

? ? ? ? }

? ? ? ? Exam exam = exams.get(position);

? ? ? ? examName.setText(exam.getExam_Name().trim());

? ? ? ? examCount.setText(exam.getExam_Count() + "".trim());

? ? ? ? examNumber.setText(exam.getExam_ID() + "".trim());

? ? ? ? return convertView;

? ? }

? ? publicfinalclass ViewText

? ? {

? ? ? ? public TextView examCount;

? ? ? ? public TextView examName;

? ? ? ? public TextView examNumber;

? ? }

}

這種優(yōu)化方式在2009年?Google?IO開(kāi)發(fā)者大會(huì)中已做說(shuō)明立哑,屬于ViewHolder類型,其優(yōu)化結(jié)果如圖所示姻灶。

9.資源性對(duì)象比如(Cursor铛绰,F(xiàn)ile文件等)往往都用了一些緩沖,我們?cè)诓皇褂玫臅r(shí)候产喉,應(yīng)該及時(shí)關(guān)閉它們捂掰,以便它們的緩沖及時(shí)回收內(nèi)存。它們的緩沖不僅存在于java虛擬機(jī)內(nèi)曾沈,還存在于java虛擬機(jī)外这嚣。如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們,往往會(huì)造成內(nèi)存泄漏塞俱。因?yàn)橛行┵Y源性對(duì)象姐帚,比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒(méi)有關(guān)閉它,它自己會(huì)調(diào)close()關(guān)閉)障涯,如果我們沒(méi)有關(guān)閉它罐旗,系統(tǒng)在回收它時(shí)也會(huì)關(guān)閉它,但是這樣的效率太低了像樊。因此對(duì)于資源性對(duì)象在不使用的時(shí)候尤莺,應(yīng)該調(diào)用它的close()函數(shù),將其關(guān)閉掉生棍,然后才置為null.在我們的程序退出時(shí)一定要確保我們的資源性對(duì)象已經(jīng)關(guān)閉颤霎。

程序中經(jīng)常會(huì)進(jìn)行查詢數(shù)據(jù)庫(kù)的操作,但是經(jīng)常會(huì)有使用完畢Cursor后沒(méi)有關(guān)閉的情況涂滴。如果我們的查詢結(jié)果集比較小友酱,對(duì)內(nèi)存的消耗不容易被發(fā)現(xiàn),只有在常時(shí)間大量操作的情況下才會(huì)復(fù)現(xiàn)內(nèi)存問(wèn)題柔纵,這樣就會(huì)給以后的測(cè)試和問(wèn)題排查帶來(lái)困難和風(fēng)險(xiǎn)缔杉。

示例代碼:

Cursor cursor = getContentResolver().query(uri...);? if (cursor.moveToNext()) {?

? ... ...? ?

}

修正示例代碼:?

Cursor cursor =null;? try {?

? cursor = getContentResolver().query(uri...);?

? if(cursor !=null&&cursor.moveToNext()) {?

? ? ? ... ...? ?

? }?

} finally {?

? if(cursor !=null) {?

? ? ? try {? ?

? ? ? ? ? cursor.close();?

? ? ? } catch (Exception e) {?

? ? ? ? ? //ignore this? ? ? ? }?

?? }?

}

need-to-insert-img

10.一些Android程序可能引用我們的Anroid程序的對(duì)象(比如注冊(cè)機(jī)制)。即使我們的Android程序已經(jīng)結(jié)束了搁料,但是別的引用程序仍然還有對(duì)我們的Android程序的某個(gè)對(duì)象的引用或详,泄漏的內(nèi)存依然不能被垃圾回收。調(diào)用registerReceiver后未調(diào)用unregisterReceiver郭计。

比如:假設(shè)我們希望在鎖屏界面(LockScreen)中霸琴,監(jiān)聽(tīng)系統(tǒng)中的電話服務(wù)以獲取一些信息(如信號(hào)強(qiáng)度等),則可以在LockScreen中定義一個(gè)PhoneStateListener的對(duì)象昭伸,同時(shí)將它注冊(cè)到TelephonyManager服務(wù)中梧乘。對(duì)于LockScreen對(duì)象,當(dāng)需要顯示鎖屏界面的時(shí)候就會(huì)創(chuàng)建一個(gè)LockScreen對(duì)象,而當(dāng)鎖屏界面消失的時(shí)候LockScreen對(duì)象就會(huì)被釋放掉选调。

但是如果在釋放LockScreen對(duì)象的時(shí)候忘記取消我們之前注冊(cè)的PhoneStateListener對(duì)象夹供,則會(huì)導(dǎo)致LockScreen無(wú)法被垃圾回收。如果不斷的使鎖屏界面顯示和消失仁堪,則最終會(huì)由于大量的LockScreen對(duì)象沒(méi)有辦法被回收而引起OutOfMemory,使得system_process進(jìn)程掛掉哮洽。

雖然有些系統(tǒng)程序,它本身好像是可以自動(dòng)取消注冊(cè)的(當(dāng)然不及時(shí))枝笨,但是我們還是應(yīng)該在我們的程序中明確的取消注冊(cè)袁铐,程序結(jié)束時(shí)應(yīng)該把所有的注冊(cè)都取消掉。

11.我們通常把一些對(duì)象的引用加入到了集合中横浑,當(dāng)我們不需要該對(duì)象時(shí)剔桨,并沒(méi)有把它的引用從集合中清理掉,這樣這個(gè)集合就會(huì)越來(lái)越大徙融。如果這個(gè)集合是static的話洒缀,那情況就更嚴(yán)重了。

12.(此段內(nèi)容摘自Android官網(wǎng))

內(nèi)存緩存技術(shù)對(duì)那些大量占用應(yīng)用程序?qū)氋F內(nèi)存的圖片提供了快速訪問(wèn)的方法欺冀。其中最核心的類是LruCache (此類在android-support-v4的包中提供) 树绩。這個(gè)類非常適合用來(lái)緩存圖片,它的主要算法原理是把最近使用的對(duì)象用強(qiáng)引用存儲(chǔ)在 LinkedHashMap 中隐轩,并且把最近最少使用的對(duì)象在緩存值達(dá)到預(yù)設(shè)定值之前從內(nèi)存中移除饺饭。

在過(guò)去,我們經(jīng)常會(huì)使用一種非常流行的內(nèi)存緩存技術(shù)的實(shí)現(xiàn)职车,即軟引用或弱引用 (SoftReference or WeakReference)瘫俊。但是現(xiàn)在已經(jīng)不再推薦使用這種方式了,因?yàn)閺?Android 2.3 (API Level 9)開(kāi)始悴灵,垃圾回收器會(huì)更傾向于回收持有軟引用或弱引用的對(duì)象扛芽,這讓軟引用和弱引用變得不再可靠。另外积瞒,Android 3.0 (API Level 11)中川尖,圖片的數(shù)據(jù)會(huì)存儲(chǔ)在本地的內(nèi)存當(dāng)中,因而無(wú)法用一種可預(yù)見(jiàn)的方式將其釋放茫孔,這就有潛在的風(fēng)險(xiǎn)造成應(yīng)用程序的內(nèi)存溢出并崩潰叮喳。

為了能夠選擇一個(gè)合適的緩存大小給LruCache, 有以下多個(gè)因素應(yīng)該放入考慮范圍內(nèi),例如:

你的設(shè)備可以為每個(gè)應(yīng)用程序分配多大的內(nèi)存缰贝?

設(shè)備屏幕上一次最多能顯示多少?gòu)垐D片嘲更?有多少圖片需要進(jìn)行預(yù)加載,因?yàn)橛锌赡芎芸煲矔?huì)顯示在屏幕上揩瞪?

你的設(shè)備的屏幕大小和分辨率分別是多少?一個(gè)超高分辨率的設(shè)備(例如 Galaxy Nexus) 比起一個(gè)較低分辨率的設(shè)備(例如 Nexus S)篓冲,在持有相同數(shù)量圖片的時(shí)候李破,需要更大的緩存空間宠哄。

圖片的尺寸和大小,還有每張圖片會(huì)占據(jù)多少內(nèi)存空間嗤攻。

圖片被訪問(wèn)的頻率有多高毛嫉?會(huì)不會(huì)有一些圖片的訪問(wèn)頻率比其它圖片要高?如果有的話妇菱,你也許應(yīng)該讓一些圖片常駐在內(nèi)存當(dāng)中承粤,或者使用多個(gè)LruCache 對(duì)象來(lái)區(qū)分不同組的圖片。

你能維持好數(shù)量和質(zhì)量之間的平衡嗎闯团?有些時(shí)候辛臊,存儲(chǔ)多個(gè)低像素的圖片,而在后臺(tái)去開(kāi)線程加載高像素的圖片會(huì)更加的有效房交。

并沒(méi)有一個(gè)指定的緩存大小可以滿足所有的應(yīng)用程序彻舰,這是由你決定的。你應(yīng)該去分析程序內(nèi)存的使用情況候味,然后制定出一個(gè)合適的解決方案刃唤。一個(gè)太小的緩存空間,有可能造成圖片頻繁地被釋放和重新加載白群,這并沒(méi)有好處尚胞。而一個(gè)太大的緩存空間,則有可能還是會(huì)引起 java.lang.OutOfMemory 的異常帜慢。

下面是一個(gè)使用 LruCache 來(lái)緩存圖片的例子:

need-to-insert-img

privateLruCache mMemoryCache;

@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {

? ? // 獲取到可用內(nèi)存的最大值笼裳,使用內(nèi)存超出這個(gè)值會(huì)引起OutOfMemory異常。

? ? // LruCache通過(guò)構(gòu)造函數(shù)傳入緩存值崖堤,以KB為單位侍咱。intmaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

? ? // 使用最大可用內(nèi)存值的1/8作為緩存的大小。intcacheSize = maxMemory / 8;

? ? mMemoryCache =newLruCache(cacheSize) {

? ? ? ? @Override

? ? ? ? protectedint sizeOf(String key, Bitmap bitmap) {

? ? ? ? ? ? // 重寫(xiě)此方法來(lái)衡量每張圖片的大小密幔,默認(rèn)返回圖片數(shù)量楔脯。returnbitmap.getByteCount() / 1024;

? ? ? ? }

? ? };

}publicvoid addBitmapToMemoryCache(String key, Bitmap bitmap) {

? ? if(getBitmapFromMemCache(key) ==null) {

? ? ? ? mMemoryCache.put(key, bitmap);

? ? }

}public Bitmap getBitmapFromMemCache(String key) {

? ? return mMemoryCache.get(key);

}

在這個(gè)例子當(dāng)中,使用了系統(tǒng)分配給應(yīng)用程序的八分之一內(nèi)存來(lái)作為緩存大小胯甩。在中高配置的手機(jī)當(dāng)中昧廷,這大概會(huì)有4兆(32/8)的緩存空間。一個(gè)全屏幕的 GridView 使用4張 800x480分辨率的圖片來(lái)填充偎箫,則大概會(huì)占用1.5兆的空間(800*480*4)木柬。因此,這個(gè)緩存大小可以存儲(chǔ)2.5頁(yè)的圖片淹办。

當(dāng)向 ImageView 中加載一張圖片時(shí),首先會(huì)在 LruCache 的緩存中進(jìn)行檢查眉枕。如果找到了相應(yīng)的鍵值,則會(huì)立刻更新ImageView ,否則開(kāi)啟一個(gè)后臺(tái)線程來(lái)加載這張圖片速挑。

publicvoidloadBitmap(int resId, ImageView imageView) {

? ? finalString imageKey = String.valueOf(resId);

? ? finalBitmap bitmap = getBitmapFromMemCache(imageKey);

? ? if(bitmap !=null) {

? ? ? ? imageView.setImageBitmap(bitmap);

? ? } else {

? ? ? ? imageView.setImageResource(R.drawable.image_placeholder);

? ? ? ? BitmapWorkerTask task =new BitmapWorkerTask(imageView);

? ? ? ? task.execute(resId);

? ? }

}

BitmapWorkerTask 還要把新加載的圖片的鍵值對(duì)放到緩存中谤牡。

classBitmapWorkerTaskextendsAsyncTask {

? ? // 在后臺(tái)加載圖片呵晚。? ? @Override

? ? protected Bitmap doInBackground(Integer... params) {

? ? ? ? finalBitmap bitmap = decodeSampledBitmapFromResource(

? ? ? ? ? ? ? ? getResources(), params[0], 100, 100);

? ? ? ? addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);

? ? ? ? return bitmap;

? ? }

}

大體過(guò)程就如上面的例子所示坦袍。下面來(lái)一個(gè)簡(jiǎn)單的完整的例子:

need-to-insert-img

need-to-insert-img

publicclassMainActivityextends Activity {

? ? privateLruCache mMemoryCache;

? ? private ImageView iv;

? ? publicString str = "http://wenwen.sogou.com/p/20100623/20100623101110-601052657.jpg";

? ? @Override

? ? protectedvoid onCreate(Bundle savedInstanceState) {

? ? ? ? // TODO Auto-generated method stubsuper.onCreate(savedInstanceState);

? ? ? ? setContentView(R.layout.main);

? ? ? ? iv = (ImageView) findViewById(R.id.iv);

? ? ? ? // 獲取到可用內(nèi)存的最大值石抡,使用內(nèi)存超出這個(gè)值會(huì)引起OutOfMemory異常炮温。

? ? ? ? // LruCache通過(guò)構(gòu)造函數(shù)傳入緩存值娶耍,以KB為單位挑社。intmaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

? ? ? ? // 使用最大可用內(nèi)存值的1/8作為緩存的大小蟹漓。intcacheSize = maxMemory / 8;

? ? ? ? mMemoryCache =newLruCache(cacheSize) {

? ? ? ? ? ? @Override

? ? ? ? ? ? protectedint sizeOf(String key, Bitmap bitmap) {

? ? ? ? ? ? ? ? // 重寫(xiě)此方法來(lái)衡量每張圖片的大小索抓,默認(rèn)返回圖片數(shù)量碳蛋。returnbitmap.getByteCount() / 1024;

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? loadBitmap(str, iv);

? ? }

? ? publicvoid addBitmapToMemoryCache(String key, Bitmap bitmap) {

? ? ? ? if(getBitmapFromMemCache(key) ==null) {

? ? ? ? ? ? mMemoryCache.put(key, bitmap);

? ? ? ? }

? ? }

? ? public Bitmap getBitmapFromMemCache(String key) {

? ? ? ? return mMemoryCache.get(key);

? ? }

? ? publicvoid loadBitmap(String url, ImageView imageView) {

? ? ? ? String imageKey = url;

? ? ? ? Bitmap bitmap = getBitmapFromMemCache(imageKey);

? ? ? ? if(bitmap !=null) {

? ? ? ? ? ? imageView.setImageBitmap(bitmap);

? ? ? ? } else {

? ? ? ? ? ? imageView.setImageResource(R.drawable.empty_photo);

? ? ? ? ? ? BitmapWorkerTask1 task =new BitmapWorkerTask1();

? ? ? ? ? ? task.execute(url);

? ? ? ? }

? ? }

? ? /**? ?? * 異步下載圖片的任務(wù)胚泌。

? ?? *

? ?? * @author guolin

? ?? */classBitmapWorkerTask1extendsAsyncTask {

? ? ? ? /**? ? ? ?? * 圖片的URL地址

? ? ? ?? */private String imageUrl;

? ? ? ? @Override

? ? ? ? protected Bitmap doInBackground(String... params) {

? ? ? ? ? ? imageUrl = params[0];

? ? ? ? ? ? // 在后臺(tái)開(kāi)始下載圖片Bitmap bitmap = downloadBitmap(imageUrl);

? ? ? ? ? ? if(bitmap !=null) {

? ? ? ? ? ? ? ? // 圖片下載完成后緩存到LrcCache中? ? ? ? ? ? ? ? addBitmapToMemoryCache(imageUrl, bitmap);

? ? ? ? ? ? }

? ? ? ? ? ? return bitmap;

? ? ? ? }

? ? ? ? @Override

? ? ? ? protectedvoid onPostExecute(Bitmap bitmap) {

? ? ? ? ? ? super.onPostExecute(bitmap);

? ? ? ? ? ? iv.setImageBitmap(bitmap);

? ? ? ? }

? ? ? ? /**? ? ? ?? * 建立HTTP請(qǐng)求,并獲取Bitmap對(duì)象疮蹦。

? ? ? ?? *

? ? ? ?? * @param imageUrl

? ? ? ?? *? ? ? ? ? ? 圖片的URL地址

? ? ? ?? * @return 解析后的Bitmap對(duì)象

? ? ? ?? */private Bitmap downloadBitmap(String imageUrl) {

? ? ? ? ? ? Bitmap bitmap =null;

? ? ? ? ? ? HttpURLConnection con =null;

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? URL url =new URL(imageUrl);

? ? ? ? ? ? ? ? con = (HttpURLConnection) url.openConnection();

? ? ? ? ? ? ? ? con.setConnectTimeout(5 * 1000);

? ? ? ? ? ? ? ? con.setReadTimeout(10 * 1000);

? ? ? ? ? ? ? ? if(con.getResponseCode() == 200) {

? ? ? ? ? ? ? ? ? ? bitmap = BitmapFactory.decodeStream(con.getInputStream());

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? System.out.println("輸入的路徑不存在");

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? } finally {

? ? ? ? ? ? ? ? if(con !=null) {

? ? ? ? ? ? ? ? ? ? con.disconnect();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? ? ? if(bitmap !=null) {

? ? ? ? ? ? ? ? return bitmap;

? ? ? ? ? ? }

? ? ? ? ? ? returnnull;

? ? ? ? }

? ? }

}

運(yùn)行結(jié)果如下(來(lái)張吾女王的圖片诸迟,嘿嘿):

PS:如果讀者對(duì)第6段中提到的 強(qiáng)引用、軟引用愕乎、弱引用阵苇、虛引用 不了解,可以查看相關(guān)博文:

作者:樂(lè)于呂

鏈接:http://www.reibang.com/p/f2d3899758f8

來(lái)源:簡(jiǎn)書(shū)

著作權(quán)歸作者所有感论。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)绅项,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末比肄,一起剝皮案震驚了整個(gè)濱河市快耿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芳绩,老刑警劉巖掀亥,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異妥色,居然都是意外死亡搪花,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門嘹害,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撮竿,“玉大人,你說(shuō)我怎么就攤上這事笔呀〈碧ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵许师,是天一觀的道長(zhǎng)房蝉。 經(jīng)常有香客問(wèn)我僚匆,道長(zhǎng),這世上最難降的妖魔是什么搭幻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任白热,我火速辦了婚禮,結(jié)果婚禮上粗卜,老公的妹妹穿的比我還像新娘。我一直安慰自己纳击,他們只是感情好续扔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著焕数,像睡著了一般纱昧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上堡赔,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天识脆,我揣著相機(jī)與錄音,去河邊找鬼善已。 笑死灼捂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的换团。 我是一名探鬼主播悉稠,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼艘包!你這毒婦竟也來(lái)了的猛?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤想虎,失蹤者是張志新(化名)和其女友劉穎卦尊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體舌厨,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岂却,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邓线。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淌友。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖骇陈,靈堂內(nèi)的尸體忽然破棺而出震庭,到底是詐尸還是另有隱情,我是刑警寧澤你雌,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布器联,位于F島的核電站二汛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拨拓。R本人自食惡果不足惜肴颊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渣磷。 院中可真熱鬧婿着,春花似錦、人聲如沸醋界。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)形纺。三九已至丘侠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逐样,已是汗流浹背蜗字。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脂新,地道東北人挪捕。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像戏羽,于是被迫代替她去往敵國(guó)和親担神。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 1.要做一個(gè)盡可能流暢的ListView始花,你平時(shí)在工作中如何進(jìn)行優(yōu)化的妄讯? ①Item布局,層級(jí)越少越好酷宵,使用hie...
    fozero閱讀 714評(píng)論 0 0
  • 【2017年最新】? iOS面試題及答案 設(shè)計(jì)模式是什么亥贸? 你知道哪些設(shè)計(jì)模式,并簡(jiǎn)要敘述浇垦? 設(shè)計(jì)模式是一種編碼經(jīng)...
    紫色冰雨閱讀 598評(píng)論 0 1
  • WebView·開(kāi)車指南 2016-08-31BugDev 北京市東城區(qū)首席Bug布道師開(kāi)山之作炕置,一整月交通事故血...
    53c021c38a1d閱讀 827評(píng)論 0 1
  • --如何處理android多線程安全不要堵塞UI線程,不在主線程中做耗時(shí)操作的處理男韧,可以開(kāi)啟子線程去執(zhí)行耗時(shí)操作 ...
    fozero閱讀 414評(píng)論 0 2
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉朴摊,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,690評(píng)論 0 9