Android線程總結(jié)

1. 為什么使用線程

android 的多線程實(shí)際上就是java的多線程。android的UI線程又稱為主線程洋腮。

我們創(chuàng)建的Service、Activity以及Broadcast均是一個(gè)主線程處理,這里我們可以理解為UI線程赞季。但是在操作一些耗時(shí)操作時(shí)现恼,比如I/O讀寫的大文件讀寫肃续,數(shù)據(jù)庫操作以及網(wǎng)絡(luò)下載需要很長(zhǎng)時(shí)間黍檩,為了不阻塞用戶界面,出現(xiàn)ANR的響應(yīng)提示窗口始锚,這個(gè)時(shí)候我們可以考慮使用Thread線程來解決刽酱。android 的多線程實(shí)際上就是java的多線程。android的UI線程又稱為主線程瞧捌。

我們創(chuàng)建的Service棵里、Activity以及Broadcast均是一個(gè)主線程處理,這里我們可以理解為UI線程察郁。但是在操作一些耗時(shí)操作時(shí)衍慎,比如I/O讀寫的大文件讀寫,數(shù)據(jù)庫操作以及網(wǎng)絡(luò)下載需要很長(zhǎng)時(shí)間皮钠,為了不阻塞用戶界面稳捆,出現(xiàn)ANR的響應(yīng)提示窗口,這個(gè)時(shí)候我們可以考慮使用Thread線程來解決麦轰。

2. Thread 和 Runnable

在java中可有兩種方式實(shí)現(xiàn)多線程乔夯,一種是繼承Thread類,一種是實(shí)現(xiàn)Runnable接口.

Thread類是在java.lang包中定義的款侵。一個(gè)類只要繼承了Thread類同時(shí)覆寫了本類中的run()方法就可以實(shí)現(xiàn)多線程操作了末荐,但是一個(gè)類只能繼承一個(gè)父類,這是此方法的局限新锈。所以甲脏,通常會(huì)實(shí)現(xiàn)Runnable接口方式。

Thread才是一個(gè)線程妹笆,而Runnable可以理解為一個(gè)任務(wù)块请。這個(gè)任務(wù)只是一個(gè)接口。具體的任務(wù)執(zhí)行是在 run()方法執(zhí)行拳缠,那么就是把一個(gè)Runnable任務(wù)放到線程里面墩新。當(dāng)調(diào)用thread.start() 的時(shí)候,系統(tǒng)新開一個(gè)線程去執(zhí)行窟坐,這個(gè)runnable任務(wù)是在多線程執(zhí)行的海渊。是在新開的線程執(zhí)行的。

注意以下兩種情況不能實(shí)現(xiàn)在工作線程執(zhí)行哲鸳,還是在UI線程執(zhí)行臣疑。

  • thread.run() ,這樣子實(shí)際上只是在UI線程執(zhí)行了Runnable徙菠,并沒有實(shí)現(xiàn)多線程朝捆。系統(tǒng)也沒有新開一個(gè)線程。
  • new Runnable().run()懒豹,那么實(shí)際上是直接在UI線程執(zhí)行了這個(gè)任務(wù)沒有進(jìn)行一個(gè)多線程的操作芙盘。

Thread使用的例子

Boolean isRun = true;
public void start_clicked(View v){
    MyTread thread = new MyTread();
    thread.start();
}
public void stop_clicked(View v){
    isRun = false;
}

public  class MyTread extends  Thread{

    @Override
    public  void run() {
        int counter = 0;
        while (isRun){
            try {
                Log.v("thread sample", String.valueOf(counter));
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter++;
        }
    }
}

Runnable使用的例子

Boolean isRun = true;
public void start_clicked(View v){
    MyTask task = new MyTask();
    Thread thread = new Thread(task,"my thread");
    thread.start();
}
public void stop_clicked(View v){
    isRun = false;
}
public class MyTask implements Runnable {

    @Override
    public void run() {
        int counter = 0;
        while (isRun){
            try {
                Log.v("thread sample", String.valueOf(counter));
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter++;
        }
    }
}

3. Handler

一驯用、Handler的定義:

Handler主要接收子線程發(fā)送的數(shù)據(jù), 并用此數(shù)據(jù)配合主線程更新UI,用來跟UI主線程交互用儒老。比如可以用handler發(fā)送一個(gè)message蝴乔,然后在handler的線程中來接收、處理該消息驮樊,以避免直接在UI主線程中處理事務(wù)導(dǎo)致影響UI主線程的其他處理工作薇正,Android提供了Handler作為主線程和子線程的紐帶;也可以將handler對(duì)象傳給其他進(jìn)程囚衔,以便在其他進(jìn)程中通過handler給你發(fā)送事件挖腰;還可以通過handler的延時(shí)發(fā)送message,可以延時(shí)處理一些事務(wù)的處理练湿。

通常情況下猴仑,當(dāng)應(yīng)用程序啟動(dòng)時(shí),Android首先會(huì)開啟一個(gè)主線程 (也就是UI線程) , 主線程為管理界面中的UI控件肥哎,進(jìn)行事件分發(fā)辽俗。如果此時(shí)需要一個(gè)耗時(shí)的操作,例如:聯(lián)網(wǎng)讀取數(shù)據(jù)篡诽,或者讀取本地較大的一個(gè)文件的時(shí)候崖飘,你不能把這些操作放在主線程中,如果你放在主線程中的話杈女,界面會(huì)出現(xiàn)假死現(xiàn)象,如果5秒鐘還沒有完成的話朱浴,會(huì)收到Android系統(tǒng)的一個(gè)錯(cuò)誤提示"強(qiáng)制關(guān)閉".

這個(gè)時(shí)候我們需要把這些耗時(shí)的操作,放在一個(gè)子線程中,因?yàn)樽泳€程涉及到UI更新达椰,但是當(dāng)子線程中有涉及到操作UI的操作時(shí)翰蠢,就會(huì)對(duì)主線程產(chǎn)生危險(xiǎn),也就是說砰碴,更新UI只能在主線程中更新躏筏,在子線程中操作是危險(xiǎn)的. 這個(gè)時(shí)候板丽,Handler就出現(xiàn)了來解決這個(gè)復(fù)雜的問題呈枉,由于Handler運(yùn)行在主線程中(UI線程中),它與子線程可以通過Message對(duì)象來傳遞數(shù)據(jù)埃碱,這個(gè)時(shí)候猖辫,Handler就承擔(dān)著接受子線程傳過來的(子線程用sedMessage()方法傳遞)Message對(duì)象,(里面包含數(shù)據(jù)), 把這些消息放入主線程隊(duì)列中砚殿,配合主線程進(jìn)行更新UI啃憎。

二、Handler一些特點(diǎn)

handler可以分發(fā)Message對(duì)象和Runnable對(duì)象到主線程中, 每個(gè)Handler實(shí)例,都會(huì)綁定到創(chuàng)建他的線程中(一般是位于主線程), 也就是說Handler對(duì)象初始化后似炎,就默認(rèn)與對(duì)它初始化的進(jìn)程的消息隊(duì)列綁定辛萍,因此可以利用Handler所包含的消息隊(duì)列悯姊,制定一些操作的順序。

三贩毕、Handler中分發(fā)消息的一些方法

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

post類方法允許你排列一個(gè)Runnable對(duì)象到線程隊(duì)列中

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

sendMessage類方法, 允許你安排一個(gè)帶數(shù)據(jù)的Message對(duì)象到隊(duì)列中悯许,等待更新.

四、應(yīng)用實(shí)例:

1辉阶,sendMessage

一先壕、用于接受子線程發(fā)送的數(shù)據(jù), 并用此數(shù)據(jù)配合主線程更新UI

在Android中,對(duì)于UI的操作通常需要放在主線程中進(jìn)行操作谆甜。如果在子線程中有關(guān)于UI的操作垃僚,那么就需要把數(shù)據(jù)消息作為一個(gè)Message對(duì)象發(fā)送到消息隊(duì)列中,然后规辱,用Handler中的handlerMessge方法處理傳過來的數(shù)據(jù)信息谆棺,并操作UI。類sendMessage(Message msg)方法實(shí)現(xiàn)發(fā)送消息的操作按摘。 在初始化Handler對(duì)象時(shí)重寫的handleMessage方法來接收Messgae并進(jìn)行相關(guān)操作包券。

此處輸入圖片的描述
此處輸入圖片的描述
public void start_clicked(View v){

    new Thread(new Runnable() {
        @Override
        public void run() {
            int counter = 0;
            while (isRun){
                try {
                    Log.v("thread sample", String.valueOf(counter));

                    // send message to UI thread
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putInt("counter", counter);
                    msg.setData(b);
                    
                    
                    mHandler.sendMessage(msg);

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                counter++;
            }
        }
    }).start();
}
public void stop_clicked(View v){
    isRun = false;
}

Handler mHandler = new Handler(){
    // handler 在UI線程創(chuàng)建,所以可以更新UI
    public void handleMessage(android.os.Message msg) {
        super.handleMessage(msg);
        Bundle b = msg.getData();
        int c = b.getInt("counter");
        mInfo.setText(String.valueOf(c));
    }
};

有一點(diǎn)需要注意炫贤,在獲得message實(shí)例有兩種辦法

//第一種
Message msg = new Message();
msg.what = xxx;
msg.arg1  = xxx;
msg.arg2  = xxx;
handler.sendMessage(msg);

//第一種
Message msg = handler.obtainMessage();
msg.what = xxx;
msg.arg1  = xxx;
msg.arg2  = xxx;
msg.obj    = xxx;
msg.sendToTarget();

盡量使用第二種方法
原因:

  • Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
  • creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
  • If you don't want that facility, just call Message.obtain() instead.

這里我們的Message 已經(jīng)不是 自己創(chuàng)建的了,而是從MessagePool 拿的,省去了創(chuàng)建對(duì)象申請(qǐng)內(nèi)存的開銷溅固。。兰珍。侍郭。。

二掠河、傳遞Message亮元。用戶UI線程向工作線程發(fā)送消息或者線程之間通信

Handler mThreadHandler;
public void start_clicked(View v){ //啟動(dòng)線程
new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();// 創(chuàng)建該線程的Looper對(duì)象,用于接收消息,在非主線程中是沒有l(wèi)ooper的所以在創(chuàng)建handler前一定要使用prepare()創(chuàng)建一個(gè)Looper
        mThreadHandler = new Handler() { // mThreadHandler 在工作線程創(chuàng)建唠摹,不可以更新UI
            public void handleMessage(Message msg) {

                // 耗時(shí)操作
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 結(jié)束完耗時(shí)操作 通知UI線程更新UI
                Message msgToUI = mHandler.obtainMessage();
                Bundle b = new Bundle();
                b.putInt("counter", 5000);
                msgToUI.setData(b);
                mHandler.sendMessage(msgToUI);
            }
        };
        Looper.loop();//建立一個(gè)消息循環(huán)爆捞,該線程不會(huì)退出
        }
    }).start();
}
public void stop_clicked(View v){
    mThreadHandler.getLooper().quit(); //使用完成要停止looper,釋放線程
}

public void sendMessageToThread_clicked(View v) {
    // UI線程向工作線程通過 mThreadHandler向工作線程發(fā)送sendMessage
    mThreadHandler.sendEmptyMessage(0);
}

Handler mHandler = new Handler(){
    // handler 在UI線程創(chuàng)建勾拉,所以可以更新UI
    public void handleMessage(android.os.Message msg) {
        //super.handleMessage(msg);

        Bundle b = msg.getData();
        int c = b.getInt("counter");
        mInfo.setText(String.valueOf(c));
    }
};

Looper類說明

Looper 類用來為一個(gè)線程跑一個(gè)消息循環(huán)煮甥。

線程在默認(rèn)情況下是沒有消息循環(huán)與之關(guān)聯(lián)的,Thread類在run()方法中的內(nèi)容執(zhí)行完之后就退出了藕赞,即線程做完自己的工作之后就結(jié)束了成肘,沒有循環(huán)的概念。

調(diào)用Looper類的 prepare() 方法可以為當(dāng)前線程創(chuàng)建一個(gè)消息循環(huán)斧蜕,調(diào)用loop() 方法使之處理信息双霍,直到循環(huán)結(jié)束。

Looper:

public void startThread_clicked(View v){

    //簡(jiǎn)寫Runnable方式
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();// 創(chuàng)建該線程的Looper對(duì)象,用于接收消息,在非主線程中是沒有l(wèi)ooper的所以在創(chuàng)建handler前一定要使用prepare()創(chuàng)建一個(gè)Looper
            mThreadHandler = new Handler() { // mThreadHandler 在工作線程創(chuàng)建
                public void handleMessage(Message msg) {

                    // 耗時(shí)操作
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 結(jié)束完耗時(shí)操作 通知UI線程更新UI
                    Message msgToUI = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putInt("counter", 5000);
                    msgToUI.setData(b);
                    mHandler.sendMessage(msgToUI);
                }
            };
            Looper.loop();//建立一個(gè)消息循環(huán)洒闸,該線程不會(huì)退出
        }
    }).start();
}

public void sendMessageToThread_clicked(View v) {
    // UI線程向工作線程通過 mThreadHandler向工作線程發(fā)送sendMessage
    mThreadHandler.sendEmptyMessage(0);
}
public void stopThread_clicked(View v){
    mThreadHandler.getLooper().quit(); //使用完成要停到looper染坯,釋放線程
}

HandlerThread:

我們實(shí)現(xiàn)Looper有沒有更加簡(jiǎn)單的方法呢?當(dāng)然有,這就是我們的HandlerThread丘逸。我們來看下Android對(duì)HandlerThread的描述:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

上面的工作線程代碼可以簡(jiǎn)化成

HandlerThread handlerThread = new HandlerThread("xxx");
    handlerThread.start(); //創(chuàng)建HandlerThread后一定要記得start()
    mThreadHandler = new Handler(handlerThread.getLooper()){
        @Override
        public void handleMessage(Message msg) {

             //耗時(shí)操作
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 結(jié)束完耗時(shí)操作 通知UI線程更新UI
            Message msgToUI = mHandler.obtainMessage();
            Bundle b = new Bundle();
            b.putInt("counter", 5000);
            msgToUI.setData(b);
            mHandler.sendMessage(msgToUI);

            super.handleMessage(msg);
        }
    };
public void sendMessageToThread_clicked(View v) {
    // UI線程向工作線程通過 mThreadHandler向工作線程發(fā)送sendMessage
    mThreadHandler.sendEmptyMessage(0);
}
public void stopThread_clicked(View v){
    mThreadHandler.getLooper().quit(); //使用完成要停到looper酒请,釋放線程
}

2,Post

除了上述的使用Handler發(fā)送以及處理消息外鸣个,handler還有一個(gè)作用就是處理傳遞給它的action對(duì)象羞反,具體使用步驟示例:

1、在主線程中定義Handler對(duì)象

2囤萤、構(gòu)造一個(gè)runnable對(duì)象昼窗,為該對(duì)象實(shí)現(xiàn)runnable方法。

3涛舍、在子線程中使用Handler對(duì)象post(runnable)對(duì)象.

handler.post這個(gè)函數(shù)的作用是把Runnable里面的run方法里面的這段代碼發(fā)送到消息隊(duì)列中澄惊,等待運(yùn)行。
如果handler是以UI線程消息隊(duì)列為參數(shù)構(gòu)造的富雅,那么是把run里面的代碼發(fā)送到UI線程中掸驱,等待UI線程運(yùn)行這段代碼。
如果handler是以子線程線程消息隊(duì)列為參數(shù)構(gòu)造的没佑,那么是把run里面的代碼發(fā)送到子線程中毕贼,等待子線程運(yùn)行這段代碼。

//延遲5秒 向UI線程消息隊(duì)列發(fā)送一條任務(wù)(Runnable)
 public void start_clicked(View v){
        Handler h = new Handler(this.getMainLooper());
        h.postDelayed(new Runnable() {
            @Override
            public void run() {
                mInfo.setText("Come from postDelayed");
            }
        }, 5000);
}

4. synchronized

一蛤奢、當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對(duì)象object中的這個(gè)synchronized(this)同步代碼塊時(shí)鬼癣,一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊啤贩。

二待秃、然而,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)痹屹,另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊章郁。

三、尤其關(guān)鍵的是志衍,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)暖庄,其他線程對(duì)object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。

四足画、當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)雄驹,它就獲得了這個(gè)object的對(duì)象鎖佃牛。結(jié)果淹辞,其它線程對(duì)該object對(duì)象所有同步代碼部分的訪問都被暫時(shí)阻塞。

 public void start_clicked(View v){

        synchronizedTask task = new synchronizedTask();
        new Thread(task,"A").start();
        new Thread(task,"B").start();
    }

public class synchronizedTask implements Runnable {

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        }
    }
}

不加synchronized (this) 輸出為
System.out﹕ A
System.out﹕ B
System.out﹕ A
System.out﹕ B
System.out﹕ A
System.out﹕ B
System.out﹕ A
System.out﹕ B
System.out﹕ A
System.out﹕ B



synchronized (this) 輸出為
System.out﹕ A
System.out﹕ A
System.out﹕ A
System.out﹕ A
System.out﹕ A
System.out﹕ B
System.out﹕ B
System.out﹕ B
System.out﹕ B
System.out﹕ B

wait、notify象缀、notifyall蔬将、synchronized的使用機(jī)制:

synchronized(obj) {
        while(!condition) {
        obj.wait();
    }
    obj.doSomething();
}

當(dāng)線程A獲得了obj鎖后,發(fā)現(xiàn)條件condition不滿足央星,無法繼續(xù)下一處理霞怀,于是線程A就wait() , 放棄對(duì)象鎖.

之后在另一線程B中,如果B更改了某些條件莉给,使得線程A的condition條件滿足了毙石,就可以喚醒線程A:

synchronized(obj) {
    condition = true;
    obj.notify();
}

需要注意的概念是:

調(diào)用obj的wait(), notify()方法前,必須獲得obj鎖颓遏,也就是必須寫在synchronized(obj) {…} 代碼段內(nèi)徐矩。

調(diào)用obj.wait()后,線程A就釋放了obj的鎖叁幢,否則線程B無法獲得obj鎖滤灯,也就無法在synchronized(obj) {…} 代碼段內(nèi)喚醒A。

當(dāng)obj.wait()方法返回后曼玩,線程A需要再次獲得obj鎖鳞骤,才能繼續(xù)執(zhí)行。

如果A1,A2,A3都在obj.wait()黍判,則B調(diào)用obj.notify()只能喚醒A1,A2,A3中的一個(gè)(具體哪一個(gè)由JVM決定)豫尽。

obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續(xù)執(zhí)行obj.wait()的下一條語句顷帖,必須獲得obj鎖拂募,因此,A1,A2,A3只有一個(gè)有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行窟她,例如A1陈症,其余的需要等待A1釋放obj鎖之后才能繼續(xù)執(zhí)行。

當(dāng)B調(diào)用obj.notify/notifyAll的時(shí)候震糖,B正持有obj鎖录肯,因此,A1,A2,A3雖被喚醒吊说,但是仍無法獲得obj鎖论咏。直到B退出synchronized塊,釋放obj鎖后颁井,A1,A2,A3中的一個(gè)才有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行

5. AsyncTask

在Android中實(shí)現(xiàn)異步任務(wù)機(jī)制有兩種方式厅贪,Handler和AsyncTask。

Handler模式需要為每一個(gè)任務(wù)創(chuàng)建一個(gè)新的線程雅宾,任務(wù)完成后通過Handler實(shí)例向UI線程發(fā)送消息养涮,完成界面的更新,這種方式對(duì)于整個(gè)過程的控制比較精細(xì),但也是有缺點(diǎn)的贯吓,例如代碼相對(duì)臃腫懈凹,在多個(gè)任務(wù)同時(shí)執(zhí)行時(shí),不易對(duì)線程進(jìn)行精確的控制悄谐。關(guān)于Handler的相關(guān)知識(shí)介评,前面也有所介紹。

為了簡(jiǎn)化操作爬舰,Android1.5提供了工具類android.os.AsyncTask们陆,它使創(chuàng)建異步任務(wù)變得更加簡(jiǎn)單,不再需要編寫任務(wù)線程和Handler實(shí)例即可完成相同的任務(wù)情屹。

先來看看AsyncTask的定義:

public abstract class AsyncTask<Params, Progress, Result> {  

三種泛型類型分別代表“啟動(dòng)任務(wù)執(zhí)行的輸入?yún)?shù)”棒掠、“后臺(tái)任務(wù)執(zhí)行的進(jìn)度”、“后臺(tái)計(jì)算結(jié)果的類型”屁商。在特定場(chǎng)合下烟很,并不是所有類型都被使用,如果沒有被使用蜡镶,可以用java.lang.Void類型代替雾袱。

一個(gè)異步任務(wù)的執(zhí)行一般包括以下幾個(gè)步驟:

1.execute(Params... params),執(zhí)行一個(gè)異步任務(wù)官还,需要我們?cè)诖a中調(diào)用此方法芹橡,觸發(fā)異步任務(wù)的執(zhí)行。

2.onPreExecute()望伦,在execute(Params... params)被調(diào)用后立即執(zhí)行林说,一般用來在執(zhí)行后臺(tái)任務(wù)前對(duì)UI做一些標(biāo)記。

3.doInBackground(Params... params)屯伞,在onPreExecute()完成后立即執(zhí)行腿箩,用于執(zhí)行較為費(fèi)時(shí)的操作,此方法將接收輸入?yún)?shù)和返回計(jì)算結(jié)果劣摇。在執(zhí)行過程中可以調(diào)用publishProgress(Progress... values)來更新進(jìn)度信息珠移。

4.onProgressUpdate(Progress... values),在調(diào)用publishProgress(Progress... values)時(shí)末融,此方法被執(zhí)行钧惧,直接將進(jìn)度信息更新到UI組件上。

5.onPostExecute(Result result)勾习,當(dāng)后臺(tái)操作結(jié)束時(shí)浓瞪,此方法將會(huì)被調(diào)用,計(jì)算結(jié)果將做為參數(shù)傳遞到此方法中巧婶,直接將結(jié)果顯示到UI組件上乾颁。

在使用的時(shí)候涂乌,有幾點(diǎn)需要格外注意:

1.異步任務(wù)的實(shí)例必須在UI線程中創(chuàng)建。

2.execute(Params... params)方法必須在UI線程中調(diào)用钮孵。

3.不要手動(dòng)調(diào)用onPreExecute(),doInBackground(Params... params)眼滤,onProgressUpdate(Progress... values)巴席,onPostExecute(Result result)這幾個(gè)方法。

4.不能在doInBackground(Params... params)中更改UI組件的信息诅需。

5.一個(gè)任務(wù)實(shí)例只能執(zhí)行一次漾唉,如果執(zhí)行第二次將會(huì)拋出異常。

private TextView mAsyncTaskInfo;
private ProgressBar mProgress;
private MyAsyncTask mTask;
    
public void asyncTask_clicked(View v){
    mTask = new MyAsyncTask();
    mTask.execute("demo");
}

public void cancel_asyncTask_clicked(View v){
    mTask.cancel(true);
}

public class MyAsyncTask extends AsyncTask<String, Integer, String>{

    //在execute(Params... params)被調(diào)用后立即執(zhí)行
    //onPreExecute方法用于在執(zhí)行后臺(tái)任務(wù)前做一些UI操作
    @Override
    protected void onPreExecute() {
        mAsyncTaskInfo.setText("loading...");
    }

    //onPreExecute()完成后立即執(zhí)行
    //doInBackground方法內(nèi)部執(zhí)行后臺(tái)任務(wù),不可在此方法內(nèi)修改UI
    @Override
    protected String doInBackground(String... params) {

        try {
            if(params[0].equals("demo")){
                Thread.sleep(2000);

                while (counter < 100){
                    publishProgress(counter);

                    Thread.sleep(50);
                    counter++;
                }
                return  "finished : " + String.valueOf(counter);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    //在調(diào)用publishProgress(Progress... values)時(shí)堰塌,此方法被執(zhí)行
    //onProgressUpdate方法用于更新進(jìn)度信息
    @Override
    protected void onProgressUpdate(Integer... progresses) {
        mProgress.setProgress(progresses[0]);
        mAsyncTaskInfo.setText("loading..." + progresses[0] + "%");
    }

    //onPostExecute方法用于在執(zhí)行完后臺(tái)任務(wù)后更新UI,顯示結(jié)果
    @Override
    protected void onPostExecute(String result) {
        mAsyncTaskInfo.setText(result);
    }

    //onCancelled方法用于在取消執(zhí)行中的任務(wù)時(shí)更改UI
    @Override
    protected void onCancelled() {
        mAsyncTaskInfo.setText("cancelled");
        mProgress.setProgress(0);
    }
}

完赵刑!

樣例代碼 git 地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市场刑,隨后出現(xiàn)的幾起案子般此,更是在濱河造成了極大的恐慌,老刑警劉巖牵现,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铐懊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瞎疼,警方通過查閱死者的電腦和手機(jī)科乎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贼急,“玉大人茅茂,你說我怎么就攤上這事√ィ” “怎么了空闲?”我有些...
    開封第一講書人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)走敌。 經(jīng)常有香客問我进副,道長(zhǎng)悔常,這世上最難降的妖魔是什么影斑? 我笑而不...
    開封第一講書人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮机打,結(jié)果婚禮上矫户,老公的妹妹穿的比我還像新娘。我一直安慰自己残邀,他們只是感情好皆辽,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開白布柑蛇。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪媒咳。 梳的紋絲不亂的頭發(fā)上碳竟,一...
    開封第一講書人閱讀 52,821評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音盆耽,去河邊找鬼。 笑死扼菠,一個(gè)胖子當(dāng)著我的面吹牛摄杂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播循榆,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼析恢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了秧饮?” 一聲冷哼從身側(cè)響起映挂,我...
    開封第一講書人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盗尸,沒想到半個(gè)月后袖肥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡振劳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年椎组,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片历恐。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寸癌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弱贼,到底是詐尸還是另有隱情蒸苇,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布吮旅,位于F島的核電站溪烤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庇勃。R本人自食惡果不足惜檬嘀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望责嚷。 院中可真熱鬧鸳兽,春花似錦、人聲如沸罕拂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衷掷,卻和暖如春辱姨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戚嗅。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來泰國打工雨涛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渡处。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓镜悉,卻偏偏與公主長(zhǎng)得像祟辟,于是被迫代替她去往敵國和親医瘫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

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