Android17-多線程

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();
            }
        });

    }
}

如上代碼所示压怠,

  1. 首先定義了一個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功能了雇盖。
2017-04-17_09-11-04.gif

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ù)缝龄。

  1. Params:在執(zhí)行AsyncTask時需要傳入的參數(shù),可用于在后臺任務(wù)中使用挂谍。
  2. Progress:在后臺任務(wù)執(zhí)行時叔壤,如果需要在界面上顯示當(dāng)前的進度,則使用這里指定的泛型作為進度單位口叙。
  3. Result:當(dāng)任務(wù)執(zhí)行完畢后炼绘,如果需要對結(jié)果進行返回,則使用這里指定的泛型作為返回值類型妄田。
//自定義一個簡單的AsyncTask
class DownloadTask extends AsyncTask<void, Integer, Boolean> {
  ···
}

2.重寫父類方法

創(chuàng)建完自定義的AsyncTask之后俺亮,還需重寫父類方法才能完成對任務(wù)的定制,經(jīng)常重寫的有以下幾個方法

  1. 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();
    }
}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碗降,隨后出現(xiàn)的幾起案子隘竭,更是在濱河造成了極大的恐慌,老刑警劉巖讼渊,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件动看,死亡現(xiàn)場離奇詭異,居然都是意外死亡爪幻,警方通過查閱死者的電腦和手機菱皆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挨稿,“玉大人仇轻,你說我怎么就攤上這事∧谈剩” “怎么了篷店?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長臭家。 經(jīng)常有香客問我疲陕,道長,這世上最難降的妖魔是什么钉赁? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任蹄殃,我火速辦了婚禮,結(jié)果婚禮上你踩,老公的妹妹穿的比我還像新娘诅岩。我一直安慰自己讳苦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布按厘。 她就那樣靜靜地躺著医吊,像睡著了一般钱慢。 火紅的嫁衣襯著肌膚如雪逮京。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天束莫,我揣著相機與錄音懒棉,去河邊找鬼。 笑死览绿,一個胖子當(dāng)著我的面吹牛策严,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饿敲,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妻导,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怀各?” 一聲冷哼從身側(cè)響起倔韭,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓢对,沒想到半個月后寿酌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡硕蛹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年醇疼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片法焰。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秧荆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出埃仪,到底是詐尸還是另有隱情乙濒,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布贵试,位于F島的核電站琉兜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏毙玻。R本人自食惡果不足惜豌蟋,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桑滩。 院中可真熱鬧梧疲,春花似錦允睹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至该互,卻和暖如春米者,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宇智。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工蔓搞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人随橘。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓喂分,卻偏偏與公主長得像,于是被迫代替她去往敵國和親机蔗。 傳聞我的和親對象是個殘疾皇子蒲祈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

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