當我們在做項目過程中,一遇到顯示圖檔時,就要考慮圖檔的大小,所占記憶體的大小,原因就是Android配置設定給Bitmap的大小隻有8M,試想想我們用手機拍照,普通的一張照片不也得1M以上,是以android處理圖檔時不得不考慮圖檔過大造成的記憶體異常。
那時候隻是簡單地緩存圖檔到本地 然後将圖檔進行壓縮,但是感覺這個問題沒有很好的解決辦法,隻是減小了發生的幾率
這裡,我将前輩們解決的方法重新整理一番,友善自己以後使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<code>import</code> <code>java.lang.ref.PhantomReference;</code>
<code>import</code> <code>java.lang.ref.Reference;</code>
<code>import</code> <code>java.lang.ref.ReferenceQueue;</code>
<code>import</code> <code>java.lang.reflect.Field;</code>
<code>public</code> <code>class</code> <code>Test {</code>
<code> </code><code>public</code> <code>static</code> <code>boolean isRun = </code><code>true</code><code>;</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(</code><code>String</code><code>[] args) throws Exception {</code>
<code> </code><code>String</code> <code>abc = </code><code>new</code> <code>String</code><code>(</code><code>"abc"</code><code>);</code>
<code> </code><code>System.out.println(abc.getClass() + </code><code>"@"</code> <code>+ abc.hashCode());</code>
<code> </code>
<code> </code><code>final</code> <code>ReferenceQueue referenceQueue = </code><code>new</code> <code>ReferenceQueue<</code><code>String</code><code>>();</code>
<code> </code><code>new</code> <code>Thread() {</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code> </code><code>while</code> <code>(isRun) {</code>
<code> </code><code>Object</code> <code>o = referenceQueue.poll();</code>
<code> </code><code>if</code> <code>(o != </code><code>null</code><code>) {</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>Field rereferent = Reference.</code><code>class</code>
<code> </code><code>.getDeclaredField(</code><code>"referent"</code><code>);</code>
<code> </code><code>rereferent.setAccessible(</code><code>true</code><code>);</code>
<code> </code><code>Object</code> <code>result = rereferent.</code><code>get</code><code>(o);</code>
<code> </code><code>System.out.println(</code><code>"gc will collect:"</code>
<code> </code><code>+ result.getClass() + </code><code>"@"</code>
<code> </code><code>+ result.hashCode());</code>
<code> </code><code>} </code><code>catch</code> <code>(Exception e) {</code>
<code> </code><code>e.printStackTrace();</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
<code> </code><code>}.start();</code>
<code> </code><code>PhantomReference<</code><code>String</code><code>> abcWeakRef = </code><code>new</code> <code>PhantomReference<</code><code>String</code><code>>(abc,</code>
<code> </code><code>referenceQueue);</code>
<code> </code><code>abc = </code><code>null</code><code>;</code>
<code> </code><code>Thread.currentThread().sleep(</code><code>3000</code><code>);</code>
<code> </code><code>System.gc();</code>
<code> </code><code>isRun = </code><code>false</code><code>;</code>
<code> </code><code>}</code>
<code>}</code>
結果:
<code>class</code> <code>java.lang.</code><code>String</code><code>@</code><code>96354</code>
<code>gc will collect:</code><code>class</code> <code>java.lang.</code><code>String</code><code>@</code><code>96354</code>
2.在記憶體中加載圖檔時直接在記憶體中做處理
A.邊界壓縮
<code>@SuppressWarnings(</code><code>"unused"</code><code>)</code>
<code>private</code> <code>Bitmap copressImage(</code><code>String</code> <code>imgPath){</code>
<code> </code><code>File picture = </code><code>new</code> <code>File(imgPath);</code>
<code> </code><code>Options bitmapFactoryOptions = </code><code>new</code> <code>BitmapFactory.Options();</code>
<code> </code><code>//下面這個設定是将圖檔邊界不可調節變為可調節</code>
<code> </code><code>bitmapFactoryOptions.inJustDecodeBounds = </code><code>true</code><code>;</code>
<code> </code><code>bitmapFactoryOptions.inSampleSize = </code><code>2</code><code>;</code>
<code> </code><code>int</code> <code>outWidth = bitmapFactoryOptions.outWidth;</code>
<code> </code><code>int</code> <code>outHeight = bitmapFactoryOptions.outHeight;</code>
<code> </code><code>bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(),</code>
<code> </code><code>bitmapFactoryOptions);</code>
<code> </code><code>float imagew = </code><code>150</code><code>;</code>
<code> </code><code>float imageh = </code><code>150</code><code>;</code>
<code> </code><code>int</code> <code>yRatio = (</code><code>int</code><code>) Math.ceil(bitmapFactoryOptions.outHeight</code>
<code> </code><code>/ imageh);</code>
<code> </code><code>int</code> <code>xRatio = (</code><code>int</code><code>) Math</code>
<code> </code><code>.ceil(bitmapFactoryOptions.outWidth / imagew);</code>
<code> </code><code>if</code> <code>(yRatio > </code><code>1</code> <code>|| xRatio > </code><code>1</code><code>) {</code>
<code> </code><code>if</code> <code>(yRatio > xRatio) {</code>
<code> </code><code>bitmapFactoryOptions.inSampleSize = yRatio;</code>
<code> </code><code>} </code><code>else</code> <code>{</code>
<code> </code><code>bitmapFactoryOptions.inSampleSize = xRatio;</code>
<code> </code><code>}</code>
<code> </code>
<code> </code><code>bitmapFactoryOptions.inJustDecodeBounds = </code><code>false</code><code>;</code><code>//false --- allowing the caller to query the bitmap without having to allocate the memory for its pixels.</code>
<code> </code><code>bitmapFactoryOptions);</code>
<code> </code><code>if</code><code>(bmap != </code><code>null</code><code>){ </code>
<code> </code><code>//ivwCouponImage.setImageBitmap(bmap);</code>
<code> </code><code>return</code> <code>bmap;</code>
<code> </code><code>return</code> <code>null</code><code>;</code>
B.邊界壓縮的情況下間接的使用了軟引用來避免OOM
<code>/* 自定義Adapter中部分代碼*/</code>
<code> </code><code>public</code> <code>View getView(</code><code>int</code> <code>position, View convertView, ViewGroup parent) {</code>
<code> </code><code>File file = </code><code>new</code> <code>File(it.</code><code>get</code><code>(position));</code>
<code> </code><code>SoftReference<Bitmap> srf = imageCache.</code><code>get</code><code>(file.getName());</code>
<code> </code><code>Bitmap bit = srf.</code><code>get</code><code>();</code>
<code> </code><code>ImageView i = </code><code>new</code> <code>ImageView(mContext);</code>
<code> </code><code>i.setImageBitmap(bit);</code>
<code> </code><code>i.setScaleType(ImageView.ScaleType.FIT_XY);</code>
<code> </code><code>i.setLayoutParams( </code><code>new</code> <code>Gallery.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,</code>
<code> </code><code>WindowManager.LayoutParams.WRAP_CONTENT));</code>
<code> </code><code>return</code> <code>i;</code>
但大家都知道,這些函數在完成decode後,最終都是通過java層的createBitmap來完成的,需要消耗更多記憶體,如果圖檔多且大,這種方式還是會引用OOM異常的,是以需要進一步處理:
A.第一種方式
<code>InputStream </code><code>is</code> <code>= </code><code>this</code><code>.getResources().openRawResource(R.drawable.pic1);</code>
<code> </code><code>BitmapFactory.Options options=</code><code>new</code> <code>BitmapFactory.Options();</code>
<code> </code><code>options.inJustDecodeBounds = </code><code>false</code><code>;</code>
<code> </code><code>options.inSampleSize = </code><code>10</code><code>; </code><code>//width,hight設為原來的十分一</code>
<code> </code><code>Bitmap btp =BitmapFactory.decodeStream(</code><code>is</code><code>,</code><code>null</code><code>,options);</code>
<code> </code><code>if</code><code>(!bmp.isRecycle() ){</code>
<code> </code><code>bmp.recycle() </code><code>//回收圖檔所占的記憶體</code>
<code> </code><code>system.gc() </code><code>//提醒系統及時回收</code>
B.第二中方式
<code>/**</code>
<code>* 以最省記憶體的方式讀取本地資源的圖檔</code>
<code>* */</code>
<code>public</code> <code>static</code> <code>Bitmap readBitMap(Context context, </code><code>int</code> <code>resId){ </code>
<code> </code><code>BitmapFactory.Options opt = </code><code>new</code> <code>BitmapFactory.Options(); </code>
<code> </code><code>opt.inPreferredConfig = Bitmap.Config.RGB_565; </code>
<code> </code><code>opt.inPurgeable = </code><code>true</code><code>; </code>
<code> </code><code>opt.inInputShareable = </code><code>true</code><code>; </code>
<code> </code><code>//擷取資源圖檔 </code>
<code> </code><code>InputStream </code><code>is</code> <code>= context.getResources().openRawResource(resId); </code>
<code> </code><code>return</code> <code>BitmapFactory.decodeStream(</code><code>is</code><code>,</code><code>null</code><code>,opt); </code>
<code> </code><code>}</code>
C.在适當的時候垃圾回收
<code>if</code><code>(bitmapObject.isRecycled()==</code><code>false</code><code>) </code><code>//如果沒有回收 </code>
<code> </code><code>bitmapObject.recycle();</code>
D.優化Dalvik虛拟機的堆記憶體配置設定
對于Android平台來說,其托管層使用的Dalvik JavaVM從目前的表現來看還有很多地方可以優化處理,eg我們在開發一些大型遊戲或耗資源的應用中可能考慮手動幹涉GC處理,使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強程式堆記憶體的處理效率。
<code>private</code> <code>final</code> <code>static</code> <code>floatTARGET_HEAP_UTILIZATION = </code><code>0</code><code>.75f;</code>
<code>//在程式onCreate時就可以調用</code>
<code>VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);</code>
<code>即可</code>
至于上面為何是0.75,是因為堆(HEAP)是VM中占用記憶體最多的部分,通常是動态配置設定的。堆的大小不是一成不變的,通常有一個配置設定機制來控制它的大小。比如初始的HEAP是4M大,當4M的空間被占用超過75%的時候,重新配置設定堆為8M大;當8M被占用超過75%,配置設定堆為16M大。倒過來,當16M的堆利用不足30%的時候,縮減它的大小為8M大。重新設定堆的大小,尤其是壓縮,一般會涉及到記憶體的拷貝,是以變更堆的大小對效率有不良影響。
E.自定義我們的應用需要多大的記憶體
<code>private</code> <code>final</code> <code>static</code> <code>int</code> <code>CWJ_HEAP_SIZE = </code><code>6</code><code>* </code><code>1024</code><code>* </code><code>1024</code> <code>;</code>
<code> </code><code>//設定最小heap記憶體為6MB大小</code>
<code>VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);</code>
以上這些就是本人總結的一些解決OOM異常的方法,希望能幫助到大家!
參考部落格:http://blog.sina.com.cn/s/blog_7501670601014dcj.html
本文轉自zhf651555765 51CTO部落格,原文連結:http://blog.51cto.com/smallwoniu/1248875,如需轉載請自行聯系原作者