Android線程與線程池

因為要寫團隊的推文所以寫了這個因妙,很多東西都參考自《Android開發(fā)藝術探索》這本書,姑且當做自己的學習筆記票髓。感覺自己還需要學很多東西攀涵,要有自己的東西和思想。

在Android中洽沟,線程分為主線程和子線程以故,主界面用于與用戶交互谓传,進行UI的相關操作照筑,而子線程則負責耗時操作。如果在主線程中進行耗時操作烙懦,就會使程序無法及時的響應跷车。因此耗時操作必須放在子線程中進行棘利。

1、主線程和子線程

主線程是指進程所有用的線程朽缴,在Android中即指進行UI與用戶交互的線程就是主線程善玫。因此在Android開發(fā)中,需要竟可能的把耗時操作密强,網絡請求訪問操作茅郎,數(shù)據(jù)庫讀取操作等放在子線程,以避免主線程長期處于占用狀態(tài)以降低用戶體驗或渤。系統(tǒng)要求網絡訪問必須在子線程中進行系冗,否則會拋出NetworkOnMainThreadException異常。

2薪鹦、線程形態(tài)

Android中的線程形態(tài)有傳統(tǒng)的Thread掌敬,AsyncTask,HandlerThread和IntentService池磁。

2.1奔害、AsyncTask

AsyncTask封裝了Thread和Handler,必須在主線程進行調用地熄,它可以在子線程中執(zhí)行任務华临,然后將執(zhí)行的結果傳遞給主線程并更新UI。但AsyncTask并不適合執(zhí)行特別耗時的任務端考。

2.1.1雅潭、參數(shù):

AsyncTask是一個泛型類揭厚,提供了三個泛型參數(shù),Params 扶供,Progress 和Result筛圆。

  • Params表示參數(shù)的類型
  • Progress表示后臺任務的執(zhí)行進度的類型
  • Result表示后臺任務返回結果的類型

AsyncTask的聲明:

public abstract class Asynctask<Params,Progress,Result>

2.1.2、方法:

AsyncTask提供了一些核心方法:

  • onPreExecute() 在主線程中調用用來進行異步任務的準備操作椿浓。
  • doInBackground(Params …… params) 在執(zhí)行完onPreExecute()后進行子線程任務時自動調用顽染,Params表示異步任務的輸入參數(shù)。在方法中可以通過publishProgress更新任務的完成進度轰绵,同時在結束調用后會返回結果給onPostExecute()方法。
  • onProgressUpdate(Params …… params) 在主線程中用于顯示任務進行的進度尼荆,在publishProgress()方法中被調用左腔。
  • onProgressExecute(Result result) 在主線程中用戶獲取任務結束后回返的結果,即doInBackground的返回值捅儒。
  • onCancelled() 在主線程中執(zhí)行液样,當異步任務被取消后不會執(zhí)行onProgressExecute方法而會執(zhí)行onCancelled方法。

2.1.3巧还、調用:

AsyncTask的使用步驟如下:
1.創(chuàng)建AsyncTask子類鞭莽,具體實現(xiàn)其核心方法
2.創(chuàng)建子類的實例對象
3.調用execute執(zhí)行異步線程任務

2.1.4、AsyncTask的限制:

1.必須在主線程加載麸祷,即第一次訪問AsyncTask必須在主線程澎怒,對象必須在主線程創(chuàng)建
2.execute必須在UI線程中調用
3.不能再程序中直接調用onPreExecute(),onPostExecute(),doInBackground和onProgressUpdate方法。
4.一個AsyncTask方法只能使用一次

2.1.5阶牍、AsyncTask的工作原理:

  • execute方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params,Progress,Result>executeOnExecutor(Executor exec, Params...params){
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                    + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                    + " the task has already been executed "
                    + "(a task can be executed only once)");
        }
    }
    mStatus=Status.RUNNING;
    onPreExecute();
    mWorker.mParams=params;
    exec.execute(mFuture);
     return this;
}

在execute方法中喷面,會檢測AsyncTask的當前狀態(tài),如果當前為RUNNING或FINISHED狀態(tài)走孽,系統(tǒng)會拋出異常惧辈,當為空閑狀態(tài),則置為RUNNING狀態(tài)磕瓷。
置為RUNNING狀態(tài)后會調用onPreExecute方法盒齿,同時將參數(shù)Params傳遞給mWorker的mParams。之后調用exec.execute困食,并傳入mFuture边翁,其中exec就是傳進來的sDefaultExecutor。

  • sDefaultExecutor
    sDefaultExecutor是一個串行的線程池陷舅,一個進程中的所有的AsyncTask全部在這個串行線程池中排隊執(zhí)行倒彰,在executeOnExecute方法中,onPreExecute方法最先執(zhí)行莱睁。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
        
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
 
    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
           
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

mTasks代表了SerialExecutor這個串行線程池的任務緩存隊列待讳,之后用offer向任務緩存隊列中添加了一個任務芒澜,調用了r的run方法,r就是傳入的mFuture创淡,而mFuture的run方法內部會調用MWorker的call方法痴晦,接著調用doInBackground方法,開始執(zhí)行后臺任務琳彩。執(zhí)行結束后會調用scheduleNext方法誊酌,執(zhí)行下一個任務。另外mActive表示了AsyncTask的對象露乏,如果MActive為null碧浊,則同樣會執(zhí)行scheduleNext方法。在scheduleNext方法中瘟仿,若緩存隊列不為空箱锐,則從隊列中取出任務執(zhí)行。
綜上劳较,SerialExecutor的工作是將任務加入緩存隊列中驹止,而執(zhí)行任務的是THREAD_POOL_EXECUTOR。

  • THREAD_POOL_EXECUTOR
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
 
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);
 
    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
 
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

即corePoolSize的CPU數(shù)加一观蜗,maxumumPoolSize的CPU數(shù)二倍加一臊恋,存活1s,任務緩存隊列為LinkedBlockingQueue墓捻。

  • postResult方法
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

首先用getHandler方法獲取AsyncTask獲取內部包含的sHandler抖仅,然后發(fā)送MESSAGE_POST_RESULT消息。

  • sHandler
private static final InternalHandler sHandler = new InternalHandler();
 
private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }
 
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
} 

sHandler是一個靜態(tài)的Handler對象砖第,為了sHandler能夠執(zhí)行環(huán)境從后臺切換到主線程岸售,則應使用主線程的Looper,在主線程中創(chuàng)建sHandler厂画。sHandler收到MESSAGE_POST_RESULT后凸丸,會調用finish方法。源碼如下:

  • finish
private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } 
    else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
} 

調用isCancelled()判斷任務是否取消袱院,如果取消則調用 onCancelled(result)屎慢,否則調用onPostExecute(result)。同時將mStatus設為FINISHED忽洛,表示對象執(zhí)行完畢腻惠。

2.2、HandlerThread

2.2.1欲虚、簡介及實現(xiàn)

HandlerThread繼承了Thread集灌,能夠使用Handler,實現(xiàn)簡單,節(jié)省系統(tǒng)資源開銷欣喧。
實現(xiàn)如下:

HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
mHandler = new Handler(thread.getLooper());
mHandler.post(new Runnable(){...});

2.2.2腌零、源碼及分析

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

}

它創(chuàng)建了一個消息隊列,外界需要通過Handler的消息通知它執(zhí)行一個任務唆阿,由于HandlerThread的run方法是一個無限循環(huán)方法益涧,所以可以通過quit方法終止線程的執(zhí)行。

2.3驯鳖、IntentService

IntentService是特殊的Service闲询,繼承了Service,因為IntentService是一個抽象類浅辙,所以必須創(chuàng)建IntentService的子類才能使用扭弧。
同時,IntentService是服務记舆,所以在執(zhí)行時寄狼,優(yōu)先級較高。
IntentService封裝了HandlerThread和Handler氨淌。

2.3.1、使用

1.定義IntentService子類伊磺,傳入線程名稱盛正,復寫onHandlerIntent()方法。
2.在Manifest.xml中注冊
3.在Activity中開啟服務

2.3.2屑埋、源碼分析

  • onCreate
    在IntentThread被第一次啟動時豪筝,會調用相應的onCreate方法。具體如下:
@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
  
    
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper); 
}

首先實例化新線程摘能,然后獲得工作線程的 Looper续崖,并且構造一個Handler對象mServiceHandler,通過mServiceHandler發(fā)送的消息都會在HandlerThread中執(zhí)行团搞。

  • onStartCommand

每次啟動IntentService都會調用onStartCommand方法严望,用于處理每個后臺任務的Intent。

public int onStartCommand(Intent intent, int flags, int startId) {

    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

public void onStart(Intent intent, int startId) {

    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

可以看出在onStartCommand中會調用onStart 方法逻恐,在onStart方法中像吻,獲得ServiceHandler消息的引用,后將消息包裝到msg中复隆,之后發(fā)送消息拨匆。這個消息會在HandlerThread中被處理。

  • ServiceHandler方法
private final class ServiceHandler extends Handler {

    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
  
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);
    }

}

IntentService的onHandleIntent方法是一個抽象方法挽拂,需要實現(xiàn)惭每,作用是從Intent中區(qū)分具體任務并執(zhí)行,調用執(zhí)行完任務后亏栈,stopSelf會直接停止服務台腥,當存在多個任務時宏赘,stopSelf則會在最后一個任務執(zhí)行完畢后停止服務。

3览爵、線程池

在實際使用中置鼻,線程池往往更加放標,和線程相比蜓竹,線程池性能開銷小箕母,可避免因為大量線程搶占資源而堵塞,也更容易管理俱济。

java中線程池的接口是ExecutorService嘶是,默認實現(xiàn)是ThreadPoolExecutor。

3.1蛛碌、ThreadPoolExecutor

ThreadPoolExecutor是線程池的實現(xiàn)聂喇,構造方法如下

public ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory)
  • corePoolSize

核心線程數(shù),核心線程會在線程池中一直存活蔚携。若將allowCoreThreadTimeOut設置為true希太,那么核心線程也將有timepout,會在超時后被殺死酝蜒。

  • maximumPoolSize

最大線程數(shù)誊辉。

  • keepAliveTime

非核心線程限制的超時時長,超過設置時間后非核心線程會被收回亡脑,allowCoreThreadTimeOut設置為true后核心線程也會被收回堕澄。

  • unit
    時間的參數(shù)單位

  • workQueue

任務隊列,通過線程池的execute方法提交的Runnable對象儲存在其中

  • threadFactory

線程工廠霉咨,為線程池創(chuàng)建線程

ThreadPoolExecutor在執(zhí)行線程任務是需滿足一定的規(guī)則

  • 首先使用核心線程蛙紫,在核心線程不夠時才啟用新線程
  • 當任務書大于核心線程數(shù)量,那么會被插入等待隊列中
  • 如果不能插入等待隊列途戒,且線程數(shù)量未達到最大線程數(shù)坑傅,則會開啟新線程
  • 若無法插入等待隊列且無法創(chuàng)建新線程,則請求會被拒絕

3.2喷斋、線程池的分類

線程池分為四種不同的功能裁蚁,分別是FixedThreadPool、CachedThreadPool继准、ScheduledThreadPool枉证、SingkeThreadExecutor。

  • FixedThreadPool

通過execute的newFixedThreadPool方法創(chuàng)建移必,固定大小的線程池室谚,每次提交任務都會創(chuàng)建新的線程,直到池中線程數(shù)目達到最大。如果有一個線程異常結束后秒赤,會產生一個新的線程補充進去猪瞬。能夠更快地相應外界請求。
具體實現(xiàn)如下:

public static ExecutorSrevice newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThread,nThreads,
                            0L,TimeUnit.MILLISECINDS,
                            new LinkedBlockingQueue<Runnable>());
}
  • CachedThreadPool
    一個可以根據(jù)需要創(chuàng)建線程的線程池入篮,對于很多短期的異步任務陈瘦,將有效提高性能,調用execute()將重用已構造的線程潮售,如果沒有線程可用痊项,將創(chuàng)造一個新的線程并加入線程池,并移除超過60s未使用的線程酥诽。適合執(zhí)行耗時少的任務鞍泉。
    具體實現(xiàn)如下:
public static ExecutorService newCachedThreadPool(){
    reutrn new ThreadPoolExecutor(0,Interger.MAX_VALUE,
                            60L,TimeUnit.SECONDS,
                            new SynchronousQueue<Runnable>());
}
  • ScheduledThreadPool
    核心線程有限,非核心線程數(shù)無線肮帐,創(chuàng)建一個大小無限的線程池咖驮,此線程池支持定時以及周期性執(zhí)行任務的需求。
    具體實現(xiàn)如下:
public static ScheduledThreadPool newScheduledThreadPool(int corePoolSize){
    reutrn new ScheduledThreadPool(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize){
    super(corePoolSize,Integer.MAX_VALUE,0,MANOSECONDS,new DelayWorkQueue());
}

  • SingkeThreadExecutor
    創(chuàng)建單線程池训枢,即線程池中只有一個線程托修,所有的任務是串行執(zhí)行,如果池中線程因異常結束恒界,會有一個新的線程來代替睦刃。以此保證了任務按提交順序執(zhí)行。
    具體實現(xiàn)如下:
public static ExecutorService newScheduledThreadExecutor(){
    reutrn new FinalizeableDelegatedExecutorService
            (new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECINDS,new LinkedBlockingQueue<Runnable>()));
}

參考來源:

《Android開發(fā)藝術探索》
https://www.cnblogs.com/absfree/p/5357678.html
https://blog.csdn.net/carson_ho/article/details/53407806

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末仗处,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子枣宫,更是在濱河造成了極大的恐慌婆誓,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件也颤,死亡現(xiàn)場離奇詭異洋幻,居然都是意外死亡,警方通過查閱死者的電腦和手機翅娶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進店門文留,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人竭沫,你說我怎么就攤上這事燥翅。” “怎么了蜕提?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵森书,是天一觀的道長。 經常有香客問我,道長凛膏,這世上最難降的妖魔是什么杨名? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮猖毫,結果婚禮上台谍,老公的妹妹穿的比我還像新娘。我一直安慰自己吁断,他們只是感情好趁蕊,可當我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胯府,像睡著了一般介衔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骂因,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天炎咖,我揣著相機與錄音,去河邊找鬼寒波。 笑死乘盼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的俄烁。 我是一名探鬼主播绸栅,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼页屠!你這毒婦竟也來了粹胯?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤辰企,失蹤者是張志新(化名)和其女友劉穎风纠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牢贸,經...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡竹观,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了潜索。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臭增。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖竹习,靈堂內的尸體忽然破棺而出誊抛,到底是詐尸還是另有隱情,我是刑警寧澤整陌,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布芍锚,位于F島的核電站昔园,受9級特大地震影響,放射性物質發(fā)生泄漏并炮。R本人自食惡果不足惜默刚,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逃魄。 院中可真熱鬧荤西,春花似錦、人聲如沸伍俘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽癌瘾。三九已至觅丰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妨退,已是汗流浹背妇萄。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咬荷,地道東北人冠句。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像幸乒,于是被迫代替她去往敵國和親懦底。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,499評論 2 348

推薦閱讀更多精彩內容