Android中的線程形態(tài)(二)(HandlerThread/IntentService)

本篇提綱(二).png

一.HandlerThread的使用與原理解析

??HandlerThread繼承于Thread,所以它本質(zhì)就是個Thread。與普通Thread的差別就在于,它不僅建立了一個線程该押,并且創(chuàng)立了消息隊列,有自己的looper,可以讓我們在自己的線程中分發(fā)和處理消息阵谚,并對外提供自己這個Looper對象的get方法蚕礼。
??HandlerThread自帶Looper使他可以通過消息隊列烟具,來重復(fù)使用當(dāng)前線程,節(jié)省系統(tǒng)資源開銷奠蹬。這是它的優(yōu)點也是缺點朝聋,每一個任務(wù)都將以隊列的方式逐個被執(zhí)行到,一旦隊列中有某個任務(wù)執(zhí)行時間過長罩润,那么就會導(dǎo)致后續(xù)的任務(wù)都會被延遲處理玖翅。

1.HandlerThread使用步驟

第一步:創(chuàng)建HandlerThread實例對象

HandlerThread handlerThread = new HandlerThread("myThread");

第二步:啟動HandlerThread線程

handlerThread.start();

第三步:構(gòu)建循環(huán)消息處理機制

 private Handler.Callback mSubCallback = new Handler.Callback() {
        //該接口的實現(xiàn)就是處理異步耗時任務(wù)的,因此該方法執(zhí)行在子線程中
        @Override
        public boolean handleMessage(Message msg) {
                //doSomething  處理異步耗時任務(wù)的
                mUIHandler.sendMessage(msg1);   //向UI線程發(fā)送消息割以,用于更新UI等操作
        }
    };

第四步:構(gòu)建子線程中的Handler:

//由于這里已經(jīng)獲取了workHandle.getLooper()金度,因此這個Handler是在HandlerThread線程也就是子線程中
childHandler = new Handler(handlerThread.getLooper(), mSubCallback);
button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        //發(fā)送異步耗時任務(wù)到HandlerThread中,也就是上面的mSubCallback中
        childHandler.sendMessage(msg);
    }
});

第五步:構(gòu)建UI線程Handler處理消息(如果需要更新UI的話有這一步)

    private Handler mUIHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //在UI線程中做的事情,比如設(shè)置圖片什么的
        }
    };

2.HandlerThread的實現(xiàn)原理

??HandlerThread的實現(xiàn)原理比較簡單严沥,源碼也比較少(只有150行代碼)猜极,首先它是繼承自Thread類的:

public class HandlerThread extends Thread {

因此它完全可以當(dāng)一個線程來使用,就像上面我們說的:new HandlerTread().start()這樣消玄,但是8!翩瓜!——我們之前在Android中的消息機制——Looper受扳、Handler、MessageQueue與Message這篇文章中說過兔跌,在子線程中創(chuàng)建一個Handler勘高,需要手動創(chuàng)建Looper,即先Looper.parpare()坟桅,完了再Looper.loop()
這樣华望,我們來看看HandlerThread中的run方法:

    @Override
    public void run() {
        mTid = Process.myTid();  //獲得當(dāng)前線程的id
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            //發(fā)出通知,當(dāng)前線程已經(jīng)創(chuàng)建mLooper對象成功仅乓,這里主要是通知getLooper方法中的wait
            notifyAll();
        }
        //設(shè)置當(dāng)前線程的優(yōu)先級
        Process.setThreadPriority(mPriority);
        //該方法實現(xiàn)體是空的赖舟,子類可以實現(xiàn)該方法,作用就是在線程循環(huán)之前做一些準(zhǔn)備工作夸楣,當(dāng)然子類也可以不實現(xiàn)宾抓。
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

可以看到,這個run方法里邊寫死了豫喧,先Looper.prepare()然后Looper.loop()洞慎,也即是說,我們的HandlerThread這個特殊的“線程”是再帶Looper的嘿棘,因此——我們不能再主線程使用它劲腿,畢竟主線程是自帶MainLooper的,一個線程只能有一個Looper,否則就會拋出異常鸟妙。
??這里我們說一下焦人,為什么要看這個run()方法挥吵,之前我們在Android中的線程形態(tài)(一)(進程/線程/線程池)中有講過,Thread工作的時候花椭,不論哪種實現(xiàn)方法忽匈,我們都必須重寫這個類的run()方法,在其中寫我們自己要做的事情矿辽〉ぴ剩可以看到,HandlerThread類的run()方法已經(jīng)給我們寫死了袋倔,這就注定我們只能將它用在子線程中雕蔽。

??我們再來看看HandelerThread類的額另一個重要的方法——getLooper():

    //該方法主要作用是獲得當(dāng)前HandlerThread線程中的mLooper對象
    public Looper getLooper() {
        if (!isAlive()) {   //如果線程不是活動的,則直接返回null
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        //如果線程已經(jīng)啟動宾娜,但是Looper還未創(chuàng)建的話批狐,就等待,直到Looper創(chuàng)建成功
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    //這里會調(diào)用wait方法去等待前塔,當(dāng)run方法中的notifyAll方法調(diào)用之后
                    //通知當(dāng)前線程的wait方法等待結(jié)束嚣艇,跳出循環(huán),獲得mLooper對象的值华弓。
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

OK,看到這里食零,結(jié)合上面的HandelrThread使用步驟,我們需要總結(jié)一下:

  • HandelrThread是一個自帶Looper的線程寂屏,因此只能作為子線程使用
  • HandlerThread必須配合Handler使用慌洪,HandlerThread線程中做具體事情,必須要在Handler的callback接口中進行凑保,他自己的run方法被寫死了。
  • 子線程中的Handler與HandlerThread的聯(lián)系是通過childHandler = new Handler(handlerThread.getLooper(), mSubCallback);這句來進行的涌攻,也就是你說欧引,childHandler獲得HandlerThread線程的Looper,這樣恳谎,他們兩個就在同一陣營了芝此。這也就是在創(chuàng)建Handler作為HandlerThread線程消息執(zhí)行者,必須在調(diào)用start()方法之后的原因——HandlerThread.start()之后因痛,run()方法才能跑起來婚苹,Looper才能得以創(chuàng)建,handlerThread.getLooper()才不會出錯鸵膏。

二.IntentService的使用與實現(xiàn)原理

??HandlerThread在Android中的一個具體的應(yīng)用就是IntentService膊升。IntentService是繼承于Service并處理異步請求的一個類。注意這句話——“繼承于Service”“處理異步請求”谭企。“繼承于Service”表示他是一個服務(wù)廓译,我們知道Service是存在于主線程的评肆,它之中不能存在耗時操作,否則的話回應(yīng)起ANR非区,因此便有了IntentService——這個封裝了HandlerThread和Handler的特殊Service瓜挽。

1.IntentService的使用

第一步:創(chuàng)建一個繼承IntentService類的子類
首先,通過源碼我們知道征绸,IntentService是一個繼承自Service類的抽象類久橙,因此我們必須創(chuàng)建一個繼承它的子類才能實現(xiàn)相關(guān)功能:

public class MyIntentService extends IntentService {

    public ServiceUpdate() {
         // 注意構(gòu)造函數(shù)參數(shù)為空,這個myIntentService字符串就是IntentService中工作線程的名字
         super("myIntentService");
    }

    //打印生命周期
    @Override
    public void onCreate() {
        Log.i("myIntentService", "onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("myIntentService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    
    @Override
      public void onStart(Intent intent, int startId) {
          Log.i("myIntentService", "onStart");
          super.onStart(intent, startId);
      }
    
     @Override
      public IBinder onBind(Intent intent) {
        Log.i("myIntentService", "onBind");
        return super.onBind(intent);
      }
      
    //重寫onHandleIntent方法管怠,在該方法中進行我們的各種事務(wù)處理
    @Override
    protected void onHandleIntent(Intent intent) {
         //一般Intent是從Activity發(fā)過來的淆衷,攜帶識別參數(shù),根據(jù)參數(shù)不同執(zhí)行不同的任務(wù)
        String taskName = intent.getExtras().getString("taskName");
        switch (taskName) {
        case "task1":
            Log.i("myIntentService", "task1");
            break;
        case "task2":
            Log.i("myIntentService", "task2");
            break;
        default:
            break;
        }
    }

    @Override
    public void onDestroy() {
        Log.i("myIntentService", "onDestroy");
        super.onDestroy();
    }
}

第二步:在Activity中開啟服務(wù):

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        ......

        //同一服務(wù)只會開啟一個工作線程排惨,在onHandleIntent函數(shù)里依次處理intent請求吭敢。
        Intent intent1 = new Intent(this,MyIntentService.class);
        Bundle bundle = new Bundle();
        bundle.putString("taskName", "task1");
        intent1.putExtras(bundle);
        startService(intent1);

        Intent intent2 = new Intent(this,MyIntentService.class);
        Bundle bundle2 = new Bundle();
        bundle2.putString("taskName", "task2");
        intent2.putExtras(bundle2);
        startService(intent2);
    }
}

第三步:在 Manifest 中注冊服務(wù)

<service android:name=".service.MyIntentService"/>

上述過程中我們啟動了兩個IntentService服務(wù),打印出來生命周期為:onCreate -> onStartCommand -> onStart -> onStartCommand -> onStart -> task1 -> task2 -> onDestroy暮芭,OnBinder沒有執(zhí)行鹿驼。
??從結(jié)果可以看到,onCreate 方法只執(zhí)行了一次辕宏,而 onStartCommand 和 onStart 方法執(zhí)行了兩次畜晰,開啟了兩個 工作線程(Work Thread),這就證實了之前所說的瑞筐,啟動多次凄鼻,但IntentService 的實例只有一個,這跟傳統(tǒng)的Service 是一樣的聚假。

2.IntentServic源碼分析

??IntentService繼承Service類之后块蚌,整體的源碼也就165行,跟HandlerThread差不多膘格,我們先來看看它的實例變量:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;     //服務(wù)所在線程的Looper
    private volatile ServiceHandler mServiceHandler;    //構(gòu)建一個服務(wù)中的Handler
    private String mName;       //服務(wù)所在線程的名字
    private boolean mRedelivery;
1).IntentService的構(gòu)造方法

上面加了注釋的三個變量較為重要峭范,我們待會會一步步分析。接下來我們看看他的構(gòu)造方法:

    public IntentService(String name) {
        super();
        mName = name;
    }

可以看到瘪贱,上線例子中我們得

    public ServiceUpdate() {
         super("myIntentService");
    }

其中“myIntentService”實際會上傳到父類IntentService的構(gòu)造方法中纱控,也就是IntentService所在線程的線程名字,IntentService構(gòu)造方法中的super();又會進一步調(diào)用Service類中的構(gòu)造函數(shù):

    public Service() {
        super(null);
    }

再網(wǎng)上菜秦,就到ContextWrapper類里邊去了甜害。如果我們看過Activity的源碼的話就會知道,Activity也是繼承自ContextWrapper類的球昨,而ContextWrapper類繼承自Context尔店!也就說,Service和Activity實際上都是一個Context!ContextWrapper有一個比較特殊的地方是闹获,他的實例化的地方是在ActivityThread中由系統(tǒng)完成的期犬,我們無權(quán)插手。因此避诽,在StartService或者StartActivity的時候龟虎,系統(tǒng)就會自動幫我們實例化這兩個組件。這樣沙庐,Service的構(gòu)造方法為空也就很好解釋了鲤妥。

2).OnCreat()

??看上面的生命周期流程圖我們知道,IntentService第一步指定的onCreat方法:

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

首先拱雏,super.onCreate();棉安,這個我們看到Service的onCreat()方法是空的,所以不管铸抑,接著看贡耽。HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");看到了吧,這里重量級人物——HandlerThread出場了鹊汛,新建了一個HandlerThread實例蒲赂,上面我們說過HandlerThread()的構(gòu)造方法傳進去的是HandlerThread所在線程的名字,然后thread.start();刁憋,嗯滥嘴,非常符合我們上半片文章講的HandlerThread使用流程。
??mServiceLooper = thread.getLooper();這里通過mServiceLooper獲取到HandlerThread線程的Looper至耻。然后mServiceHandler = new ServiceHandler(mServiceLooper);若皱,我們接著看源碼:

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

可以看到,ServiceHandler是IntentService類的一個內(nèi)部類尘颓,并且繼承自Handler走触,重寫了handleMessage方法。很顯然疤苹,這個類是用來處理startService(intent);傳遞來的消息的互广。這個消息處理的過程分為兩步,第一調(diào)用onHandleIntent((Intent)msg.obj);方法痰催;第二步stopSelf(msg.arg1);消息處理完后自動終止服務(wù)。所以我們接著而看onHandleIntent((Intent)msg.obj);:

    @WorkerThread
    protected abstract void onHandleIntent(Intent intent);

呦呵迎瞧?這是一個抽象方法夸溶,抽象類中的抽象方法意味著我們需要在子類中重寫這個方法,也就是說凶硅,這個方法才是我們處理消息的地方缝裁。可以看到,這個方法上面加了注解捷绑,是位于工作線程(子線程)中的韩脑。

3).onStartCommand()/onStart()

??好了,OnCreat()方法終于執(zhí)行完了粹污,onStartCommand()方法段多,它和onStart()方法實際上是連在一起的:

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

直接看onStart():

    @Override
    public void onStart(Intent intent, int startId) {
        //Service 啟動后直接就向 mServiceHandler 發(fā)送消息,則馬上就會執(zhí)行 handleMessage方法
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

這個方法中壮吩,mServiceHandler這個Handler的子類进苍,調(diào)用了obtainMessage()方法,該方法實際上上到Handler類中調(diào)用的是:

    public final Message obtainMessage(){
        return Message.obtain(this);
    }

這個方法中鸭叙,Message.obtain(this);這個方法我們復(fù)習(xí)一下觉啊,實際上就是Handler從消息池中取出一個消息,避免Message類重復(fù)創(chuàng)建的一個方法沈贝「苋耍回到IntentSerice的onStart()方法中,Message msg = mServiceHandler.obtainMessage();宋下,這句就是獲取一個Message對象嗡善,然后mServiceHandler.sendMessage(msg);通過Handler發(fā)送消息,添加到Looper中的MessageQuenue隊列中杨凑。

4).onDestroy()
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

安全退出Looper()

3.IntentService中的一些坑

1).為什么不建議通過 bindService() 啟動 IntentService滤奈?
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

??IntentService 源碼中的 onBind() 默認返回 null;不適合 bindService() 啟動服務(wù)撩满,如果你執(zhí)意要 bindService() 來啟動 IntentService蜒程,可能因為你想通過 Binder 或 Messenger 使得 IntentService 和 Activity 可以通信,這樣那么 onHandleIntent() 不會被回調(diào)伺帘,相當(dāng)于在你使用 Service 而不是 IntentService昭躺。

2).為什么多次啟動 IntentService 會順序執(zhí)行事件,停止服務(wù)后伪嫁,后續(xù)的事件得不到執(zhí)行领炫?

??IntentService 中使用的 Handler、Looper张咳、MessageQueue 機制把消息發(fā)送到線程中去執(zhí)行的帝洪,所以多次啟動 IntentService 不會重新創(chuàng)建新的服務(wù)和新的線程,只是把消息加入消息隊列中等待執(zhí)行脚猾,而如果服務(wù)停止葱峡,會清除消息隊列中的消息,后續(xù)的事件得不到執(zhí)行龙助。

站在巨人的肩膀上摘蘋果:
IntentService 示例與詳解
Service 和 IntentService 的區(qū)別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砰奕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌军援,老刑警劉巖仅淑,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胸哥,居然都是意外死亡涯竟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門烘嘱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昆禽,“玉大人,你說我怎么就攤上這事蝇庭∽肀睿” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵哮内,是天一觀的道長盗棵。 經(jīng)常有香客問我,道長北发,這世上最難降的妖魔是什么纹因? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮琳拨,結(jié)果婚禮上瞭恰,老公的妹妹穿的比我還像新娘。我一直安慰自己狱庇,他們只是感情好惊畏,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著密任,像睡著了一般颜启。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浪讳,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天缰盏,我揣著相機與錄音,去河邊找鬼淹遵。 笑死口猜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的透揣。 我是一名探鬼主播济炎,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淌实!你這毒婦竟也來了冻辩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤拆祈,失蹤者是張志新(化名)和其女友劉穎恨闪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體放坏,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡咙咽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了淤年。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钧敞。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖麸粮,靈堂內(nèi)的尸體忽然破棺而出溉苛,到底是詐尸還是另有隱情,我是刑警寧澤弄诲,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布愚战,位于F島的核電站,受9級特大地震影響齐遵,放射性物質(zhì)發(fā)生泄漏寂玲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一梗摇、第九天 我趴在偏房一處隱蔽的房頂上張望拓哟。 院中可真熱鬧,春花似錦伶授、人聲如沸断序。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逢倍。三九已至,卻和暖如春景图,著一層夾襖步出監(jiān)牢的瞬間较雕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工挚币, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亮蒋,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓妆毕,卻偏偏與公主長得像慎玖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子笛粘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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