android筆記--android中的多線程--Handler, Looper, MessageQueue, Message類
0
多線程在所有編程語言中都是比較難以理解和掌握的, 這幾天上網看了很多android多線程方面的資料, 在這里做一些總結.
什么時候使用多線程:
1. 耗時操作使用多線程, 耗時操作放在UI線程中會導致用戶的操作無法得到響應.
2. 阻塞操作使用多線程, 理由同上.
3. 多核CUP的設備使用多線程, 可以有效提高CPU的利用率.
4. 并行操作使用多線程.
android中的多線程模型主要涉及的類有:Looper, Handler, MessageQueue, Message等.
Looper類用來創建消息隊列. 每個線程最多只能有一個消息隊列, android中UI線程默認具有消息隊列, 但非UI線程在默認情況下是不具備消息隊列的. 如果需要在非UI線程中開啟消息隊列, 需要調用Looper.prepare()方法, 在該方法的執行過程中會創建一個Looper對象, 而Looper的構造函數中會創建一個MessageQueue instance(Looper的構造函數是私有的, 在Looper類之外無法創建其對象). 此后再為該線程綁定一個Handler instance, 然后調用Looper.loop()方法, 就可以不斷的從消息隊列中取出消息和處理消息了. Looper.myLoop()方法可以得到線程的Looper對象, 如果為null, 說明此時該線程尚未開啟消息隊列.
Handler類用于處理消息. 該類具有四個構造函數:
1. public Handler(). 創建好的Handler instance將綁定在代碼所在的線程的消息隊列上, 因此一定要確定該線程開啟了消息隊列, 否則程序將發生錯誤. 使用這個構造函數創建Handler instance, 一般來說, 我們需要重寫Hanler類的handleMessage()方法, 以便在之后的消息處理時調用.
2. public Handler(Callback callback). Callback是Handler內部定義的一個接口, 因此想要使用這個構造函數創建Handler對象, 需要自定義一個類實現Callback接口, 并重寫接口中定義的handleMessage()方法. 這個構造函數其實與無參的構造函數類似, 也要確保代碼所在的線程開啟了消息隊列. 不同的是在之后處理消息時, 將調用callback的handleMessage()方法, 而不是Handler對象的handleMssage()方法.
3. public Handler(Looper looper). 這個構造函數表示創建一個Handler instance, 并將其綁定在looper所在的線程上. 此時looper不能為null. 此時一般也需要重寫Hanler類的handleMessage()方法
4. public Handler(Looper looper, Callback callback). 可以結合2和3理解.
MessageQueue類用于表示消息隊列. 隊列中的每一個Message都有一個when字段, 這個字段用來決定Message應該何時出對處理. 消息隊列中的每一個Message根據when字段的大小由小到大排列, 排在最前面的消息會首先得到處理, 因此可以說消息隊列并不是一個嚴格的先進先出的隊列.
Message類用于表示消息. Message對象可以通過arg1, arg2, obj字段和setData()攜帶數據, 此外還具有很多字段. when字段決定Message應該何時出對處理, target字段用來表示將由哪個Handler對象處理這個消息, next 字段表示在消息隊列中排在這個Message之后的下一個Message, callback字段如果不為null表示這個Message包裝了一個runnable對象, what字段表示code, 即這個消息具體是什么類型的消息. 每個what都在其handler的namespace中, 我們只需要確保將由同一個handler處理的消息的what屬性不重復就可以.
將消息壓入消息隊列: Message對象的target字段關聯了哪個線程的消息隊列, 這個消息就會被壓入哪個線程的消息隊列中.
1. 調用Handler類中以send開頭的方法可以將Message對象壓入消息隊列中, 調用Handler類中以post開頭的方法可以將一個runnable對象包裝在一個Message對象中, 然后再壓入消息隊列, 此時入隊的Message其callback字段不為null, 值就是這個runnable對象. 調用Handler對象的這些方法入隊的Message, 其target屬性會被賦值為這個handler對象.
2. 調用Message對象的sendToTarget()方法可以將其本身壓入與其target字段(即handler對象)所關聯的消息隊列中.
將未來得及處理的消息從消息隊列中刪除:
調用Handler對象中以remove開頭的方法就可以.
從消息隊列中取出消息并處理消息: 所有在消息隊列中的消息, 都具有target字段. 消息是在target所關聯的線程上被取出和處理的.
1. 如果取出的Message對象的callback字段不為null, 那么就調用callback字段的run()方法(callback字段的類型是runnable). 注意此時并不開啟一個新的線程運行run()方法, 而是直接在handler對象(即Message的target字段)所關聯的線程上運行.
2. 如果取出的Message對象的callback字段為null, 且Handler對象中的callback字段也為null, 那么這個消息將由Handler對象的handleMessage(msg)方法處理. 注意Message對象的callback字段是Runnable類型的而Handler對象的callback字段是Callback類型的, Handler對象的callback字段是在創建Handler instance的時候指定的, 如果沒有指定則這個字段為null, 詳見Handler類的四個構造方法.
3. 如果取出的Message對象的callback字段為null, 且Handler對象中的callback字段不為null, 那么這個消息將由Handler對象中的callback字段的handleMessage方法處理.
線程間通信: 有了以上的敘述, 線程間的通信也就好理解了. 假如一個handler關聯了A線程上的消息隊列, 那么我們可以在B線程上調用handler的相關方法向A線程上的消息隊列壓入一個Message, 這個Message將在A線程上得到處理.
android中還有一些和多線程有關的類, 比如AsyncTask, HandlerThread等, 這些以后再總結.
[coolxing按: 轉載請注明作者和出處, 如有謬誤, 歡迎在評論中指正.]
轉自:http://coolxing.iteye.com/blog/1208371
什么時候使用多線程:
1. 耗時操作使用多線程, 耗時操作放在UI線程中會導致用戶的操作無法得到響應.
2. 阻塞操作使用多線程, 理由同上.
3. 多核CUP的設備使用多線程, 可以有效提高CPU的利用率.
4. 并行操作使用多線程.
android中的多線程模型主要涉及的類有:Looper, Handler, MessageQueue, Message等.
Looper類用來創建消息隊列. 每個線程最多只能有一個消息隊列, android中UI線程默認具有消息隊列, 但非UI線程在默認情況下是不具備消息隊列的. 如果需要在非UI線程中開啟消息隊列, 需要調用Looper.prepare()方法, 在該方法的執行過程中會創建一個Looper對象, 而Looper的構造函數中會創建一個MessageQueue instance(Looper的構造函數是私有的, 在Looper類之外無法創建其對象). 此后再為該線程綁定一個Handler instance, 然后調用Looper.loop()方法, 就可以不斷的從消息隊列中取出消息和處理消息了. Looper.myLoop()方法可以得到線程的Looper對象, 如果為null, 說明此時該線程尚未開啟消息隊列.
Handler類用于處理消息. 該類具有四個構造函數:
1. public Handler(). 創建好的Handler instance將綁定在代碼所在的線程的消息隊列上, 因此一定要確定該線程開啟了消息隊列, 否則程序將發生錯誤. 使用這個構造函數創建Handler instance, 一般來說, 我們需要重寫Hanler類的handleMessage()方法, 以便在之后的消息處理時調用.
2. public Handler(Callback callback). Callback是Handler內部定義的一個接口, 因此想要使用這個構造函數創建Handler對象, 需要自定義一個類實現Callback接口, 并重寫接口中定義的handleMessage()方法. 這個構造函數其實與無參的構造函數類似, 也要確保代碼所在的線程開啟了消息隊列. 不同的是在之后處理消息時, 將調用callback的handleMessage()方法, 而不是Handler對象的handleMssage()方法.
3. public Handler(Looper looper). 這個構造函數表示創建一個Handler instance, 并將其綁定在looper所在的線程上. 此時looper不能為null. 此時一般也需要重寫Hanler類的handleMessage()方法
4. public Handler(Looper looper, Callback callback). 可以結合2和3理解.
MessageQueue類用于表示消息隊列. 隊列中的每一個Message都有一個when字段, 這個字段用來決定Message應該何時出對處理. 消息隊列中的每一個Message根據when字段的大小由小到大排列, 排在最前面的消息會首先得到處理, 因此可以說消息隊列并不是一個嚴格的先進先出的隊列.
Message類用于表示消息. Message對象可以通過arg1, arg2, obj字段和setData()攜帶數據, 此外還具有很多字段. when字段決定Message應該何時出對處理, target字段用來表示將由哪個Handler對象處理這個消息, next 字段表示在消息隊列中排在這個Message之后的下一個Message, callback字段如果不為null表示這個Message包裝了一個runnable對象, what字段表示code, 即這個消息具體是什么類型的消息. 每個what都在其handler的namespace中, 我們只需要確保將由同一個handler處理的消息的what屬性不重復就可以.
將消息壓入消息隊列: Message對象的target字段關聯了哪個線程的消息隊列, 這個消息就會被壓入哪個線程的消息隊列中.
1. 調用Handler類中以send開頭的方法可以將Message對象壓入消息隊列中, 調用Handler類中以post開頭的方法可以將一個runnable對象包裝在一個Message對象中, 然后再壓入消息隊列, 此時入隊的Message其callback字段不為null, 值就是這個runnable對象. 調用Handler對象的這些方法入隊的Message, 其target屬性會被賦值為這個handler對象.
2. 調用Message對象的sendToTarget()方法可以將其本身壓入與其target字段(即handler對象)所關聯的消息隊列中.
將未來得及處理的消息從消息隊列中刪除:
調用Handler對象中以remove開頭的方法就可以.
從消息隊列中取出消息并處理消息: 所有在消息隊列中的消息, 都具有target字段. 消息是在target所關聯的線程上被取出和處理的.
1. 如果取出的Message對象的callback字段不為null, 那么就調用callback字段的run()方法(callback字段的類型是runnable). 注意此時并不開啟一個新的線程運行run()方法, 而是直接在handler對象(即Message的target字段)所關聯的線程上運行.
2. 如果取出的Message對象的callback字段為null, 且Handler對象中的callback字段也為null, 那么這個消息將由Handler對象的handleMessage(msg)方法處理. 注意Message對象的callback字段是Runnable類型的而Handler對象的callback字段是Callback類型的, Handler對象的callback字段是在創建Handler instance的時候指定的, 如果沒有指定則這個字段為null, 詳見Handler類的四個構造方法.
3. 如果取出的Message對象的callback字段為null, 且Handler對象中的callback字段不為null, 那么這個消息將由Handler對象中的callback字段的handleMessage方法處理.
線程間通信: 有了以上的敘述, 線程間的通信也就好理解了. 假如一個handler關聯了A線程上的消息隊列, 那么我們可以在B線程上調用handler的相關方法向A線程上的消息隊列壓入一個Message, 這個Message將在A線程上得到處理.
android中還有一些和多線程有關的類, 比如AsyncTask, HandlerThread等, 這些以后再總結.
[coolxing按: 轉載請注明作者和出處, 如有謬誤, 歡迎在評論中指正.]
轉自:http://coolxing.iteye.com/blog/1208371