日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Android之ListView原理學(xué)習(xí)與優(yōu)化總結(jié)

在整理前幾篇文章的時(shí)候有朋友提出寫一下ListView的性能優(yōu)化方面的東西,這個(gè)問(wèn)題也是小馬在面試過(guò)程中被別人問(wèn)到的…..今天小馬就借此機(jī)會(huì)來(lái)整理下,網(wǎng)上類似的資料蠻多的,倒不如自己寫一篇,記錄在這個(gè)地方,供自己以后使用,不用再翻來(lái)翻去的找了,用自己寫的…呵呵,不多講其它了,說(shuō)起優(yōu)化我想大家第一反應(yīng)跟小馬一樣吧?想到利用ViewHolder來(lái)優(yōu)化ListView數(shù)據(jù)加載,僅僅就此一條嗎?其實(shí)不是的,首先,想要優(yōu)化ListView就得先了解ListView加載數(shù)據(jù)原理,這是前提,但是小馬在這個(gè)地方先做一些簡(jiǎn)單的補(bǔ)充,大家一定仔細(xì)看下,保證會(huì)有收獲的:

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、萊州網(wǎng)絡(luò)推廣、成都小程序開發(fā)、萊州網(wǎng)絡(luò)營(yíng)銷、萊州企業(yè)策劃、萊州品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供萊州建站搭建服務(wù),24小時(shí)服務(wù)熱線:18980820575,官方網(wǎng)址:www.cdcxhl.com

列表的顯示需要三個(gè)元素:

  1. ListVeiw:  用來(lái)展示列表的View。

  2. 適配器 : 用來(lái)把數(shù)據(jù)映射到ListView上

  3. 數(shù)據(jù):    具體的將被映射的字符串,圖片,或者基本組件。 

根據(jù)列表的適配器類型,列表分為三種,ArrayAdapter,SimpleAdapter和SimpleCursorAdapter,這三種適配器的使用大家可學(xué)習(xí)下官網(wǎng)上面的使用或者自行百度谷歌,一堆DEMO?。?!其中以ArrayAdapter最為簡(jiǎn)單,只能展示一行字。SimpleAdapter有最好的擴(kuò)充性,可以自定義出各種效果。SimpleCursorAdapter可以認(rèn)為是SimpleAdapter對(duì)數(shù)據(jù)庫(kù)的簡(jiǎn)單結(jié)合,可以方便的把數(shù)據(jù)庫(kù)的內(nèi)容以列表的形式展示出來(lái)。

系統(tǒng)要繪制ListView了,他首先用getCount()函數(shù)得到要繪制的這個(gè)列表的長(zhǎng)度,然后開始繪制第一行,怎么繪制呢?調(diào)用getView()函數(shù)。在這個(gè)函數(shù)里面首先獲得一個(gè)View(這個(gè)看實(shí)際情況,如果是一個(gè)簡(jiǎn)單的顯示則是View,如果是一個(gè)自定義的里面包含很多控件的時(shí)候它其實(shí)是一個(gè)ViewGroup),然后再實(shí)例化并設(shè)置各個(gè)組件及其數(shù)據(jù)內(nèi)容并顯示它。好了,繪制完這一行了。那 再繪制下一行,直到繪完為止,前面這些東西做下鋪墊,繼續(xù)…….

 現(xiàn)在我們?cè)賮?lái)了解ListView加載數(shù)據(jù)的原理,有了這方面的了解后再說(shuō)優(yōu)化才行,下面先跟大家一起來(lái)看下ListView加載數(shù)據(jù)的基本原理小馬就直接寫了:

ListView的工作原理如下:

ListView 針對(duì)每個(gè)item,要求 adapter “返回一個(gè)視圖” (getView),也就是說(shuō)ListView在開始繪制的時(shí)候,系統(tǒng)首先調(diào)用getCount()函數(shù),根據(jù)他的返回值得到ListView的長(zhǎng)度,然后根據(jù)這個(gè)長(zhǎng)度,調(diào)用getView()一行一行的繪制ListView的每一項(xiàng)。如果你的getCount()返回值是0的話,列表一行都不會(huì)顯示,如果返回1,就只顯示一行。返回幾則顯示幾行。如果我們有幾千幾萬(wàn)甚至更多的item要顯示怎么辦?為每個(gè)Item創(chuàng)建一個(gè)新的View?不可能?。?!實(shí)際上Android早已經(jīng)緩存了這些視圖,大家可以看下下面這個(gè)截圖來(lái)理解下,這個(gè)圖是解釋ListView工作原理的最經(jīng)典的圖了大家可以收藏下,不懂的時(shí)候拿來(lái)看看,加深理解,其實(shí)Android中有個(gè)叫做Recycler的構(gòu)件,順帶列舉下與Recycler相關(guān)的已經(jīng)由Google做過(guò)N多優(yōu)化過(guò)的東東比如:AbsListView.RecyclerListener、ViewDebug.RecyclerTraceType等等,要了解的朋友自己查下,不難理解,下圖是ListView加載數(shù)據(jù)的工作原理(原理圖看不清楚的點(diǎn)擊后看大圖):

下面簡(jiǎn)單說(shuō)下上圖的原理:

  1. 如果你有幾千幾萬(wàn)甚至更多的選項(xiàng)(item)時(shí),其中只有可見的項(xiàng)目存在內(nèi)存(內(nèi)存內(nèi)存哦,說(shuō)的優(yōu)化就是說(shuō)在內(nèi)存中的優(yōu)化!?。。┲?,其他的在Recycler中
  2. ListView先請(qǐng)求一個(gè)type1視圖(getView)然后請(qǐng)求其他可見的項(xiàng)目。convertView在getView中是空(null)的
  3. 當(dāng)item1滾出屏幕,并且一個(gè)新的項(xiàng)目從屏幕低端上來(lái)時(shí),ListView再請(qǐng)求一個(gè)type1視圖。convertView此時(shí)不是空值了,它的值是item1。你只需設(shè)定新的數(shù)據(jù)然后返回convertView,不必重新創(chuàng)建一個(gè)視圖
  4. 下面來(lái)看下小馬從網(wǎng)上找來(lái)的示例代碼,網(wǎng)址搞丟了,只有一個(gè)word文檔,只能 copy過(guò)來(lái),不然直接貼網(wǎng)址,結(jié)合上面的原理圖一起加深理解,如下:
 
 
 
 
  1. public class MultipleItemsList extends ListActivity {    
  2.  private MyCustomAdapter mAdapter;     
  3. @Override     
  4. public void onCreate(Bundle savedInstanceState) {         
  5. super.onCreate(savedInstanceState);        
  6.  mAdapter = new MyCustomAdapter();         
  7. for (int i = 0; i < 50; i++) {            
  8.  mAdapter.addItem("item " + i);         
  9. }         
  10. setListAdapter(mAdapter);     
  11. }     
  12. private class MyCustomAdapter extends BaseAdapter {        
  13. private ArrayList mData = new ArrayList();        
  14.  private LayoutInflater mInflater;          
  15. public MyCustomAdapter() {             
  16. mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);        
  17.  }        
  18. public void addItem(final String item) {             
  19. mData.add(item);            
  20. notifyDataSetChanged();        
  21.  }         
  22.  
  23. @Override        
  24. public int getCount() {             
  25. return mData.size();        
  26.  }        
  27.  @Override        
  28.  public String getItem(int position) {            
  29.  return mData.get(position);         
  30. }          
  31. @Override       
  32.  public long getItemId(int position) {           
  33.  return position;        
  34. }          
  35. @Override        
  36. public View getView(int position, View convertView, ViewGroup parent) {            
  37.  System.out.println("getView " + position + " " + convertView);            
  38. ViewHolder holder = null;             
  39. if (convertView == null) {                 
  40. convertView = mInflater.inflate(R.layout.item1, null);                 
  41. holder = new ViewHolder();                
  42. holder.textView = (TextView)convertView.findViewById(R.id.text);                
  43.  convertView.setTag(holder);             
  44. } else {                
  45.  holder = (ViewHolder)convertView.getTag();         
  46.    }            
  47.  holder.textView.setText(mData.get(position));            
  48.  return convertView;       
  49.  }    }    
  50.  public static class ViewHolder {        
  51.  public TextView textView;    
  52.  } }  

執(zhí)行程序,查看日志:

getView 被調(diào)用 9 次 ,convertView 對(duì)于所有的可見項(xiàng)目是空值(如下):

然后稍微向下滾動(dòng)List,直到item10出現(xiàn):

       convertView仍然是空值,因?yàn)閞ecycler中沒(méi)有視圖(item1的邊緣仍然可見,在頂端)再滾動(dòng)列表,繼續(xù)滾動(dòng):

      convertView不是空值了!item1離開屏幕到Recycler中去了,然后item11被創(chuàng)建,再滾動(dòng)下:

此時(shí)的convertView非空了,在item11離開屏幕之后,它的視圖(…0f8)作為convertView容納item12了,好啦,結(jié)合以上原理,下面來(lái)看看今天最主要的話題,主角ListView的優(yōu)化:

             首先,這個(gè)地方先記兩個(gè)ListView優(yōu)化的一個(gè)小點(diǎn):

                       1. ExpandableListView 與 ListActivity 由官方提供的,里面要使用到的ListView是已經(jīng)經(jīng)過(guò)優(yōu)化的ListView,如果大家的需求可以用Google自帶的ListView滿足的的話盡量用官方的,絕對(duì)沒(méi)錯(cuò)!

                       2.其次,像小馬前面講的,說(shuō)ListView優(yōu)化,其實(shí)并不是指其它的優(yōu)化,就是內(nèi)存是的優(yōu)化,提到內(nèi)存…(想到OOM,折騰了我不少時(shí)間),很多很多,先來(lái)寫下,如果我們的ListView中的選項(xiàng)僅僅是一些簡(jiǎn)單的TextView的話,就好辦啦,消耗不了多少的,但如果你的Item是自定義的Item的話,例如你的自定義Item布局ViewGroup中包含:按鈕、圖片、flash、CheckBox、RadioButton等一系列你能想到的控件的話, 你要在getView中單單使用文章開頭提到的ViewHolder是遠(yuǎn)遠(yuǎn)不夠的,如果數(shù)據(jù)過(guò)多,加載的圖片過(guò)多過(guò)大,你BitmapFactory.decode的猛多的話,OOM搞死你,這個(gè)地方再警告下大家,是警告……….也提醒下自己:

                         小馬碰到的問(wèn)題大家應(yīng)該也都碰到過(guò)的,自定義的ListView項(xiàng)亂序問(wèn)題,我很天真的在getView()中強(qiáng)制清除了下ListView的緩存數(shù)據(jù)convertView,也就是convertView = null了,雖然當(dāng)時(shí)是解決了這個(gè)問(wèn)題讓其它每次重繪,但是犯了大錯(cuò)了,如果數(shù)據(jù)太多的話,出現(xiàn)最最惡心的錯(cuò),手機(jī)卡死或強(qiáng)制關(guān)機(jī),關(guān)機(jī)啊哥哥們……O_O,客戶殺了我都有可能,但大家以后別犯這樣的錯(cuò)了,單單使用清除緩存convertView是解決不了實(shí)際問(wèn)題的,繼續(xù)……

下面是小記:圖片用完了正確的釋放… 

 
 
 
 
  1. if(!bmp.isRecycle() ){        
  2. bmp.recycle()   //回收?qǐng)D片所占的內(nèi)存 
  3.  system.gc()  //提醒系統(tǒng)及時(shí)回收 
  4. }  

下面來(lái)列舉下真正意義上的優(yōu)化吧:

  1.  ViewHolder   Tag 必不可少,這個(gè)不多說(shuō)!
  2. 如果自定義Item中有涉及到圖片等等的,一定要狠狠的處理圖片,圖片占的內(nèi)存是ListView項(xiàng)中最惡心的,處理圖片的方法大致有以下幾種:
    2.1:不要直接拿個(gè)路徑就去循環(huán)decodeFile();這是找死….用Option保存圖片大小、不要加載圖片到內(nèi)存去;
    2.2:  拿到的圖片一定要經(jīng)過(guò)邊界壓縮
    2.3:在ListView中取圖片時(shí)也不要直接拿個(gè)路徑去取圖片,而是以WeakReference(使用WeakReference代替強(qiáng)引用。比如可以使        用WeakReference mContextRef)、SoftReference、WeakHashMap等的來(lái)存儲(chǔ)圖片信息,是圖片信息不是圖片哦!
    2.4:在getView中做圖片轉(zhuǎn)換時(shí),產(chǎn)生的中間變量一定及時(shí)釋放,用以下形式:
  3. 盡量避免在BaseAdapter中使用static 來(lái)定義全局靜態(tài)變量,我以為這個(gè)沒(méi)影響 ,這個(gè)影響很大,static是Java中的一個(gè)關(guān)鍵字,當(dāng)用它來(lái)修飾成員變量時(shí),那么該變量就屬于該類,而不是該類的實(shí)例。所以用static修飾的變量,它的生命周期是很長(zhǎng)的,如果用它來(lái)引用一些資源耗費(fèi)過(guò)多的實(shí)例(比如Context的情況最多),這時(shí)就要盡量避免使用了..
  4. 如果為了滿足需求下必須使用Context的話:Context盡量使用Application Context,因?yàn)锳pplication的Context的生命周期比較長(zhǎng),引用它不會(huì)出現(xiàn)內(nèi)存泄露的問(wèn)題
  5. 盡量避免在ListView適配器中使用線程,因?yàn)榫€程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控制
  6.  記下小馬自己的錯(cuò)誤:
  7. 之前使用的自定義ListView中適配數(shù)據(jù)時(shí)使用AsyncTask自行開啟線程的,這個(gè)比用Thread更危險(xiǎn),因?yàn)門hread只有在run函數(shù)不 結(jié)束時(shí)才出現(xiàn)這種內(nèi)存泄露問(wèn)題,然而AsyncTask內(nèi)部的實(shí)現(xiàn)機(jī)制是運(yùn)用了線程執(zhí)行池(ThreadPoolExcutor,要想了解這個(gè)類的話大家加下我們的Android開發(fā)群五號(hào),因?yàn)槠渌旱拇鎯?chǔ)空間快滿了,所以只上傳到五群里了,看下小馬上傳的Gallery源碼,你會(huì)對(duì)線程執(zhí)行池、軟、弱、強(qiáng)引用有個(gè)更深入的認(rèn)識(shí)),這個(gè)類產(chǎn)生的Thread對(duì)象的生命周期是不確定的,是應(yīng)用程序無(wú)法控制的,因此如果AsyncTask作為Activity的內(nèi)部類,就更容易出現(xiàn)內(nèi)存泄露的問(wèn)題。這個(gè)問(wèn)題的解決辦法小馬當(dāng)時(shí)網(wǎng)上查到了記在txt里了,如下: 
    6.1:將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類。
    6.2:在線程內(nèi)部采用弱引用保存Context引用
    示例代碼如下:
     
  8.   
      
      
      
    1. public abstract class WeakAsyncTask extends  AsyncTask {        
    2.  protected WeakReference mTarget;            
    3. public WeakAsyncTask(WeakTarget target) {             
    4. mTarget = new WeakReference(target);         
    5. }                  
    6. @Override        
    7. protected final void onPreExecute() {            
    8. final WeakTarget target = mTarget.get();             
    9. if (target != null) {                 
    10. this.onPreExecute(target);             
    11. }        
    12.  
    13. }                   
    14.  @Override         
    15. protected final Result doInBackground(Params... params) {             
    16. final WeakTarget target = mTarget.get();            
    17. if (target != null) {                 
    18. return this.doInBackground(target, params);             
    19. } else {                
    20. return null;             
    21. }         
    22. }                    
    23. @Override        
    24. protected final void onPostExecute(Result result) {            
    25.  final WeakTarget target = mTarget.get();             
    26. if (target != null) {                 
    27. this.onPostExecute(target, result);           
    28. }        
    29. }         
    30.  protected void onPreExecute(WeakTarget target) {            
    31. // No default action         }           
    32.  protected abstract Result doInBackground(WeakTarget target, Params... params);           
    33. protected void onPostExecute(WeakTarget target, Result result) {           
    34.  // No default action         }     }  

好啦,ListVIew的優(yōu)化問(wèn)題,小馬就暫時(shí)先理解記錄這么多了,如果朋友們有什么更好的優(yōu)化建議什么的,留言指點(diǎn)下小馬,一定會(huì)及時(shí)添加到進(jìn)來(lái)的,先謝謝啦,其實(shí)在ListView適配器的getView()方法中可以做很多的優(yōu)化,我記得還有可以優(yōu)化findViewById()這個(gè)方法來(lái)尋址資源信息效率的方法,資料太多了,小馬發(fā)現(xiàn)了會(huì)及時(shí)更新的哦,天太晚了,先休息了,吼吼,大家加油,一起努力學(xué)習(xí)!?。_O


本文名稱:Android之ListView原理學(xué)習(xí)與優(yōu)化總結(jié)
網(wǎng)頁(yè)鏈接:http://www.5511xx.com/article/coihjdi.html