Android應(yīng)用界面開發(fā)
第三章學(xué)習(xí)
第三部分####
異步處理中使用AsyncTask踩過的坑####
AsyncTask是Android提供的工具之一,可以簡單方便地用于子線程更新UI,他也是個(gè)抽象類,使用時(shí)需要重寫其方法,根據(jù)定義時(shí)傳入的3個(gè)參數(shù)類型來判斷重寫哪些,但必須要重寫doInBackground()。
能夠通過getStatus()方法返回線程的工作狀態(tài),如“PENDING,RUNNING,FINISHED”分別表示“準(zhǔn)備中,運(yùn)行中,已完成”。
知識(shí)點(diǎn):
以下是Android官方文檔關(guān)于自定義AsyncTask類的范例:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
先說重寫的方法:
onPreExecute()
這個(gè)方法會(huì)在后臺(tái)任務(wù)開始執(zhí)行之前調(diào)用,用于進(jìn)行一些界面上的初始化操作,比如顯示一個(gè)進(jìn)度條對(duì)話框等。doInBackground(URL... urls)
這個(gè)方法中的所有代碼都會(huì)在子線程中運(yùn)行,我們應(yīng)該在這里去處理所有的耗時(shí)任務(wù)。任務(wù)一旦完成就可以通過 return 語句來將任務(wù)的執(zhí)行結(jié)果返回,如果 AsyncTask的第三個(gè)泛型參數(shù)指定的是 Void,就可以不返回任務(wù)執(zhí)行結(jié)果。注意,在這個(gè)方法中是不可以進(jìn)行 UI 操作的,如果需要更新 UI 元素,比如說反饋當(dāng)前任務(wù)的執(zhí)行進(jìn)度,可以調(diào)用 publishProgress(Progress...)方法來完成。onProgressUpdate(Integer... progress)
當(dāng)在后臺(tái)任務(wù)中調(diào)用了 publishProgress(Progress...)方法后,這個(gè)方法就會(huì)很快被調(diào)用,方法中攜帶的參數(shù)就是在后臺(tái)任務(wù)中傳遞過來的。在這個(gè)方法中可以對(duì) UI 進(jìn)行操作,利用參數(shù)中的數(shù)值就可以對(duì)界面元素進(jìn)行相應(yīng)地更新。onPostExecute(Long result)
當(dāng)后臺(tái)任務(wù)執(zhí)行完畢并通過 return 語句進(jìn)行返回時(shí),這個(gè)方法就很快會(huì)被調(diào)用。返回的數(shù)據(jù)會(huì)作為參數(shù)傳遞到此方法中,可以利用返回的數(shù)據(jù)來進(jìn)行一些 UI 操作,比如說提醒任務(wù)執(zhí)行的結(jié)果,以及關(guān)閉掉進(jìn)度條對(duì)話框等。
顯然,由定義時(shí)指定的3個(gè)泛型參數(shù)< URL, Integer, Long >,就是重寫的方法中對(duì)應(yīng)的3個(gè)方法所接收的類型。
順序如下:
URL-->doInBackground(URL)-->publishProgress(Integer)-->onProgressUpdate(Integer),使用Integer進(jìn)行UI更新。
以及URL-->doInBackground(URL)-->return Long-->onPostExecute(Long),進(jìn)行結(jié)果展示
要啟動(dòng)的時(shí)候,使用下方語句即可
new DownloadFilesTask().execute();
這是第一個(gè)坑
當(dāng)內(nèi)部子線程運(yùn)行完畢,則AsyncTask進(jìn)入onPostExecute()方法,然后狀態(tài)變?yōu)镕INISHED,再次調(diào)用不可
而cancel()這方法就更不可思議了,分為cancel(false)軟取消,和cancel(true)硬取消,使用第一個(gè)方法時(shí),系統(tǒng)會(huì)自動(dòng)判斷取消的時(shí)機(jī),第二個(gè)方法是立即取消,不建議時(shí)候后者。
可是!其實(shí)甭管軟硬,這AsyncTask都不一定吃……用了也沒反應(yīng)……他總是在他喜歡的時(shí)候才停止……這怎么控制啊,于是……關(guān)于AsyncTask狀態(tài)的判斷就顯得很重要了……
這里,我碰到了第二個(gè)坑
一般為了后臺(tái)實(shí)時(shí)更新進(jìn)度條,泛型只會(huì)設(shè)置為AsyncTask< Void, Integer, Void>其中Integer用來更新進(jìn)度條,在onProgressUpdate()中使用此方法:
確實(shí)順利更新了進(jìn)度條,那,我順便還想同時(shí)更新邊上的進(jìn)度文字呢?
初步設(shè)想,傳2個(gè)參數(shù)String和Integer到onProgressUpdate()中分別更新TextView和SeekBar。
能行嗎?因?yàn)橹荒軅饕粋€(gè)參數(shù)給onProgressUpdate(),使用String[]數(shù)組作為中間的參數(shù):
AsyncTask<Void, String[], Void>
然后通過把其中代表進(jìn)度的字符串轉(zhuǎn)為int,用來setProgress?
以上失敗了
用Buldle呢? 失敗
Map呢? 失敗
這什么鬼啊!!能用???
網(wǎng)上找啊找,關(guān)于AsyncTask的范例全都寫得一模一樣,只有更新seekbar等一個(gè)UI的。思考了一夜,第二天…………
只給onProgressUpdate()傳入一個(gè)參數(shù),而更新,則靠這個(gè)參數(shù)來轉(zhuǎn)變?yōu)?個(gè)UI所需的數(shù)據(jù)
而MusicService中有個(gè)將毫秒轉(zhuǎn)變?yōu)镾tring型“00:00”格式的方法
竟然成功了!原來,AsyncTask中間這泛型,其實(shí)能支持的類型有限吧!(我猜的!),給他2個(gè)參數(shù)時(shí),運(yùn)行一次就FINISHED了。所以,需要更新多個(gè)UI時(shí)候,使用AsyncTask還是得多考慮考慮。
--完--