線程的基本用法:
定義一個(gè)線程只需要新建一個(gè)類繼承自Thread,然后重新父類的run()方法,并在里面編寫耗時(shí)邏輯即可
class MyThread extends Thread{
@Override
public void run(){
//處理具體的邏輯
}
}
然后啟用這個(gè)線程,只需要調(diào)用該類的start方法即可:
new MyThread().start()
但是使用繼承的方式耦合性有點(diǎn)高,更多時(shí)候選擇使用實(shí)現(xiàn)Runable接口的方式來定義一個(gè)線程
class MyThread implemens Runnable {
@Override
public void run(){
//處理具體邏輯
}
}
使用這種寫法粘昨,啟動(dòng)線程是:
MyThread myThread = new MyThread();
new Thread(myThread).start();
Android是不允許在子線程中進(jìn)行UI操作的码耐,我們可以利用Android提供的異步消息機(jī)制,來解決在子線程中進(jìn)行UI操作的問題辐董。
public class MainActivity extends AppCompatActivity {
public static final int UPDATE_TEXT = 1;
private TextView text;
//消息接收處理的handler
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UPDATE_TEXT:
Log.d("MainActivity","test");
//在這里進(jìn)行UI操作
Log.d("MainActivity",""+msg.arg1);
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_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() {
//在子線程里發(fā)送更新文字的message
Message message = new Message();
message.what = UPDATE_TEXT;
message.arg1 = 1;
handler.sendMessage(message);
}
}).start();
}
});
}
}
解析異步消息處理機(jī)制:
Android的異步消息處理主要由4個(gè)部分組成:Message、Handler禀综、MessageQueue和Looper
- Message
Message 是在線程之間傳遞的消息简烘,可在內(nèi)部攜帶少量的信息苔严,用于不同線程之間交換數(shù)據(jù)。
有what孤澎、arg1届氢、arg2來攜帶一些整形數(shù)據(jù),使用obj字段攜帶一個(gè)Object對(duì)象 - Handler
Handler 顧名思義也就是處理者的意思覆旭,它主要是用于發(fā)送和處理消息退子。
發(fā)送消息一般使用Handler的sendMessage()方法;處理消息使用Handler的 handleMessage()方法 - MessageQueue
MessageQueue是消息隊(duì)列的意思型将,它主要是用于存放所有通過Handler發(fā)送的消息寂祥。這部分消息會(huì)一直存放在消息隊(duì)列中,等待被處理七兜。每個(gè)線程中只會(huì)有一個(gè)Message對(duì)象丸凭。 - Looper
Looper是每個(gè)線程中的MessageQueue的管家,調(diào)用Looper的loop()方法后腕铸,就會(huì)進(jìn)入到一個(gè)無線循環(huán)當(dāng)中惜犀,然后每當(dāng)發(fā)現(xiàn)MessageQueue中存在一條消息,就會(huì)將它取出狠裹,并傳遞到Handler的handleMessage()方法當(dāng)中
使用AsyncTask
由于AsyncTask是一個(gè)抽象類虽界,所以要使用它,就必須創(chuàng)建一個(gè)子類去繼承它涛菠。在繼承時(shí)莉御,可以為AsyncTask類指定3個(gè)泛型參數(shù),這3個(gè)參數(shù)的用途如下:
- Params
在執(zhí)行AsyncTask時(shí)需要傳入的參數(shù)俗冻,可用于在后臺(tái)任務(wù)中使用 - Progress
后臺(tái)任務(wù)執(zhí)行時(shí)颈将,如果需要在界面上顯示當(dāng)前進(jìn)度,則使用這里指定的泛型作為進(jìn)度單位 - Result
當(dāng)任務(wù)執(zhí)行完畢后言疗,如果需要對(duì)結(jié)果進(jìn)行返回,則使用這里指定的泛型作為返回值類型
使用AsyncTask還經(jīng)常需要重寫以下4個(gè)方法:
onPreExecute()
在后臺(tái)任務(wù)開始執(zhí)行之前調(diào)用颂砸,用于進(jìn)行一些界面上的初始化操作噪奄,比如顯示一個(gè)進(jìn)度條對(duì)話框doInBackground(Params...)
這個(gè)方法中所有的代碼都在子線程執(zhí)行,應(yīng)該在這里處理所有的耗時(shí)任務(wù)人乓。在這個(gè)方法中是不能進(jìn)行UI操作的勤篮,只能調(diào)用publishProgress(Progress...)方法將數(shù)值傳遞出去,并觸發(fā) onProgressUpdate(Progress...)方法onProgressUpdate(Progress...)
當(dāng)在后臺(tái)任務(wù)中調(diào)用了publishProgress(Progress...)方法后,onProgressUpdate(Progress...)方法就會(huì)很快被調(diào)用色罚,該方法中攜帶的參數(shù)就是在后臺(tái)任務(wù)中傳遞過來的碰缔,在這個(gè)方法中可以對(duì)UI進(jìn)行操作。-
onPostExecute(Result)
后臺(tái)任務(wù)執(zhí)行完畢并通過return語句進(jìn)行返回時(shí)戳护,這個(gè)方法就很快被調(diào)用金抡,返回的數(shù)據(jù)就會(huì)作為參數(shù)傳遞到此方法之中瀑焦,可以利用返回的數(shù)據(jù)來進(jìn)行一些UI操作。public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
ProgressDialog progressDialog = new ProgressDialog();@Override protected void onPreExecute() { progressDialog.show(); //顯示進(jìn)度對(duì)話框 } @Override protected Boolean doInBackground(Void... params) { try { while (true) { int downloadPercent = doDownload(); //此處調(diào)用publishProgress()方法梗肝,會(huì)觸發(fā) onProgressUpdate()方法 publishProgress(downloadPercent); if (downloadPercent >= 100) { break; } } }catch(Exception e){ return false; } return true; } } @Override protected void onProgressUpdate(Integer... values) { //在這里進(jìn)行UI操作榛瓮,更新進(jìn)度條 progressDialog.setMessage("Downloaded " + values[0] + "%"); } @Override protected void onPostExecute(Boolean result) { progressDialog.dismiss(); //在這里提示下載結(jié)果 if(result) { Toast.makeText(context, "Download successed", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(context, "Download faild", Toast.LENGTH_SHORT).show(); } }
}
啟用這個(gè)任務(wù),只需要
new DownloadTask().execute();
簡單來說巫击,使用AsyncTask的訣竅就是禀晓,在onPreExecute()方法中進(jìn)行初始化工作,在doInBackground()方法中執(zhí)行具體的耗時(shí)任務(wù)坝锰,在onProgressUpdate()方法中進(jìn)行UI操作粹懒,在onPostExecute()方法中執(zhí)行一些任務(wù)的收尾工作。
而調(diào)用publishProgress()方法顷级,能夠?qū)?shù)據(jù)從后臺(tái)傳遞到主線程凫乖,觸發(fā)onProgressUpdate(Progress...)方法