Android app應用性能優化

1、名詞解釋

內存溢出html

實質應用程序不能及時釋放內存或者加載到內存上的數據太大而致使的OOM問題java

內存泄漏android

目標類被其餘類持有、致使沒法銷燬、從而致使目標類沒法被調用也沒法銷燬。緩存

窗體(內存)泄露數據結構

是指Activity或者Fragment在Destory的狀況下、自身引用被其餘對象或者線程持有,沒法銷燬。app

棧內存溢出框架

‍StackOverflowError:應用程序調用中,致使棧空間無限延長,超過了虛擬機的承載能力‍異步

 

2、App內存回收機制

app使用內存超過3/4而且不是當前的app,那麼系統會回收內存(android.app.ActivityThread)ide

Android系統內存不足時,組建回收順序問題oop

http://www.2cto.com/kf/201512/453248.html

 

(空進程,死亡進程,後臺進程)

省電方面

Android 應用開發的耗電量控制

使用StrictMode嚴格模式檢測不良代碼

3、代碼優化

1>Handler的使方式和內存優化

消息隊列遵循先進先出(First in First out)的原則來說某些信息或者任務進行排隊等待。android中有本身的消息隊列,如Handler和Broadcastreceiver。

andriod提供了 Handler 和 Looper 來知足線程間的通訊。Handler 先進先出原則。Looper類用來管理特定線程內對象之間的消息交換(Message Exchange)。
1)Looper: 一個線程能夠產生一個Looper對象,由它來管理此線程裏的Message Queue(消息隊列)。
2)Handler: 你能夠構造Handler對象來與Looper溝通,以便push新消息到Message Queue裏;或者接收Looper從Message Queue取出)所送來的消息。
3) Message Queue(消息隊列):用來存放線程放入的消息。
4)線程:UI thread 一般就是main thread,而Android啓動程序時會替它創建一個Message Queue。

Handler的集中建立方式和用法

Handler mHandler = new Handler() {  
          public void handleMessage(Message msg) {   
               switch (msg.what) {   
                    case TestHandler.GUIUPDATEIDENTIFIER:   
                         myBounceView.invalidate();  
                         break;   
               }   
               super.handleMessage(msg);   
          }   
     };

在工做線程中建立(異步)Handler

class LooperThread extends Thread
{
public Handler mHandler;
public void run() 
{
    Looper.prepare();
    mHandler = new Handler() 
    {
            public void handleMessage(Message msg) 
            {
            
            }
        };
    Looper.loop();
}

使用HandlerThread建立(異步)Handler

HandlerThread handlerThread = new HandlerThread("my.handlerthread");
handlerThread.start(); 

Handler mHandler = new Handler(handlerThread.getLooper()){
   public void handleMessage(Message msg) 
   {
            
   }
};

 

Handler的用法不少,這裏只貼出線程通訊的用法,讀者能夠自行深刻研究其餘用法

class myThread implements Runnable {   
          public void run() {  
               while (!Thread.currentThread().isInterrupted()) {    
                    //這裏可使用 new,由於主線程中的消息隊列只有一條
                    Message message = new Message(); 
                    message.what = 1024;   
                      
                    TmHandler.sendMessage(message);   
                    try {   
                         Thread.sleep(100);    
                    } catch (InterruptedException e) {   
                         Thread.currentThread().interrupt();   
                    }   
               }   
          }   
     }

1.1>Handler關於內存泄露

當咱們這樣寫在一個Activity中時,Android Lint會提示咱們這樣一個 warning: In Android, Handler classes should be static or leaks might occur.。
意思說:在Android中,Handler 類應該是靜態的不然可能發生泄漏

一樣在Eclipse代碼編輯區域的 Handler定義行也會出現相似的警告,通常都會加上supresslint的註解

先來看看,爲何會出現這種問題:

1.當Android程序第一次建立的時候,在主線程同時會建立一個Looper對象。Looper實現了一個簡單的消息隊列,一個接着一個處理Message對象。
程序框架全部主要的事件(例如:屏幕上的點擊時間,Activity生命週期的方法等等)都包含在Message對象中,而後添加到Looper的消息隊列中,
一個一個處理。主線程的Looper存在整個應用程序的生命週期內。
2.當一個Handler對象在主線程中建立的時候,它會關聯到Looper的 message queue 。Message添加到消息隊列中的時候Message
會持有當前Handler引用,當Looper處理到當前消息的時候,會調用Handler#handleMessage(Message).
3.在java中,no-static的內部類會 隱式的 持有當前類的一個引用。static的類則沒有。

綜上三點可知,這種泄露很是危險,Activity或者被持有者(宿主)不能及時釋放內存,Looper也在不斷循環,是致使內存泄露緣由之一,

緣由之二是 Activity未被回收,當Activity處於非活動狀態時,若是handlerMessage的處理致使UI的改變,將會致使窗體泄露,好比彈框,UI改變等。

1.2定義靜態類

  /** 
   * 使用靜態的內部類,不會持有當前對象的引用 
   */ 
  private static class MyHandler extends Handler 
  { 
    private SoftReference<Activity> mActivityReference = null; 
 
    public MyHandler(SampleActivity activity) 
    { 
       mActivityReference = new SoftReference<Activity>(activity)
    } 
 
    @Override 
    public void handleMessage(Message msg) { 
      SampleActivity activity = (SampleActivity )mActivityReference.get(); 
      if (activity != null) { 
        // ... 
      } 
    } 
    
    public void release()
    {
       removeCallbacksAndMessages(null);
       mActivityReference.clear();

    }
  } 
 
  private final MyHandler mHandler = new MyHandler(this);

在Activity的OnDestroy中調用

  public void onDestroy()
  {
       mHandler.release();
  }

1.3 Handler替代方案

有些時候,Handler無需本身定義,咱們可使用View自身提供的Handler進行操做

myView.post()

myView.postDelay()

myView.getHandler()

runOnUiThread()

.............................

LocalBroadcastReceiver

BroadcastReceiver

或者使用開源方案EventBus

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

2>避免(static)全局化或被全局對象持有,嚴格按照Android註冊/解除機制

2.1 避免全局static化

對於廣播,或者一些Runnable,CallBacks甚至普通內部類,咱們要避免全局化,致使變量不能被釋放,從而對象也不能釋放

這裏所說的全局化是被靜態對象持有,好比單例,全局靜態List,Map等

2.2全局化每每是容易持有對象,所以咱們必須學會

add——remove

addAll——clearAll

register——unregister

bind——unbind

 

這裏相關的主要有View,ViewTreeObserver,ContentObserver,static Map,static List, static SparseArray,singleObject如

View.addOnAttachStateChangeListener

View.addOnLayoutChangeListener

ViewTreeObserver.add

registerBroadcast

bindService

registerContentObserver

........

2.3若是全局化對象非要持有Context對象

建議使其持有ApplicationContext對象,而不是Service,BroadCastReceiver或者Activity

舉個栗子

android.support.v4.content.LocalBroadcastManager的getInstance

   private static LocalBroadcastManager mInstance;
   
  public static LocalBroadcastManager getInstance(Context context)
  {
        if(mInstance == null)
            mInstance = new LocalBroadcastManager(context.getApplicationContext());
        return mInstance;
       
    }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

3>Bitmap內存優化

Android中圖片有四種屬性,分別是:
ALPHA_8:每一個像素佔用1byte內存
ARGB_4444:每一個像素佔用2byte內存
ARGB_8888:每一個像素佔用4byte內存 (默認)
RGB_565:每一個像素佔用2byte內存
Android默認的顏色模式爲ARGB_8888,這個顏色模式色彩最細膩,顯示質量最高。但一樣的,佔用的內存也最大。 因此在對圖片效果不是特別高的狀況下使用RGB_565(565沒有透明度屬性)

publicstaticBitmapreadBitMap(Contextcontext, intresId) {
    BitmapFactory.Optionsopt = newBitmapFactory.Options();
    opt.inPreferredConfig = Bitmap.Config.RGB_565;
    opt.inPurgeable = true;
    opt.inInputShareable = true;
    //獲取資源圖片 
    InputStreamis = context.getResources().openRawResource(resId);
    returnBitmapFactory.decodeStream(is, null, opt);
}
// 先判斷是否已經回收
if(bitmap != null && !bitmap.isRecycled()){
    // 回收而且置爲null
    bitmap.recycle();
    bitmap = null;
}

3.1Bitmap調用的時候有個方法是recycle,用來釋放內存

3.2使用SoftReference<Bitmap>,WeakReference<Bitmap>進行小內存引用管理

3.3創建磁盤緩存LruDisk,LruCache,具體須要Http 304文件校驗,Http 206斷點續傳等方面知識

3.4進行圖片壓縮,具體參考http://my.oschina.net/ososchina/blog/495861

 

4>使用Android中高效的數據結構

具體用法請參考http://my.oschina.net/ososchina/blog/355721

 

5>關閉IO流,關閉Cursor、關閉Formatter、按時結束線程

 

6>.普通內部類(非靜態內部類)

問題一,主類釋放問題:內部類隱式持有主類的當前對象,對於內部類需不須要和Handler同樣進行靜態化,徹底取決於內部類會不會被全局(static/單例)對象引用,若是被全局(static/單例)對象引用可能形成沒法釋放,不然徹底不要過於擔憂,由於內部類對主類對象是強引用。

問題二.循環引用問題:

(所謂循環引用是2個貨2個以上的相互關聯的類的對象互相引用,形成了一種引用閉環問題,這種引用形成的問題是閉環內的全部對象都沒法及時銷燬)

Java的虛擬機機制避免了此類問題,可是爲了App性能,咱們須要特別注意此類問題,儘量提早釋放內存(release memory)。

 class MyAdapter extends BaseAdapter {
    
            private ListView listView;//用於接收傳遞過來的Context對象
            public MyImgAdapter(ListView listView) {
                super();
                this.listView = listView;
                this.listView.setAdapter(this);  //從這裏開始出現循環引用
            }

            @Override
            public int getCount() {
                return imgs.length;
            }

            @Override
            public Object getItem(int position) {
                return position;
            }

            
            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
           
                return convertView;
            }

            public void release()
            {
            //如下2句選擇一種便可解除循環引用,可是爲了效率,都用吧
              this.listView.setAdapter(null); 
              this.listView = null; 
            }
        }

7>判斷Activity的狀態機制,在Activity被壓入棧中時阻止某些設計UI的API操做,防止窗體泄露

參考:Android postDelay+Dialog引發的窗體泄露

8>對Adapter中的View進行緩存

findViewById自己不須要緩存,由於每次都是樹形遍歷,而LayoutInflater每次都是建立一個新的View,因此必要時使用ViewHolder或者ViewModel進行緩存

 

9>不要過於使用枚舉,枚舉在內存中所佔字節是2倍的靜態變量所佔的字節

 

10.提早釋放某些再也不用或者急須要減小內存的變量

this.field = null;
---------------------------------------------------------------------------------------------------
ViewGroup  container= getWindow().getDecorView().findViewById(android.R.id.content);
container.removeAllViews();
---------------------------------------------------------------------------------------------------
Iterator it = mImageList.iterator();
while(it.hasNext())
{
   Bitmap bmp = it.next();
    bmp.recycle();
     it.remove();
}
---------------------------------------------------------------------------------------------------
mList.clear();

 

11.避免AsyncTask

沒法清空隊列中的任務、而且不能超過128個任務

相關文章
相關標籤/搜索