Android多線程和異步任務(wù)詳解

Android開發(fā)中使用多線程的原因

  • 避免ANR(Application is not responding)
  • 實現(xiàn)異步,比如從云端獲取圖片比較費時,不應(yīng)該使用同步阻塞獲取結(jié)果,使用異步加載完成一個刷新一個
  • 多任務(wù),比如多線程下載

Android事件處理機制

在Android中事件處理的原則是:耗時的操作放到其他線程中處理,Main線程中處理不太耗時的操作,否則事件在5秒內(nèi)無法得到響應(yīng)就會出現(xiàn)ANR現(xiàn)象,在Main線程中一般是處理UI,處理Activity事件(OnCreat()等)和事件處理方法(OnClick()等)。

同步和異步的理解

有些事件必須使用同步比如用戶的注冊,需要得到結(jié)果后才能進行下面的操作,有些事件需要異步,比如微博的點贊,只需要點贊完成后提示我一下就行了,沒必要在與服務(wù)器溝通的過程中采用同步處理不可以干其他的事情。

Java中多線程的創(chuàng)建

通過繼承Thread類

Thread類的本質(zhì)是一個實現(xiàn)了Runnable接口的類,繼承Thread類,復(fù)寫run()方法,最后在通過.start()調(diào)用。

通過接口Runnable

Runnable的好處是接口比繼承更自由,新建對象后實例一個Thread對象將Runnable傳入并調(diào)用.start(),如果多個Thread對象傳入了同一個Runnable它們將共享數(shù)據(jù),比如這個賣火車票的例子。

package info.wujiajun.threadlearning;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

public void btnOnClickByThread(View view){
    ThreadText text1 = new ThreadText();
    ThreadText text2 = new ThreadText();
    text1.start();
    text2.start();
}
public void btnOnClickByRunnable(View view){
    RunnableText text1 = new RunnableText();
    RunnableText text2 = new RunnableText();
    Thread t1 = new Thread(text1);
    Thread t2 = new Thread(text2);
    t1.start();
    t2.start();
}
public void saleTicket(View view){
    SaleTicket text = new SaleTicket();
    Thread t1 = new Thread(text);
    Thread t2 = new Thread(text);
    t1.start();
    t2.start();
}

SaleTicket.java

package info.wujiajun.threadlearning;

import android.util.Log;

/**
 * Email:amojury@outlook.com
 * Github:https://github.com/amojury
 * Created by Jun on 2017/7/27.
 */

public class SaleTicket implements Runnable {
    private int ticket = 20;
    @Override
    public void run() {
        while (true){
            if(ticket > 0){
                Log.d("TAG",Thread.currentThread().getName()+"賣出了第"+(20-ticket+1)+"張票");
                ticket--;
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            else
                break;
        }

    }

}

線程池的應(yīng)用

new Thread的弊端

  • 性能差
  • 缺乏統(tǒng)一管理,無序競爭
  • 無法定時定期執(zhí)行

線程池

Java通過Executors提供了四種線程池

  • newCachedThreadPool 可緩存線程池,回收閑置線程
  • newFixedThreadPool 定長線程池,有最大并發(fā)數(shù)
  • newScheduledThreadPool 可定時定長線程池
  • newSingleThreadExecutor 制定順序單線程池

實現(xiàn)多線程之間的通訊

Android線程通訊原理

原理圖
原理圖

Handle(send方法)

實現(xiàn)兩秒后改變button上的文字


public class MainActivity extends AppCompatActivity {
@BindView(R.id.btnHelloWorld)
Button btnHelloWorld;

private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {  //回調(diào)方法,復(fù)寫handleMessage,處理要放在主線程的事情
        if(msg.what == 1)
            btnHelloWorld.setText("Not HelloWorld");
    }
};



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
}


@OnClick(R.id.btnHelloWorld)
public void onViewClicked() {
    new Thread(new Runnable() {   //開啟新線程
        @Override
        public void run() {
            try {
                Thread.sleep(2000);  //模擬網(wǎng)絡(luò)請求
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mHandler.sendEmptyMessage(1);  //使用send方法發(fā)送一個空消息,標(biāo)識是1

        }
    }).start();
}
}

send的一些方法

  • sendEmptyMessage(int)
  • sendMessage(Message)
  • sendMessageAtTime(Message,long)
  • sendMessageDelayer(Message,long)

Handle(post方法)

post方法允許你排列一個Runnable對象在主線程中

  • post(Runnable)
  • postAtTime(Runnable,long)
  • postDelayer(Runnabld)

三種更新UI的方法

  • post
  • View.post()
  • RunOnUiThread()

AsyncTask類(可以在子線程和UI線程中跳轉(zhuǎn))

解決使用Handle較為繁瑣
AsyncTask是一個抽象類,我們需要一個子類繼承它并提供三個泛型參數(shù)。

  1. Params 參數(shù)
  2. Progress 進度
  3. Result 返回結(jié)果

class DownloadTask extend AsyncTask< Void,Integer,Boolean>
Void表示不需要傳參數(shù)給后臺,Integer表示用整形的數(shù)來表示過程,Boolean表示用布爾值表示結(jié)果。

重寫方法

  1. OnPreExecute() 在后臺任務(wù)開始之前調(diào)用,用于初始化UI界面等

  2. doInBackground(Params) 所有代碼都在子線程用于耗時操作,完成后返回結(jié)果,如果第三個參數(shù)是Void,不用返回,如果需要反饋任務(wù)進度調(diào)用publishProgress()方法

  3. OnProgressUpdate(Progress),當(dāng)執(zhí)行publishProgress()方法后被調(diào)用用于界面更新(不一定會被調(diào)用)

  4. OnPostEcecute(Result) 后臺任務(wù)執(zhí)行完畢返回參數(shù)。
    使用AsyncTask實現(xiàn)下載器



    public class MainActivity extends AppCompatActivity {
    @BindView(R.id.btn)
    Button btn;
    @BindView(R.id.pb)
    ProgressBar pb;
    private int progress = 0;

    private Handler mHandler = new Handler();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
    
    @OnClick(R.id.btn)
    public void onViewClicked() {
        new DownloadTask().execute();
    }
    
    class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
    
        @Override
        protected void onPreExecute() {
            KLog.d("開始下載");
            pb.setVisibility(View.VISIBLE);
        }
    
        @Override
        protected Boolean doInBackground(Void... voids) {
            KLog.d("開始下載");
            while (true){
                try {
                    Thread.sleep(1000);
                    progress += 10;
                    publishProgress(progress);
                    if(progress >= 100)
                        break;
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return false;
                }
            }
            return true;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            KLog.d("正在下載"+values[0]);
            pb.setProgress(values[0]);
        }
    
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            if(aBoolean){
                KLog.d("下載成功");
                pb.setVisibility(View.GONE);
            }else{
                KLog.d("下載失敗");
            }
        }
    }
    

    }


AsyncTask優(yōu)點:簡單,快捷
缺點:當(dāng)有多個異步操作時并需要變更ui復(fù)雜

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 簡介 1. 線程分類 主線程(UI線程) : 處理和界面相關(guān)的事情. 子線程 : 處理耗時操作. Android中...
    王世軍Steven閱讀 974評論 0 2
  • 由于Android的特性,如果要執(zhí)行耗時操作,則必須方法子線程中執(zhí)行。除了Thread可以開啟子線程外,Andro...
    Ruheng閱讀 26,044評論 6 18
  • 由于從wordpress將文章倒回,發(fā)現(xiàn)格式有點混亂,經(jīng)努力調(diào)整后依然有部分的格式還未調(diào)教好,請多多包涵. 分析A...
    walker_lee0707閱讀 1,334評論 3 51
  • Android中的線程 線程,在Android中是非常重要的,主線程處理UI界面,子線程處理耗時操作。如果在主線程...
    shenhuniurou閱讀 875評論 0 3
  • 妻子的白頭發(fā) 象一枚枚針扎進我的瞳孔 血,從瞳孔內(nèi)側(cè)漫涌 沿著神經(jīng)流到我心里 老了,我們真的老了 妻子的白頭發(fā),一...
    陶廬閱讀 418評論 0 5

友情鏈接更多精彩內(nèi)容