Android四大組件
1.ContentProvider
共享應(yīng)用程序內(nèi)的數(shù)據(jù),在數(shù)據(jù)修改時(shí)可以監(jiān)聽(tīng)
1.Activity
供用戶操作的界面
2.BroadcastReceiver
用來(lái)接收廣播,可以根據(jù)系統(tǒng)發(fā)生的一些時(shí)間做出一些處理
3.Service
長(zhǎng)期在后臺(tái)運(yùn)行的,沒(méi)有界面的組件,用來(lái)在后臺(tái)執(zhí)行一些耗時(shí)的操作
?熟悉掌握ListView的優(yōu)化及異步任務(wù)加載網(wǎng)絡(luò)數(shù)據(jù)
一、異步任務(wù)加載網(wǎng)絡(luò)數(shù)據(jù):
在Android中提供了一個(gè)異步任務(wù)的類AsyncTask,簡(jiǎn)單來(lái)說(shuō),這個(gè)類中的任務(wù)是運(yùn)行在后臺(tái)線程中的,并可以將結(jié)果放到UI線程中進(jìn)行處理,它定義了三種泛型,分別是Params、Progress和Result,分別表示請(qǐng)求的參數(shù)、任務(wù)的進(jìn)度和獲得的結(jié)果數(shù)據(jù)。
1、使用原因:
1)是其中使用了線程池技術(shù),而且其中的方法很容易實(shí)現(xiàn)調(diào)用
2)可以調(diào)用相關(guān)的方法,在開(kāi)啟子線程前和后,進(jìn)行界面的更新
3)一旦任務(wù)多了,不用每次都new新的線程,可以直接使用
2、執(zhí)行的順序:
onPreExecute()【執(zhí)行前開(kāi)啟】--- > doInBackground() --- > onProgressUpdate() --- >onPostExecute()
3、執(zhí)行過(guò)程:
當(dāng)一個(gè)異步任務(wù)開(kāi)啟后,執(zhí)行過(guò)程如下:
1)、onPreExecute():
這個(gè)方法是執(zhí)行在主線程中的。這步操作是用于準(zhǔn)備好任務(wù)的,作為任務(wù)加載的準(zhǔn)備工作。建議在這個(gè)方法中彈出一個(gè)提示框。
2)、doInBackground():
這個(gè)方法是執(zhí)行在子線程中的。在onPreExecute()執(zhí)行完后,會(huì)立即開(kāi)啟這個(gè)方法,在方法中可以執(zhí)行耗時(shí)的操作。需要將請(qǐng)求的參數(shù)傳遞進(jìn)來(lái),發(fā)送給服務(wù)器,并將獲取到的數(shù)據(jù)返回,數(shù)據(jù)會(huì)傳給最后一步中;這些值都將被放到主線程中,也可以不斷的傳遞給下一步的onProgressUpdate()中進(jìn)行更新??梢酝ㄟ^(guò)不斷調(diào)用publishProgress(),將數(shù)據(jù)(或進(jìn)度)不斷傳遞給onProgressUpdate()方法,進(jìn)行不斷更新界面。
3)、onProgressUpdate():
這個(gè)方法是執(zhí)行在主線程中的。publishProgress()在doInBackground()中被調(diào)用后,才開(kāi)啟的這個(gè)方法,它在何時(shí)被開(kāi)啟是不確定的,執(zhí)行這個(gè)方法的過(guò)程中,doInBackground()是仍在執(zhí)行的,即子線程還在運(yùn)行著。
4)、onPostExecute():
這個(gè)方法是執(zhí)行在主線程中的。當(dāng)后臺(tái)的子線程執(zhí)行完畢后才調(diào)用此方法。doInBackground()返回的結(jié)果會(huì)作為參數(shù)被傳遞過(guò)來(lái)??梢栽谶@個(gè)方法中進(jìn)行更新界面的操作。
5)、execute():
最后創(chuàng)建AsyncTask自定義的類,開(kāi)啟異步任務(wù)。
3、實(shí)現(xiàn)原理:
1)、線程池的創(chuàng)建:
在創(chuàng)建了AsyncTask的時(shí)候,會(huì)默認(rèn)創(chuàng)建一個(gè)線程池ThreadPoolExecutor,并默認(rèn)創(chuàng)建出5個(gè)線程放入到線程池中,最多可防128個(gè)線程;且這個(gè)線程池是公共的唯一一份。
2)、任務(wù)的執(zhí)行:
在execute中,會(huì)執(zhí)行run方法,當(dāng)執(zhí)行完run方法后,會(huì)調(diào)用scheduleNext()不斷的從雙端隊(duì)列中輪詢,獲取下一個(gè)任務(wù)并繼續(xù)放到一個(gè)子線程中執(zhí)行,直到異步任務(wù)執(zhí)行完畢。
3)、消息的處理:
在執(zhí)行完onPreExecute()方法之后,執(zhí)行了doInBackground()方法,然后就不斷的發(fā)送請(qǐng)求獲取數(shù)據(jù);在這個(gè)AsyncTask中維護(hù)了一個(gè)InternalHandler的類,這個(gè)類是繼承Handler的,獲取的數(shù)據(jù)是通過(guò)handler進(jìn)行處理和發(fā)送的。在其handleMessage方法中,將消息傳遞給onProgressUpdate()進(jìn)行進(jìn)度的更新,也就可以將結(jié)果發(fā)送到主線程中,進(jìn)行界面的更新了。
4、需要注意的是:
①、這個(gè)AsyncTask類必須由子類調(diào)用
②、雖然是放在子線程中執(zhí)行的操作,但是不建議做特別耗時(shí)的操作,如果操作過(guò)于耗時(shí),建議使用線程池ThreadPoolExecutor和FutureTask
示例代碼:

二、ListView優(yōu)化:
ListView的工作原理
首先來(lái)了解一下ListView的工作原理(可參見(jiàn)http://mobile.51cto.com/abased-410889.htm),如圖:
1、如果你有幾千幾萬(wàn)甚至更多的選項(xiàng)(item)時(shí),其中只有可見(jiàn)的項(xiàng)目存在內(nèi)存(內(nèi)存內(nèi)存哦,說(shuō)的優(yōu)化就是說(shuō)在內(nèi)存中的優(yōu)化?。。。┲校渌脑赗ecycler中
2、ListView先請(qǐng)求一個(gè)type1視圖(getView)然后請(qǐng)求其他可見(jiàn)的項(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è)視圖

一、復(fù)用convertView
,減少findViewById
的次數(shù)
1、優(yōu)化一:復(fù)用convertView
Android系統(tǒng)本身為我們考慮了ListView的優(yōu)化問(wèn)題,在復(fù)寫(xiě)的Adapter的類中,比較重要的兩個(gè)方法是getCount()和getView()。界面上有多少個(gè)條顯示,就會(huì)調(diào)用多少次的getView()方法;因此如果在每次調(diào)用的時(shí)候,如果不進(jìn)行優(yōu)化,每次都會(huì)使用View.inflate(….)的方法,都要將xml文件解析,并顯示到界面上,這是非常消耗資源的:因?yàn)橛行碌膬?nèi)容產(chǎn)生就會(huì)有舊的內(nèi)容銷毀,所以,可以復(fù)用舊的內(nèi)容。
優(yōu)化:
在getView()方法中,系統(tǒng)就為我們提供了一個(gè)復(fù)用view的歷史緩存對(duì)象convertView,當(dāng)顯示第一屏的時(shí)候,每一個(gè)item都會(huì)新創(chuàng)建一個(gè)view對(duì)象,這些view都是可以被復(fù)用的;如果每次顯示一個(gè)view都要?jiǎng)?chuàng)建一個(gè),是非常耗費(fèi)內(nèi)存的;所以為了節(jié)約內(nèi)存,可以在convertView不為null的時(shí)候,對(duì)其進(jìn)行復(fù)用
2、優(yōu)化二:緩存item條目的引用——ViewHolder
findViewById()這個(gè)方法是比較耗性能的操作,因?yàn)檫@個(gè)方法要找到指定的布局文件,進(jìn)行不斷地解析每個(gè)節(jié)點(diǎn):從最頂端的節(jié)點(diǎn)進(jìn)行一層一層的解析查詢,找到后在一層一層的返回,如果在左邊沒(méi)找到,就會(huì)接著解析右邊,并進(jìn)行相應(yīng)的查詢,直到找到位置(如圖)。因此可以對(duì)findViewById進(jìn)行優(yōu)化處理,需要注意的是:
》》》》特點(diǎn):xml文件被解析的時(shí)候,只要被創(chuàng)建出來(lái)了,其孩子的id就不會(huì)改變了。根據(jù)這個(gè)特點(diǎn),可以將孩子id存入到指定的集合中,每次就可以直接取出集合中對(duì)應(yīng)的元素就可以了。

優(yōu)化:
在創(chuàng)建view對(duì)象的時(shí)候,減少布局文件轉(zhuǎn)化成view對(duì)象的次數(shù);即在創(chuàng)建view對(duì)象的時(shí)候,把所有孩子全部找到,并把孩子的引用給存起來(lái)
①定義存儲(chǔ)控件引用的類ViewHolder
這里的ViewHolder類需要不需要定義成static,根據(jù)實(shí)際情況而定,如果item不是很多的話,可以使用,這樣在初始化的時(shí)候,只加載一次,可以稍微得到一些優(yōu)化
不過(guò),如果item過(guò)多的話,建議不要使用。因?yàn)閟tatic是Java中的一個(gè)關(guān)鍵字,當(dāng)用它來(lái)修飾成員變量時(shí),那么該變量就屬于該類,而不是該類的實(shí)例。所以用static修飾的變量,它的生命周期是很長(zhǎng)的,如果用它來(lái)引用一些資源耗費(fèi)過(guò)多的實(shí)例(比如Context的情況最多),這時(shí)就要盡量避免使用了。
classViewHolder{
//定義item中相應(yīng)的控件
}
②創(chuàng)建自定義的類:ViewHolderholder = null;
③將子view添加到holder中:
在創(chuàng)建新的listView的時(shí)候,創(chuàng)建新的ViewHolder,把所有孩子全部找到,并把孩子的引用給存起來(lái)
通過(guò)view.setTag(holder)將引用設(shè)置到view中
通過(guò)holder,將孩子view設(shè)置到此holder中,從而減少以后查詢的次數(shù)
④在復(fù)用listView中的條目的時(shí)候,通過(guò)view.getTag(),將view對(duì)象轉(zhuǎn)化為holder,即轉(zhuǎn)化成相應(yīng)的引用,方便在下次使用的時(shí)候存入集合。
通過(guò)view.getTag(holder)獲取引用(需要強(qiáng)轉(zhuǎn))
示例代碼:


二、ListView
中數(shù)據(jù)的分批及分頁(yè)加載:
需求:
ListView有一萬(wàn)條數(shù)據(jù),如何顯示;如果將十萬(wàn)條數(shù)據(jù)加載到內(nèi)存,很消耗內(nèi)存
解決辦法:
優(yōu)化查詢的數(shù)據(jù):先獲取幾條數(shù)據(jù)顯示到界面上
進(jìn)行分批處理---à優(yōu)化了用戶體驗(yàn)
進(jìn)行分頁(yè)處理---à優(yōu)化了內(nèi)存空間
說(shuō)明:
一般數(shù)據(jù)都是從數(shù)據(jù)庫(kù)中獲取的,實(shí)現(xiàn)分批(分頁(yè))加載數(shù)據(jù),就需要在對(duì)應(yīng)的DAO中有相應(yīng)的分批(分頁(yè))獲取數(shù)據(jù)的方法,如findPartDatas ()
1、準(zhǔn)備數(shù)據(jù):
在dao中添加分批加載數(shù)據(jù)的方法:findPartDatas ()
在適配數(shù)據(jù)的時(shí)候,先加載第一批的數(shù)據(jù),需要加載第二批的時(shí)候,設(shè)置監(jiān)聽(tīng)檢測(cè)何時(shí)加載第二批
2、設(shè)置ListView的滾動(dòng)監(jiān)聽(tīng)器:setOnScrollListener(new OnScrollListener{….})
①、在監(jiān)聽(tīng)器中有兩個(gè)方法:滾動(dòng)狀態(tài)發(fā)生變化的方法(onScrollStateChanged)和listView被滾動(dòng)時(shí)調(diào)用的方法(onScroll)
②、在滾動(dòng)狀態(tài)發(fā)生改變的方法中,有三種狀態(tài):
手指按下移動(dòng)的狀態(tài):SCROLL_STATE_TOUCH_SCROLL: //觸摸滑動(dòng)
慣性滾動(dòng)(滑翔(flgin)狀態(tài)):SCROLL_STATE_FLING: //滑翔
靜止?fàn)顟B(tài):SCROLL_STATE_IDLE://靜止
3、對(duì)不同的狀態(tài)進(jìn)行處理:
分批加載數(shù)據(jù),只關(guān)心靜止?fàn)顟B(tài):關(guān)心最后一個(gè)可見(jiàn)的條目,如果最后一個(gè)可見(jiàn)條目就是數(shù)據(jù)適配器(集合)里的最后一個(gè),此時(shí)可加載更多的數(shù)據(jù)。在每次加載的時(shí)候,計(jì)算出滾動(dòng)的數(shù)量,當(dāng)滾動(dòng)的數(shù)量大于等于總數(shù)量的時(shí)候,可以提示用戶無(wú)更多數(shù)據(jù)了。
示例代碼:
/給listview注冊(cè)一個(gè)滾動(dòng)的監(jiān)聽(tīng)器.
lv_call_sms_safe.setOnScrollListener(newOnScrollListener() {
? ? ? ? ? ? ? ? ? ? ?//當(dāng)滾動(dòng)狀體發(fā)生變化的時(shí)候調(diào)用的方法
? ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ? ? publicvoid onScrollStateChanged(AbsListView view, int scrollState) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?switch(scrollState) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?caseSCROLL_STATE_FLING: //滑翔
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? caseSCROLL_STATE_IDLE: //靜止
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//在靜止?fàn)顟B(tài)下關(guān)心最后一個(gè)可見(jiàn)的條目如果最后一個(gè)可見(jiàn)條目就是數(shù)據(jù)適配器里面的最后一個(gè),加載更多數(shù)據(jù).
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? intposition = lv_call_sms_safe.getLastVisiblePosition(); //位置從0開(kāi)始
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? intsize = blackNumbers.size();//從1開(kāi)始的.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(position == (size - 1)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Log.i(TAG,"拖動(dòng)到了最后一個(gè)條目,加載更多數(shù)據(jù)");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?startIndex+= maxNumber;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(startIndex>=totalCount){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Toast.makeText(getApplicationContext(),"沒(méi)有更多數(shù)據(jù)了..",0).show();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fillData();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ?caseSCROLL_STATE_TOUCH_SCROLL: //觸摸滑動(dòng)
? ? ? ? ? ? ? ? ? ? ? ? ? break;
}
}
//當(dāng)listview被滾動(dòng)的時(shí)候調(diào)用的方法
@Override
publicvoid onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, inttotalItemCount) {
}
});
/**
*填充數(shù)據(jù)
*/
? ? ? ? ? ? ? ? private void fillData() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //通知用戶一下正在獲取數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ll_loading.setVisibility(View.VISIBLE);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?newThread() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?publicvoid run() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //獲取全部的黑名單號(hào)碼
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(blackNumbers != null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?blackNumbers.addAll(dao.findPartBlackNumbers(startIndex,maxNumber));
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?blackNumbers= dao.findPartBlackNumbers(startIndex,maxNumber);
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ?handler.sendEmptyMessage(0);
? ? ? ? ? ? ? ? ?//lv_call_sms_safe.setAdapter(new CallSmsSafeAdapter());
? ? ? ? ? ? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? ? ? ? ?}.start();
}


三、復(fù)雜ListView的處理:(待進(jìn)一步總結(jié))
說(shuō)明:
listView的界面顯示是通過(guò)getCount和getView這兩個(gè)方法來(lái)控制的
getCount:返回有多少個(gè)條目
getView:返回每個(gè)位置條目顯示的內(nèi)容
提供思路:
對(duì)于含有多個(gè)類型的item的優(yōu)化處理:由于ListView只有一個(gè)Adapter的入口,可以定義一個(gè)總的Adapter入口,存放各種類型的Adapter
以安全衛(wèi)士中的進(jìn)程管理的功能為例。效果如圖:
1、定義兩個(gè)(或多個(gè))集合
每個(gè)集合中存入的是對(duì)應(yīng)不同類型的內(nèi)容(這里為:用戶程序(userAppinfos)和系統(tǒng)程序的集合(systemAppinfos))
2、在初始化數(shù)據(jù)(填充數(shù)據(jù))中初始化兩個(gè)集合
如,此處是在fillData方法中初始化
3、在數(shù)據(jù)適配器中,復(fù)寫(xiě)對(duì)應(yīng)的方法
getCount():計(jì)算所有需要顯示的條目個(gè)數(shù),這里包括listView和textView
getView():對(duì)顯示在不同位置的條目進(jìn)行if處理
4、數(shù)據(jù)類型的判斷
需要注意的是,在復(fù)用view的時(shí)候,需要對(duì)convertView進(jìn)行類型判斷,是因?yàn)檫@里含有各種不同類型的view,在view滾動(dòng)顯示的時(shí)候,對(duì)于不同類型的view不能復(fù)用,所有需要判斷
示例代碼:
獲取條目個(gè)數(shù)

類型判斷:

getView中條目位置的選擇:

四、ListView中圖片的優(yōu)化:
1、處理圖片的方式:
如果自定義Item中有涉及到圖片等等的,一定要狠狠的處理圖片,圖片占的內(nèi)存是ListView項(xiàng)中最惡心的,處理圖片的方法大致有以下幾種:
①、不要直接拿路徑就去循環(huán)decodeFile();使用Option保存圖片大小、不要加載圖片到內(nèi)存去
②、拿到的圖片一定要經(jīng)過(guò)邊界壓縮
③、在ListView中取圖片時(shí)也不要直接拿個(gè)路徑去取圖片,而是以WeakReference(使用WeakReference代替強(qiáng)引用。
比如可以使用WeakReference?mContextRef)、SoftReference、WeakHashMap等的來(lái)存儲(chǔ)圖片信息,是圖片信息不是圖片哦!
④、在getView中做圖片轉(zhuǎn)換時(shí),產(chǎn)生的中間變量一定及時(shí)釋放
2、異步加載圖片基本思想:
1)、先從內(nèi)存緩存中獲取圖片顯示(內(nèi)存緩沖)
2)、獲取不到的話從SD卡里獲取(SD卡緩沖)
3)、都獲取不到的話從網(wǎng)絡(luò)下載圖片并保存到SD卡同時(shí)加入內(nèi)存并顯示(視情況看是否要顯示)
原理:
優(yōu)化一:先從內(nèi)存中加載,沒(méi)有則開(kāi)啟線程從SD卡或網(wǎng)絡(luò)中獲取,這里注意從SD卡獲取圖片是放在子線程里執(zhí)行的,否則快速滑屏的話會(huì)不夠流暢。
優(yōu)化二:于此同時(shí),在adapter里有個(gè)busy變量,表示listview是否處于滑動(dòng)狀態(tài),如果是滑動(dòng)狀態(tài)則僅從內(nèi)存中獲取圖片,沒(méi)有的話無(wú)需再開(kāi)啟線程去外存或網(wǎng)絡(luò)獲取圖片。
優(yōu)化三:ImageLoader里的線程使用了線程池,從而避免了過(guò)多線程頻繁創(chuàng)建和銷毀,有的童鞋每次總是new一個(gè)線程去執(zhí)行這是非常不可取的,好一點(diǎn)的用的AsyncTask類,其實(shí)內(nèi)部也是用到了線程池。在從網(wǎng)絡(luò)獲取圖片時(shí),先是將其保存到sd卡,然后再加載到內(nèi)存,這么做的好處是在加載到內(nèi)存時(shí)可以做個(gè)壓縮處理,以減少圖片所占內(nèi)存。
Tips:這里可能出現(xiàn)圖片亂跳(錯(cuò)位)的問(wèn)題:
圖片錯(cuò)位問(wèn)題的本質(zhì)源于我們的listview使用了緩存convertView,假設(shè)一種場(chǎng)景,一個(gè)listview一屏顯示九個(gè)item,那么在拉出第十個(gè)item的時(shí)候,事實(shí)上該item是重復(fù)使用了第一個(gè)item,也就是說(shuō)在第一個(gè)item從網(wǎng)絡(luò)中下載圖片并最終要顯示的時(shí)候,其實(shí)該item已經(jīng)不在當(dāng)前顯示區(qū)域內(nèi)了,此時(shí)顯示的后果將可能在第十個(gè)item上輸出圖像,這就導(dǎo)致了圖片錯(cuò)位的問(wèn)題。所以解決之道在于可見(jiàn)則顯示,不可見(jiàn)則不顯示。在ImageLoader里有個(gè)imageViews的map對(duì)象,就是用于保存當(dāng)前顯示區(qū)域圖像對(duì)應(yīng)的url集,在顯示前判斷處理一下即可。
Adapter示例代碼:
public class LoaderAdapter extendsBaseAdapter{
? ? ? ? ? private static final String TAG = "LoaderAdapter";
? ? ? ? ? private boolean mBusy = false;?????????? //是否處于滑動(dòng)中
? ? ? ? ? public void setFlagBusy(boolean busy) {
? ? ? ? ? this.mBusy = busy;
}
? ? ? ? ? private ImageLoader mImageLoader;
? ? ? ? ? private int mCount;
? ? ? ? ? private Context mContext;
? ? ? ? ? private String[] urlArrays;
? ? ? ? ? public LoaderAdapter(int count, Context context, String[]url) {
? ? ? ? ?this.mCount = count;
? ? ? ? ?this.mContext = context;
? ? ? ? ?urlArrays = url;
? ? ? ? ? mImageLoader = new ImageLoader(context);
}
? ? ? ? ? public ImageLoader getImageLoader(){
? ? ? ? ? return mImageLoader;
}
? ? ? ? ?@Override
? ? ? ? ?public int getCount() {
? ? ? ? ?return mCount;
}
? ? ? ?@Override
? ? ? ?public Object getItem(int position) {
? ? ? ?return position;
}
? ? ? ? @Override
? ? ? ? ?public long getItemId(int position) {
return position;
}
? ? ? ? @Override
? ? ? ? ?public View getView(int position, View convertView, ViewGroupparent) {
? ? ? ? ?ViewHolder viewHolder = null;
? ? ? ? ?if (convertView == null) {//加載新創(chuàng)建的view
? ? ? ? ?convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item,null);
? ? ? ? ?viewHolder = new ViewHolder();
? ? ? ? ?viewHolder.mTextView = (TextView) convertView.findViewById(R.id.tv_tips);
? ? ? ? ? viewHolder.mImageView = (ImageView)convertView.findViewById(R.id.iv_image);
? ? ? ? ?convertView.setTag(viewHolder);
? ? ? ? ?} else {
? ? ? ? ?viewHolder= (ViewHolder) convertView.getTag();
}
? ? ? ? ?String url = "";
? ? ? ? ?url = urlArrays[position % urlArrays.length];
? ? ? ? ?viewHolder.mImageView.setImageResource(R.drawable.ic_launcher);
? ? ? ? ?if (!mBusy) {
? ? ? ? mImageLoader.DisplayImage(url, viewHolder.mImageView, false);
? ? ? ? ?viewHolder.mTextView.setText("--" + position + "--IDLE||TOUCH_SCROLL");
? ? ? ? } else {
? ? ? ? mImageLoader.DisplayImage(url, viewHolder.mImageView, true);
? ? ? ? viewHolder.mTextView.setText("--" + position +"--FLING");
}
? ? ? //復(fù)用歷史緩存view
? ? ? return convertView;
}
? ? ? ?static class ViewHolder {
? ? ? ? ? TextView mTextView;
? ? ? ? ?ImageView mImageView;
? ? ? }
}
3、內(nèi)存緩沖機(jī)制:
首先限制內(nèi)存圖片緩沖的堆內(nèi)存大小,每次有圖片往緩存里加時(shí)判斷是否超過(guò)限制大小,超過(guò)的話就從中取出最少使用的圖片并將其移除。
當(dāng)然這里如果不采用這種方式,換做軟引用也是可行的,二者目的皆是最大程度的利用已存在于內(nèi)存中的圖片緩存,避免重復(fù)制造垃圾增加GC負(fù)擔(dān);OOM溢出往往皆因內(nèi)存瞬時(shí)大量增加而垃圾回收不及時(shí)造成的。只不過(guò)二者區(qū)別在于LinkedHashMap里的圖片緩存在沒(méi)有移除出去之前是不會(huì)被GC回收的,而SoftReference里的圖片緩存在沒(méi)有其他引用保存時(shí)隨時(shí)都會(huì)被GC回收。所以在使用LinkedHashMap這種LRU算法緩存更有利于圖片的有效命中,當(dāng)然二者配合使用的話效果更佳,即從LinkedHashMap里移除出的緩存放到SoftReference里,這就是內(nèi)存的二級(jí)緩存。
本例采用的是LRU算法,先看看MemoryCache的實(shí)現(xiàn)



五、ListView的其他優(yōu)化:
1、盡量避免在BaseAdapter中使用static來(lái)定義全局靜態(tài)變量:
static是Java中的一個(gè)關(guān)鍵字,當(dāng)用它來(lái)修飾成員變量時(shí),那么該變量就屬于該類,而不是該類的實(shí)例。所以用static修飾的變量,它的生命周期是很長(zhǎng)的,如果用它來(lái)引用一些資源耗費(fèi)過(guò)多的實(shí)例(比如Context的情況最多),這時(shí)就要盡量避免使用了。
2、盡量使用getApplicationContext:
如果為了滿足需求下必須使用Context的話:Context盡量使用Application Context,因?yàn)锳pplication的Context的生命周期比較長(zhǎng),引用它不會(huì)出現(xiàn)內(nèi)存泄露的問(wèn)題
3、盡量避免在ListView適配器中使用線程:
因?yàn)榫€程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控制。之前使用的自定義ListView中適配數(shù)據(jù)時(shí)使用AsyncTask自行開(kāi)啟線程的,這個(gè)比用Thread更危險(xiǎn),因?yàn)門(mén)hread只有在run函數(shù)不結(jié)束時(shí)才出現(xiàn)這種內(nèi)存泄露問(wèn)題,然而AsyncTask內(nèi)部的實(shí)現(xiàn)機(jī)制是運(yùn)用了線程執(zhí)行池(ThreadPoolExcutor),這個(gè)類產(chǎn)生的Thread對(duì)象的生命周期是不確定的,是應(yīng)用程序無(wú)法控制的,因此如果AsyncTask作為Activity的內(nèi)部類,就更容易出現(xiàn)內(nèi)存泄露的問(wèn)題。解決辦法如下:
①、將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類。
②、在線程內(nèi)部采用弱引用保存Context引用
示例代碼:
public?abstract?class?WeakAsyncTask?extends?AsyncTask?{
? ? ? ? ? ? ? ? ? ? protected?WeakReference?mTarget;
? ? ? ? ? ? ? ? ? ? public?WeakAsyncTask(WeakTarget?target)?{
? ? ? ? ? ? ? ? ? ? mTarget?=?new?WeakReference(target);
}
? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ?protected?final?void?onPreExecute()?{
? ? ? ? ? ? ? ? ? ?final?WeakTarget?target?=?mTarget.get();
? ? ? ? ? ? ? ? ? if?(target?!=?null)?{
? ? ? ? ? ? ? ? ? this.onPreExecute(target);
}
}
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ?protected?final?Result?doInBackground(Params...?params)?{
? ? ? ? ? ? ? ? ? ? final?WeakTarget?target?=?mTarget.get();
? ? ? ? ? ? ? ? ? ? if?(target?!=?null)?{
? ? ? ? ? ? ? ? ? ? return?this.doInBackground(target,?params);
? ? ? ? ? ? ? ? ? ?}?else?{
? ? ? ? ? ? ? ? ? return?null;
? ? ? ? ? ? ? ? ? }
}
? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ?protected?final?void?onPostExecute(Result?result)?{
? ? ? ? ? ? ? ? ? ?final?WeakTarget?target?=?mTarget.get();
? ? ? ? ? ? ? ? ? ?if?(target?!=?null)?{
? ? ? ? ? ? ? ? ? ?this.onPostExecute(target,?result);
}
}
? ? ? ? ? ? ? ? ?protected?void?onPreExecute(WeakTarget?target)?{
? ? ? ? ? ? ? ? ? ? ? ?//?No?default?action
}
? ? ? ? ? ? ? protected?abstract?Result?doInBackground(WeakTarget?target,?Params...?params);
? ? ? ? ? ? ? ? protected?void?onPostExecute(WeakTarget?target,?Result?result)?{
? ? ? ? ? ? ? ?/ /?No?default?action
}
}
六、ScrollView和ListView的沖突問(wèn)題:【摘自網(wǎng)絡(luò)】
解決方法之一:
在ScrollView添加一個(gè)ListView會(huì)導(dǎo)致listview控件顯示不全,這是因?yàn)閮蓚€(gè)控件的滾動(dòng)事件沖突導(dǎo)致。所以需要通過(guò)listview中的item數(shù)量去計(jì)算listview的顯示高度,從而使其完整展示,如下提供一個(gè)方法供大家參考。
示例代碼:
public voidsetListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() *(listAdapter.getCount() - 1));
params.height += 5;//if without this statement,the listview will bea little short
listView.setLayoutParams(params);
}