1.創(chuàng)建子線程
- 通過繼承Thread類來創(chuàng)建子線程
- 新建一個類繼承自Thread京郑,然后重寫父類的run()方法望浩,并在里邊編寫耗時邏輯即可
class MyThread extends Thread { @Override public void run() { //處理具體邏輯 } }
- 啟動子線程的方法:獲得子線程的實例侥祭,并調(diào)用start()方法
new MyThread().start();
注:通過繼承的方法來創(chuàng)建子線程的耦合性相對來說比較高,所以一般會采用實現(xiàn)Runnable接口的方法來定義一個線程
- 通過實現(xiàn)Runnable接口的方法定義一個線程
- 實現(xiàn)Runnable的接口的第一種方式
//定義一個類去實現(xiàn)Runnable接口 class MyThread implements Runnable { @Override public void run() { //處理具體邏輯 } } //外部啟動線程 MyThread myThread = new MyThread(); new Thread(myThread).start();
- 實現(xiàn)Runnable接口的第二種方式
//使用匿名類的方式實現(xiàn)接口尊勿,這種方法更常見 new Thread(new Runnable() { @Override public void run() { //處理具體邏輯 } }).start();
2. 在子線程更新UI
- 具體代碼如下
public class MainActivity extends AppCompatActivity {
private TextView text;
public static final int UPDATE_TEXT = 1;
//1. 創(chuàng)建一個Handler對象靶剑,并重寫父類的handleMessage()方法。
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
//在這個方法里邊處理接收到線程消息的事件
switch (msg.what) {
case UPDATE_TEXT:
//進行UI操作
if (text.getText().equals("我是要更改的文本")) {
text.setText("我是更改后的文本");
} else {
text.setText("我是要更改的文本");
}
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_main);
text = (TextView) findViewById(R.id.text);
Button changeText = (Button) findViewById(R.id.change_text);
changeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//2. 創(chuàng)建一個Message對象
Message message = new Message();
//設(shè)置message的what字段值
message.what = UPDATE_TEXT;
//將message發(fā)送出去
handler.sendMessage(message);
}
}).start();
}
});
}
}
如上代碼所示压怠,
- 首先定義了一個UPDATE_TEXT整形常量眠冈,用于標(biāo)記更新TextView這個動作
- 然后新增一個Handler對象,并重寫父類的handleMessage()方法
- 在子線程中菌瘫,創(chuàng)建一個Message(android.os.Message)對象蜗顽,并將對象what值指定為UPDATE_TEXT
- 然后調(diào)用Handler的sendMessage()方法將這條Message發(fā)送出去。
- 發(fā)送完之后就會調(diào)用步驟2重寫的handleMessage()方法了雨让,這時候就可以實現(xiàn)子線程的更新UI功能了雇盖。
3.解析異步處理機制
Android的異步消息處理機制主要由4個部分組成:Message、Handler栖忠、MessageQueue崔挖、Looper
1.Message
Message是線程之間傳遞的消息,它可以在內(nèi)部攜帶少量的信息庵寞、用于在不同線程之間交換數(shù)據(jù)狸相,除了前面用到的what字段,還有arg1捐川、arg2字段攜帶整型數(shù)據(jù)脓鹃,obj字段攜帶一個Object對象
2.Handler
Handler顧名思義就是處理者的意思,主要用于發(fā)送和處理消息古沥,發(fā)送消息一般使用Handler的sendMessage()方法瘸右,而發(fā)出的消息經(jīng)過一系列的輾轉(zhuǎn)處理后,最終會傳遞到Handler的handlerMessage()方法中
3.MessageQueue
MessageQueue是消息隊列的意思渐白,主要用于存放所有通過Handler發(fā)送的消息尊浓,這部分消息會一直存在于消息隊列中,等待被處理纯衍,每個線程中只有一個MessageQueue對象
4.Looper
Looper是每個線程中的MessageQueue的管家,調(diào)用Looper的loop()方法后苗胀,就會進入到一個無限循環(huán)中襟诸,然后每當(dāng)發(fā)現(xiàn)MessageQueue中存在一條消息瓦堵,就會將它取出,并傳到Handler的handleMessage()方法中歌亲,每個線程只有一個Looper對象菇用。
以2中的在子線程更新UI為例:首先創(chuàng)建一個Handler對象,并重寫handleMessage方法(在這個方法里邊做更新UI的操作)陷揪。然后當(dāng)子線程需要更新UI時惋鸥,就創(chuàng)建Message對象,并通過Handler將這條消息發(fā)送出去悍缠。之后這條消息會被添加到MessageQueue的隊列中等待被處理卦绣,而Looper則會一直嘗試從MessageQueue中取出待處理消息,最后分發(fā)回Handler的handleMessage()方法飞蚓,由于Handler是在主線程中創(chuàng)建的滤港,所以就可以在handleMessage()f昂法里邊進行更新UI的操作了。
4. 使用AsyncTask
為了更加方便的在子線程中進行更新UI的操作趴拧,Android提供了AsyncTask工具溅漾,他是一個抽象類,實現(xiàn)原理也是基于異步消息處理機制著榴,只是Android已經(jīng)做了很好的封裝而已添履。
1.創(chuàng)建AsyncTask
由于AsyncTask是一個抽象類,所以使用的時候需要創(chuàng)建一個子類去繼承它脑又。繼承是我們可以為AsyncTask類指定三個泛型參數(shù)缝龄。
- Params:在執(zhí)行AsyncTask時需要傳入的參數(shù),可用于在后臺任務(wù)中使用挂谍。
- Progress:在后臺任務(wù)執(zhí)行時叔壤,如果需要在界面上顯示當(dāng)前的進度,則使用這里指定的泛型作為進度單位口叙。
- Result:當(dāng)任務(wù)執(zhí)行完畢后炼绘,如果需要對結(jié)果進行返回,則使用這里指定的泛型作為返回值類型妄田。
//自定義一個簡單的AsyncTask class DownloadTask extends AsyncTask<void, Integer, Boolean> { ··· }
2.重寫父類方法
創(chuàng)建完自定義的AsyncTask之后俺亮,還需重寫父類方法才能完成對任務(wù)的定制,經(jīng)常重寫的有以下幾個方法
- onPreExecute() :
這個方法在后臺任務(wù)開始執(zhí)行之前調(diào)用疟呐,用于進行界面上的初始化操作脚曾,比如顯示一個進度條對話框等
2.doInBackground() :
這個方法中的所有代碼都會在子線程中運行,我們應(yīng)該在這里處理所有的耗時任務(wù)启具。任務(wù)執(zhí)行完成可以通過return語句來將任務(wù)執(zhí)行結(jié)果返回本讥,如果AsyncTask的第三個泛型參數(shù)指定的是Void,就可以不返回任務(wù)執(zhí)行結(jié)果。注意拷沸,在這個方法中是不可以進行UI操作的色查,如果需要更新UI元素,比如說反饋當(dāng)前任務(wù)執(zhí)行進度撞芍,可以調(diào)用publishProgress(Progress...)方法來完成
3.onProgressUpdate(Progress...)
當(dāng)在后臺調(diào)用了publishProgress(Progress...)方法后秧了,onProgressUpdate(Progress...)方法就會很快被調(diào)用,該方法中攜帶的參數(shù)就好似后臺任務(wù)傳遞過來的序无。在這個方法中可以對UI進行操作验毡,利用參數(shù)中的數(shù)值對界面元素進行相應(yīng)的更新
4.onPostExectue(Result)
當(dāng)后臺任務(wù)執(zhí)行完畢并通過retur語句進行返回時,這個方法就很快會被調(diào)用帝嗡,返回的數(shù)據(jù)會作為參數(shù)傳遞到此方法中晶通,可以利用返回的數(shù)據(jù)來進行一些UI操作,比如說提醒任務(wù)執(zhí)行的結(jié)果丈探,以及關(guān)閉掉進度條對話框等录择。
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
private ProgressDialog progress;
@Override
protected void onPreExecute() {
//顯示進度對話框
progress.show();
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload();//這是一個虛構(gòu)的方法
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
//在這里更新下載進度
progress.setMessage("Downloaded " + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
//在這里提示下載結(jié)果
if(result) {
Toast.makeText(context, "Download succesed", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Download failed", Toast.LENGTH_SHORT).show();
}
}
}