天天看點

【移動開發】Android中圖檔過大造成記憶體溢出,OOM(OutOfMemory)異常解決方法

 當我們在做項目過程中,一遇到顯示圖檔時,就要考慮圖檔的大小,所占記憶體的大小,原因就是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&lt;</code><code>String</code><code>&gt;();</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&lt;</code><code>String</code><code>&gt; abcWeakRef = </code><code>new</code> <code>PhantomReference&lt;</code><code>String</code><code>&gt;(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 &gt; </code><code>1</code> <code>|| xRatio &gt; </code><code>1</code><code>) {</code>

<code>        </code><code>if</code> <code>(yRatio &gt; 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&lt;Bitmap&gt; 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,如需轉載請自行聯系原作者

繼續閱讀