本來寫了開場白,算了,直接進入主題。
1、什麼是Handler,作用是啥,爲什麼人人用了都說好。
Handler,我去查了一下字典,有處理者管理者的意思,它主要是負責線程之間的通信,比如UI線程(主線程)和其他線程之間的。那爲什麼一定要它負責通信呢,直接操作ui不行嗎?當然不行,如果多個線程同時改變界面的屬性值,那會變的很混亂,也叫做會導致線程安全問題。爲此Android有一條必須遵守的規則:只允許UI線程修改Activity裏的UI組件,是不是很霸氣。但是這樣其他線程就無法動態改變UI屬性了,制定規則當然不是爲了堵住一條路,Handler的作用這時候就體現出來了。
1、在新啓動的線程中發送消息。
2、在主線程中處理消息。
比如你想改變界面上一張圖片,線程中發個消息,Handler收到後立馬乖乖的處理,然後給你修改。這麼聽話的東西怎麼會沒有人喜歡呢。
2、Handler簡單使用
先貼代碼
public class MainActivity extends Activity{
//定義一個圖片id數組
int[] imageIds = new int[]{...};
定義當前的顯示的圖片id
int currentId = 0;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView show = (ImageView)findViewById(R.id.show);
final Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg){
if(msg.what == 0x1233){
//修改照片
show.setImageResource(imageIds[currentId++] % imageIds.length);
}
}
}
//計時器
new Timer().schedule(new TimerTask(){
@Override
public void run(){
myHandler.sendMessage(0x1233);
}
}, 0, 1200);
}
}
這樣就完成了一個自動播放圖片的效果,定義了一個定時器,讓它定時的sendMessage,實質上就是開了一個新的線程,由於android不允許在新線程中訪問Activity裏的界面組件,所以只能發個消息咯,Handler也就定時的改變圖片,並且是循環的,在這裏不得不說算法好厲害。另外佈局文件就只有一個ImageView。
3、Looper,MessageQueue
其實Handler不是單打獨鬥,它還有幾個好朋友,Looper和MessageQueue。
先說Looper
- 每個線程只能擁有一個Looper,不能有第二個,它的loop()方法就是讀取MessageQueue中的消息,讀到之後就會把消息交給發送消息的Handler處理。看到這有人可能會說那你上面的Looper呢?別急,這是因爲在UI線程也就是主線程中,系統已經初始了一個Looper,所以我們就不用了啊~~。但是如果是自己啓動的線程,我在Looper源碼中發現註釋裏有個例子。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
先是Looper.prepare(),最後是Looper.loop()。我們來看看Looper.prepare()做了一些什麼。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference 這能讓你在創建Handler的時候能引用Looper
* this looper, before actually starting the loop. Be sure to call 在Looper使用前,必須保證調用了該方法
* {@link #loop()} after calling this method, and end it by calling 調用之後,在loop()就停止了
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//這裏應該就是new了Looper對象
sThreadLocal.set(new Looper(quitAllowed));
}
我用我蹩腳的英語翻譯了一下,這個方法保證了線程中只有一個Looper對象。這似乎是使用了重載的方式調用,這樣設計難道保證了封裝的特性?
然後loop()的源碼
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
//沒有調用prepare().會拋出異常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
//發消息的標識符,final
final long ident = Binder.clearCallingIdentity();
//死循環來了
for (;;) {
Message msg = queue.next(); // might block 獲取消息隊列的下一個
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted. 檢查標識符是否變了。
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
就是從MessageQueue中不斷取出消息
然後MessageQueue
消息隊列,它有一點神祕,自始至終都沒有見過它,在loop()方法的源碼中有這樣一個語句final MessageQueue queue = me.mQueue;
莫非Looper的構造器什麼的裏就有它?再次去瞧瞧:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
原來啊系統在創建Looper對象的時候,就會在它的構造器中創建該對象。而且MessageQueue是採用先進先出的方式管理消息。
3、總結一下
使用線程中的Handler:
1、Loop.prepare(),它構造器也會創建配套的MessageQueue對象
2、創建Handler實例,重寫handleMessage()方法
3、Looper.loop()。啓動Looper.
4、沒有第四
4、Handler的內存泄漏問題
視頻中說到,如果Activity結束了,但是和其有關聯的Handler還沒有處理完它的消息,如果Handler不是靜態的,就不會被Activity或者Service回收,Handler使用的內存就不會被釋放,這就造成了內存泄漏問題。這裏的一個解決方法,就是弱引用。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了具有弱引用的對象,都會回收它的內存。
public static class Testhandler extends Handler{
public final WeakReference<HandlerButtonActivity> mHandlerButoonActivityWeakReference;
public Testhandler(HandlerButtonActivity activity){
mHandlerButoonActivityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerButtonActivity activity = mHandlerButoonActivityWeakReference.get();
...}
}
大概會寫點異步,老師提了一下,所以沒弄懂啊啊啊啊