編輯:關於Android編程
內存相關的問題在面試中被問到的概率還是比較大的,而且內存優化對於一個程序的性能而言也是至關重要的,現在就讓我們一起來學習吧!
一、內存洩漏
內存洩漏就是我們對某一內存空間的使用完成後沒有釋放。
主要原因:導致內存洩漏最主要的原因就是某些長存對象持有了一些其它應該被回收的對象的引用,導致垃圾回收器無法去回收掉這些對象。
出現的場景:
1.數據庫的cursor沒有關閉;
2.構造adapter時,沒有使用緩存contentview;
3.Bitmap對象不使用時采用recycle()釋放內存;
4.activity中的對象的生命周期大於activity;
二、內存溢出
內存溢出通俗理解就是軟件(應用)運行需要的內存,超出了它可用的最大內存。
出現的場景:
1、使用大的Bitmap圖片;
三、內存優化方法:
1、應該盡量避免static成員變量引用資源耗費過多的實例,比如Context。Context盡量使用Application Context,因為Application的Context的生命周期比較長,引用它不會出現內存洩露的問題。使用WeakReference代替強引用;
2、線程也是造成內存洩露的一個重要的源頭。線程產生內存洩露的主要原因在於線程生命周期的不可控。將線程內部類,改為靜態內部類;
3、Bitmap問題:可以說出現OutOfMemory問題的絕大多原因都是因為Bitmap的問題。因為Bitmap占用的內存實在是太多了,它是一個“超級大胖子”,特別是分辨率大的圖片,如果要顯示多張那問題就更顯著了;
如何解決Bitmap帶給我們的內存問題?
1)及時的銷毀,雖然系統能夠確認Bitmap分配的內存最終會被銷毀,但是由於它占用的內存過多,所以很可能會超過java堆的限制。因此,在用完Bitmap時,要及時的recycle掉。recycle並不能確定立即就會將Bitmap釋放掉,但是會給虛擬機一個暗示:“該圖片可以釋放了”。
2)設置一定的采樣率,有時候,我們要顯示的區域很小,沒有必要將整個圖片都加載出來,而只需要記載一個縮小過的圖片,這時候可以設置一定的采樣率,那麼就可以大大減小占用的內存。如下面的代碼:
BitmapFactory.Optionsoptions=newBitmapFactoOptions();options.inSampleSize=2;//圖片寬高都為原來的二分之一,即圖片為原來的四分之一
3)使用LruCache緩存。
4、巧妙的運用軟引用(SoftRefrence),有些時候,我們使用Bitmap後沒有保留對它的引用,因此就無法調用Recycle函數。這時候巧妙的運用軟引用,可以使Bitmap在內存快不足時得到有效的釋放;
5、盡量使用9path圖片,Adapter要使用convertView復用等等;
6、比較耗資源的對象及時的關閉,例如Cursor,各種傳感器,Service 等等;
7、使用IntentService代替service,這種Service的最大特點就是當後台任務執行結束後會自動停止,在極大程度上避免了Service內存洩漏的可能性;
8、使用ProGuard,它除了混淆代碼之外,還具有壓縮和優化代碼的功能。ProGuard會對我們的代碼進行檢索,刪除一些無用的代碼,並且會對類、字段、方法等進行重名,重命名之後的類、字段和方法名都會比原來簡短很多,這樣的話也就對內存的占用變得更少了;
ListView的優化方案:
1>、復用contentView:就是自定義適配器在getView方法中要考慮方法傳進來的參數contentView是否為null,如果為null就創建contentView並返回,如果不為null則直接使用;在這個方法中盡可能少創建view;
2>、異步加載圖片:給contentView設置tag(setTag()),傳入一個viewHolder對象,下次可以直接調用getTag()顯示緩存中的數據,可以達到圖像數據異步加載的效果;
3>.快速滑動列表時不顯示圖片:
當快速滑動列表時(SCROLL_STATE_FLING),item中的圖片獲取需要消耗資源的view,可以不顯示出來;而處於其他兩種狀態:空閒(SCROLL_STATE_IDLE)和低俗拖動(SCROLL_STATE_TOUCH_SCROLL),則將那些view顯示出來。
如果 listview 需要顯示的 item 很多,就要考慮分頁加載。比如一共要顯示100條或者更多的時候,我們可以考慮先加載20條,等用戶拉到列表底部的時候,再去加載接下來的20 條。
4>.如果自定義的item中有圖片,需要處理圖片(減少圖片所占內存);
1.對圖片進行邊界壓縮 2.用option類來保存圖片大小 3.避免圖片的實時縮放,最好預先縮放到視圖大小
HandlermHandler=newHandler(){ @Override publicvoidhandleMessage(Messagemsg){ mImageView.setImageBitmap(mBitmap); } }
在一個Activity中有一個Handler的內部類,Handler對象會隱式地持有該Activity的引用,而Handler通常會伴隨著一個耗時的後台線程(例如網絡下載圖片)一起出現,這個後台線程在任務執行完畢之後,通過消息機制通知Handler,然後Handler把圖片更新到界面。然而,如果用戶在網絡請求過程中關閉了Activity,正常情況下,Activity不再被使用,它就有可能在GC檢查時被回收掉,但由於這時線程尚未執行完,而該線程持有Handler的引用,這個Handler又持有Activity的引用,就導致該Activity無法被回收(即內存洩露),直到網絡請求結束。
要解決這個問題有兩種辦法:
方法一:通過程序邏輯來進行保護。
1).在關閉Activity的時候停掉你的後台線程。線程停掉了,就相當於切斷了Handler和外部連接的線,Activity自然會在合適的時候被回收。
2).如果你的Handler是被delay的Message持有了引用(調用了postDelayed,那麼使用Handler的removeCallbacks()或removeCallbacksAndMessages(null)方法把消息對象從消息隊列移除就行了。
方法二:將Handler聲明為靜態類,並且加上Activity的弱引用。
靜態類不持有外部類的對象,所以你的Activity就可以被回收了。代碼如下:
staticclassMyHandlerextendsHandler{ WeakReferencemActivityReference; MyHandler(Activityactivity){ mActivityReference=newWeakReference(activity); } @Override publicvoidhandleMessage(Messagemsg){ finalActivityactivity=mActivityReference.get(); ImageViewimageView=(ImageView)activity.findViewById(R.id.imageview); if(activity!=null){ mImageView.setImageBitmap(mBitmap); } } }
事例二、
newThread(newRunnable(){ @Override publicvoidrun(){ SystemClock.sleep(10000); } }).start();
在Activity裡聲明了一個匿名內部類,如果Activity在銷毀之前,線程的任務還未完成, 那麼將導致Activity的內存資源無法回收,造成內存洩漏。
解決辦法就是使用靜態內部類,並且及時關閉線程,如下:
privatestaticclassMyThreadextendsThread{ privatebooleanmRunning=false; @Override publicvoidrun(){ mRunning=true; while(mRunning){ SystemClock.sleep(1000); } } publicvoidclose(){ mRunning=false; } }
我們在Activity退出時,可以在onDestroy()方法中顯示的調用mThread.close();以此來結束該線程,這樣在避免Activity OOM的同時也避免了線程的內存洩漏問題。
每一個非靜態內部類實例都會持有一個外部類的引用,若該引用是Activity的引用,那麼該Activity在被銷毀時將無法被回收。如果你的靜態內部類需要一個相關Activity的引用以確保功能能夠正常運行,那麼你得確保你在對象中使用的是一個Activity的弱引用。
這樣看來static好像是解決OOM問題的重要武器,那是不是每個內部類都使用static來修改就好了呢,其實不然,只有當此類在全局多處用到時才這樣做,因為static聲明變量的生命周期其實是和APP的生命周期一樣的,有點類似於Application。如果大量的使用的話,就會占據內存空間不釋放,積少成多也會造成內存的不斷開銷,直至掛掉。static一般用來修飾基本數據類型或者輕量級對象,盡量避免修飾集合或者大對象,常用作修飾全局配置項、工具類方法、內部類。
最後再來個擴展閱讀...
GC回收的對象?
如果GC發現一個或一組對象為不可到達狀態,則將該對象從內存中回收。也就是說,一個對象不被任何引用所指向,則該對象會在被GC發現的時候被回收;另外,如果一組對象中只包含互相的引用,而沒有來自它們外部的引用(例如有兩個對象A和B互相持有引用,但沒有任何外部對象持有指向A或B的引用),這仍然屬於不可到達,同樣會被GC回收。
什麼是WeakReference? OOM問題解決方法?
WeakReference弱引用,與強引用(即我們常說的引用)相對,它的特點是,GC在回收時會忽略掉弱引用,即就算有弱引用指向某對象,但只要該對象沒有被強引用指向(實際上多數時候還要求沒有軟引用),該對象就會在被GC檢查到時回收掉。如果對內存的開銷比較關注的APP,可以考慮使用WeakReference,當GC回收掃過這塊內存區域時就會回收;如果不是那麼關注的話,可以使用SoftReference,它會在內存申請不足的情況下自動釋放,同樣也能解決OOM問題。同時Android自3.0以後也推出了LruCache類,使用LRU算法就釋放內存,一樣的能解決OOM,如果要兼容3.0以下的版本,需要導入v4包。
1 背景當使用線程和Handler組合實現異步處理時,當每次執行耗時操作都創建一條新線程進行處理,性能開銷會比較大。為了提高性能我們使用AsyncTask實現異步處理(其
一般情況下,如果想要在ListView上面實現Listitem的滑動刪除效果,或者仿QQ的滑動顯示刪除效果的時候,只需要繼承ListView,自定義一個ListView就
.xml代碼如下: .java程序如下: package org.lxh.demo; import java.io.Byte
前一篇文章講述了Android拍照、截圖、保存並顯示在ImageView控件中,該篇文章繼續講述Android圖像處理技術,主要操作包括:通過打開相冊裡的圖片,使用Mat