一提多線程惑畴,腦子里立馬浮現(xiàn)出各種關(guān)于線程池辐棒、線程同步行剂、線程狀態(tài)的詞匯秕噪,恨不能把線程控制的像控件一樣手拿把攥,儼然已將Android系統(tǒng)提供的線程機(jī)制拋諸腦后厚宰,今天小編就來八一八這些Android獨(dú)有的多線程腌巾。
一、AsyncTask
Sync是同步,Async就是異步了壤躲。先看下異步任務(wù)的使用方法城菊,再分析其源碼實(shí)現(xiàn)备燃。
1.使用方法
AsyncTask<Params, Progress, Result>
第一個(gè)為入?yún)⒌锟耍诙€(gè)為進(jìn)度類型,第三個(gè)為結(jié)果類型并齐。
四個(gè)回調(diào)方法漏麦,
onPreExecute(),主線程中執(zhí)行準(zhǔn)備工作况褪。
doInBackground(Params... params)撕贞,線程池執(zhí)行異步任務(wù),可以調(diào)用publishProgress更新任務(wù)進(jìn)度测垛,它會(huì)調(diào)用onProgressUpdate方法捏膨。返回Result給onPostExecute()。
onProgressUpdate(Progress... values)食侮,主線程執(zhí)行号涯,更新進(jìn)度。
onPostExecute(Result result)锯七,主線程執(zhí)行链快,執(zhí)行結(jié)果的返回值。
onCancelled()眉尸,取消執(zhí)行域蜗。
2.源碼解析
- execute()-->executeOnExecutor()-->在3.0之后,第一個(gè)為串行噪猾,第二個(gè)為并行
- onPreExecute()-->serialExecutor.execute(mFuture)用于任務(wù)的排隊(duì)-->有一個(gè)ArrayDeque<Runnable>任務(wù)列表霉祸,新來的Runnable會(huì)被插入到隊(duì)列尾部-->
- 然后調(diào)用scheduleNext() –>如果沒有正在活動(dòng)的任務(wù),就會(huì)取出一個(gè)任務(wù)執(zhí)行THREAD_POOL_EXECUTOR.execute(mActive)袱蜡,用于執(zhí)行任務(wù)-->
- 執(zhí)行WorkerRunnable的call()-->postResult(doInBackground(params))-->
- message.sendToTarget()-->在Handler中handleMessage丝蹭,如果是PROGRESS,則回調(diào)onProgressUpdate戒劫;如果是RESULT半夷,則finish()-->onCancelled/onPostExecute(result)。
二迅细、HandlerThread
普通Thread用于執(zhí)行耗時(shí)任務(wù)巫橄。
HandlerThread是使用Handler的Thread,在run()中通過Looper.prepare(); Looper.loop(); 創(chuàng)建消息隊(duì)列并開啟循環(huán)茵典。使用HandlerThread.getLooper()創(chuàng)建Handler湘换,通過Handler發(fā)送消息/Runnable,Runnable被加入到隊(duì)列中,然后Looper取出Runnable執(zhí)行彩倚。這里主要是通過Handler切換到了子線程铐望,也就是HandlerThread的線程,所以可以執(zhí)行耗時(shí)任務(wù)愉耙。在這個(gè)Looper里面不是rr執(zhí)行僧叉,而是順序串行執(zhí)行。
HandlerThread handlerThread = new HandlerThread();
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.sendMessage(new Runnable());
handlerThread.quit();
三哥谷、IntentService
IntentService是能執(zhí)行耗時(shí)任務(wù)的Service岸夯,它具有Service的高優(yōu)先級,可以執(zhí)行一些高優(yōu)先級的后臺任務(wù)们妥。
IntentService就是如上面猜扮,在onCreate中生成了HandlerThread和Handler。
在onStartCommand中监婶,調(diào)用了onStart旅赢,Handler發(fā)送消息到消息隊(duì)列,Handler在handlerMessage中回調(diào)onHandleIntent(Intent)惑惶,在onHandleIntent已經(jīng)是子線程煮盼,可以執(zhí)行耗時(shí)任務(wù),然后stopSelf()集惋。通常的Handler在處理消息時(shí)是在ui線程執(zhí)行孕似,這里因?yàn)橛玫氖荋andlerThread的Looper生成的Handler,所以切換到了HandlerThread這個(gè)子線程中去執(zhí)行耗時(shí)任務(wù)刮刑。
每執(zhí)行一個(gè)任務(wù)就啟動(dòng)一次onStartCommand喉祭。
public class MyIntentService extends IntentService
{
private final static String TAG = "main";
private String url_path="http://ww2.sinaimg.cn/bmiddle/9dc6852bjw1e8gk397jt9j20c8085dg6.jpg";
public MyIntentService()
{
super("IntentSer");
}
@Override
public void onCreate()
{
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy()
{
super.onDestroy();
}
@Override
protected void onHandleIntent(Intent intent)
{
try {
// 在設(shè)備應(yīng)用目錄下創(chuàng)建一個(gè)文件
File file=new File(this.getFilesDir(), "weibo.jpg");
FileOutputStream fos=new FileOutputStream(file);
// 獲取網(wǎng)絡(luò)圖片的輸入流
InputStream inputStream = new URL(url_path).openStream();
// 把網(wǎng)絡(luò)圖片輸入流寫入文件的輸出流中
byte[] date=new byte[1024];
int len=-1;
while((len=inputStream.read(date))!=-1)
{
fos.write(date, 0, len);
}
fos.close();
inputStream.close();
Log.i(TAG, "The file download is complete");
} catch (MalformedURLException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
四、FAQ
最后附兩個(gè)常見的小問題出現(xiàn)的原因雷绢。
1.在非UI線程中更新UI原因
ViewRootImpl類中的checkThread()方法用于檢測當(dāng)前是否UI線程泛烙,如果不是就會(huì)引發(fā)CalledFromWrongThreadException。而ViewRootImpl是在onResume結(jié)束后才創(chuàng)建完成翘紊。如果在onResume執(zhí)行完成前蔽氨,比如onStart、onCreate中子線程執(zhí)行完畢帆疟,并修改了UI鹉究,此時(shí)View沒有繪制出來,相當(dāng)于設(shè)置了UI的一些屬性踪宠,最終也不會(huì)調(diào)用checkThread方法自赔,便不會(huì)報(bào)錯(cuò)。如果在onResume之后柳琢,比如延時(shí)幾秒鐘绍妨,ViewRootImpl已經(jīng)創(chuàng)建完成润脸,就會(huì)引發(fā)checkThread方法,從而報(bào)錯(cuò)他去。
2.在子線程正確更新UI
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable,long)