Android第一行代碼(十二)多線程編程

線程的基本用法:

定義一個(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

  1. Message
    Message 是在線程之間傳遞的消息简烘,可在內(nèi)部攜帶少量的信息苔严,用于不同線程之間交換數(shù)據(jù)。
    有what孤澎、arg1届氢、arg2來攜帶一些整形數(shù)據(jù),使用obj字段攜帶一個(gè)Object對(duì)象
  2. Handler
    Handler 顧名思義也就是處理者的意思覆旭,它主要是用于發(fā)送和處理消息退子。
    發(fā)送消息一般使用Handler的sendMessage()方法;處理消息使用Handler的 handleMessage()方法
  3. MessageQueue
    MessageQueue是消息隊(duì)列的意思型将,它主要是用于存放所有通過Handler發(fā)送的消息寂祥。這部分消息會(huì)一直存放在消息隊(duì)列中,等待被處理七兜。每個(gè)線程中只會(huì)有一個(gè)Message對(duì)象丸凭。
  4. 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)中
異步消息處理流程.png

使用AsyncTask

由于AsyncTask是一個(gè)抽象類虽界,所以要使用它,就必須創(chuàng)建一個(gè)子類去繼承它涛菠。在繼承時(shí)莉御,可以為AsyncTask類指定3個(gè)泛型參數(shù),這3個(gè)參數(shù)的用途如下:

  1. Params
    在執(zhí)行AsyncTask時(shí)需要傳入的參數(shù)俗冻,可用于在后臺(tái)任務(wù)中使用
  2. Progress
    后臺(tái)任務(wù)執(zhí)行時(shí)颈将,如果需要在界面上顯示當(dāng)前進(jìn)度,則使用這里指定的泛型作為進(jìn)度單位
  3. Result
    當(dāng)任務(wù)執(zhí)行完畢后言疗,如果需要對(duì)結(jié)果進(jìn)行返回,則使用這里指定的泛型作為返回值類型

使用AsyncTask還經(jīng)常需要重寫以下4個(gè)方法:

  1. onPreExecute()
    在后臺(tái)任務(wù)開始執(zhí)行之前調(diào)用颂砸,用于進(jìn)行一些界面上的初始化操作噪奄,比如顯示一個(gè)進(jìn)度條對(duì)話框

  2. doInBackground(Params...)
    這個(gè)方法中所有的代碼都在子線程執(zhí)行,應(yīng)該在這里處理所有的耗時(shí)任務(wù)人乓。在這個(gè)方法中是不能進(jìn)行UI操作的勤篮,只能調(diào)用publishProgress(Progress...)方法將數(shù)值傳遞出去,并觸發(fā) onProgressUpdate(Progress...)方法

  3. 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)行操作。

  4. 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...)方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市愕把,隨后出現(xiàn)的幾起案子拣凹,更是在濱河造成了極大的恐慌,老刑警劉巖恨豁,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚣镜,死亡現(xiàn)場離奇詭異,居然都是意外死亡橘蜜,警方通過查閱死者的電腦和手機(jī)菊匿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來计福,“玉大人跌捆,你說我怎么就攤上這事∠笥保” “怎么了佩厚?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長说订。 經(jīng)常有香客問我抄瓦,道長,這世上最難降的妖魔是什么陶冷? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任钙姊,我火速辦了婚禮,結(jié)果婚禮上埂伦,老公的妹妹穿的比我還像新娘煞额。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布膊毁。 她就那樣靜靜地躺著胀莹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪媚媒。 梳的紋絲不亂的頭發(fā)上嗜逻,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音缭召,去河邊找鬼栈顷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嵌巷,可吹牛的內(nèi)容都是我干的萄凤。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼搪哪,長吁一口氣:“原來是場噩夢啊……” “哼靡努!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晓折,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤惑朦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后漓概,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漾月,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年胃珍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了梁肿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡觅彰,死狀恐怖吩蔑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情填抬,我是刑警寧澤烛芬,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站飒责,受9級(jí)特大地震影響赘娄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜读拆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸵闪。 院中可真熱鬧檐晕,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芥喇,卻和暖如春西采,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背继控。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來泰國打工械馆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人武通。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓霹崎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親冶忱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尾菇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容