Android訊息機制Handler解析(原始碼+Demo)
阿新 • • 發佈:2019-01-30
新建了一個qq群 482543750,歡迎一起學習Android的小夥伴加入。
提供各種Android學習資料,面試資料,Android簡歷模板。
Handler是開發人員在面試過程中最常見的問題之一了,這篇文章將較為全面地對Handler進行解讀,包括原始碼層,以及使用方法。
如果看完文章有疑問,歡迎在評論中一起探討
基本內容包括:
看完文章之後,可以用這個圖片進行復習。
一、什麼是Handler Handler是Android提供用來更新UI的一套機制,也是一套訊息處理機制,可以用它來發送訊息,也可以用它來接收訊息。 二、為什麼使用Handler Android在設計之時,就封裝了一套訊息的建立、傳遞、處理機制,若不遵循這樣的處理機制,就沒辦法更新UI資訊,並且會丟擲異常 三、Handler用法public class MainActivity extends AppCompatActivity { private TextView mTextView; private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; private String[] str = new String[]{"傲慢","偏見","殭屍"}; private int index = 0; MyRunnable myRunnable = new MyRunnable(); private class MyRunnable implements Runnable{ @Override public void run() { index = index % 3; mTextView.setText(str[index]); index ++; mHandler.postDelayed(myRunnable,1000); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.tv); mHandler.postDelayed(myRunnable,1000); } }
2、sendMessage()回撥handleMessage()傳遞訊息 Demo:在子執行緒中得到資訊,傳送至主執行緒,更新textview的內容
public class MainActivity extends AppCompatActivity { private TextView mTextView; Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { mTextView.setText(msg.obj+""+ "arg1="+msg.arg1 + " arg2="+msg.arg2); super.handleMessage(msg); } }; private class Person{ String name; int age; @Override public String toString() { return "name="+name+" age="+age; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.tv); new Thread(){ @Override public void run() { Message msg = new Message(); msg.arg1 = 1; msg.arg2 = 2; Person person = new Person(); person.name = "pig"; person.age = 10; msg.obj = person; mHandler.sendMessage(msg); } }.start(); } }
3、sendToTarget()傳遞訊息 與第二種用法原理一致
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
mTextView.setText(msg.obj+""+ "arg1="+msg.arg1 + " arg2="+msg.arg2);
super.handleMessage(msg);
}
};
private class Person{
String name;
int age;
@Override
public String toString() {
return "name="+name+" age="+age;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
new Thread(){
@Override
public void run() {
Message msg = mHandler.obtainMessage();//同樣可以獲取Message物件
msg.arg1 = 1;
msg.arg2 = 2;
Person person = new Person();
person.name = "pig";
person.age = 10;
msg.obj = person;
msg.sendToTarget();
}
}.start();
}
}
4、使用CallBack截獲Handler的訊息
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
Handler mHandler = new Handler(new Handler.Callback() {
//傳入CallBack物件,對於過載的返回值為bollean的handleMessage()
//返回值為false,將先執行這個方法,再執行返回值為void的handleMessage()方法
//返回值為true,只執行這個方法
@Override
public boolean handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "截獲訊息", Toast.LENGTH_SHORT).show();
return false;
}
}){
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "發出訊息", Toast.LENGTH_SHORT).show();
}
};
private class Person{
String name;
int age;
@Override
public String toString() {
return "name="+name+" age="+age;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendEmptyMessage(0);
}
});
}
}
四、為什麼在Android中智慧通過Handler機制在主執行緒中更新UI? 最根本的是解決多執行緒併發問題。 假如在同一個Activity中,有多個執行緒同時更新UI,且沒有加鎖,那會導致什麼問題呢? UI更新混亂。 假如加鎖呢? 會導致效能下降。 使用Handler機制,我們不用去考慮多執行緒的問題,所有更新UI的操作,都是在 主執行緒訊息佇列中輪詢去處理的。 五、Handler機制的原理 1、Handler封裝了訊息的傳送(主要包括訊息傳送給誰) Looper (1)內部包含一個訊息佇列,即MessageQueue,所有Handler傳送的訊息都會進入這個佇列 (2)Looper.loop方法,是一個死迴圈,不斷從MessageQueue取出訊息,如有訊息就處理,沒有就阻塞 2、MessageQueue,一個訊息佇列,可以新增訊息,處理訊息 3、Handler內部會跟Looper進行關聯,也就是說,在Handler內部可以找到Looper,找到了Looper也就找到了MessageQueue,在Handler中傳送訊息,其實就是向Message傳送訊息, 總結:Handler負責傳送訊息,Looper負責接收訊息,並把訊息回傳給Handler自己,而MessageQueue是一個儲存訊息的容器。 原始碼層: Android的應用程式是通過ActivityThread進行建立,在ActivityThread預設建立一個Main執行緒,一個Looper,所有更新UI的執行緒都是通過Main執行緒進行建立的。 檢視Looper.loop的原始碼,發現確實是一個死迴圈
public static void loop() {
final Looper me = myLooper();
if (me == null) {
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 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();
}
}
發現是通過msg.target.dispatchMessage()方法來處理訊息,檢視其原始碼
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
從原始碼看出,當有CallBack的時候,會截獲訊息,沒有的話會回撥handleMessage()來處理訊息 而對於SendMessage()系列的方法,這裡不再做過多解析,但從其原始碼可以看出,確實是最終把訊息傳入了訊息佇列中。 六、建立與執行緒相關的Handler 在子執行緒中建立Handler,需要通過Looper.prepare()獲取Looper,且呼叫Looper.loop()方法對訊息佇列中的Message進行輪詢
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
public Handler mHandler = new Handler(){//主執行緒中的Handler
@Override
public void handleMessage(Message msg) {
Log.d("CurrentThread",Thread.currentThread()+"");//列印Thread 的ID
}
};
class MyThread extends Thread{
private Handler handler;//子執行緒中的Handler
@Override
public void run() {
Looper.prepare();//獲取Looper
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("CurrentThread",Thread.currentThread()+"");
}
};
Looper.loop();//輪詢訊息佇列
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyThread thread= new MyThread();
thread.start();
try {
thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.handler.sendEmptyMessage(1);
mHandler.sendEmptyMessage(1);
}
}
輸出的結果 03-31 20:56:06.498 1804-1816/? D/CurrentThread: Thread[Thread-113,5,main] 03-31 20:56:06.578 1804-1804/com.lian.handlerdemo D/CurrentThread: Thread[main,5,main] 七、HandlerThread HandlerThread本質是一個Thread,區別在於他在run()之後建立了一個含有訊息佇列的Looper,這樣我們在子執行緒中建立Handler時候只需指定使用HandlerThread中的Looper,不用再呼叫Looper.prepare(),looper.loop()等,簡化了操作。 Android系統提供的Handler使用的Looper預設綁定了UI執行緒的訊息佇列,所以我們在Handler中不能進行耗時操作,而對於非UI執行緒,若想使用訊息機制,HandlerThread內部的Looper是最合適的,他不會阻塞UI執行緒。
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
public HandlerThread mHandlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandlerThread = new HandlerThread("handler thread");
mHandlerThread.start();
Handler handler = new Handler(mHandlerThread.getLooper()){//通過getLooper()獲取Looper
@Override
public void handleMessage(Message msg) {
Log.d("current thread","" + Thread.currentThread());
}
};
handler.sendEmptyMessage(1);
}
}
結果: 03-31 21:36:42.770 7225-7237/? D/current thread: Thread[handler thread,5,main]
八、主執行緒與子執行緒資訊互動 主執行緒中的Handler與子執行緒中的Handler互相傳送訊息,只要呼叫對方的sendMessage()就可以了
public class MainActivity extends AppCompatActivity {
public Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("current thread", "" + Thread.currentThread());
Message message = new Message();
message.what = 1;
handler.sendMessageDelayed(message,1000);//向子執行緒的Handler傳送訊息
}
};
public HandlerThread mHandlerThread;
public Handler handler;
private Button btn1,btn2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = (Button) findViewById(R.id.btn);
btn2 = (Button) findViewById(R.id.btn2);
mHandlerThread = new HandlerThread("handler thread");//指定HandlerThread的名字
mHandlerThread.start();
handler = new Handler(mHandlerThread.getLooper()){//通過getLooper()獲取Looper
@Override
public void handleMessage(Message msg) {
Log.d("current thread", "" + Thread.currentThread());
Message message = new Message();
mHandler.sendMessageDelayed(message,1000);//向主執行緒中的Handler傳送訊息
}
};
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.sendEmptyMessage(1);//開始傳送訊息
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.removeMessages(1);//停止傳送訊息
}
});
}
}
結果: 03-31 22:21:11.422 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main] 03-31 22:21:12.422 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main] 03-31 22:21:13.422 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main] 03-31 22:21:14.422 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main] 03-31 22:21:15.426 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main] 03-31 22:21:16.426 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main] 03-31 22:21:20.414 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main] 03-31 22:21:21.414 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main] 03-31 22:21:22.414 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main] 03-31 22:21:23.418 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main]
九、四種更新UI的方法 1、Handler.post(); 2、Handler.sendMessage(); 第一二種方法事實上沒有本質的區別,都是通過傳送訊息,在UI執行緒中更新UI,前面已經做過演示,不再贅述 3、runOnUIThread() 使用方法:
public class MainActivity extends AppCompatActivity {
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText("更新UI");
}
});
}
}.start();
}
}
我們檢視runOnUIThread()的原始碼
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
可以發現,其本質上仍然是通過Handler.post()方法再UI執行緒中更新UI 4、View.post() 使用方法
public class MainActivity extends AppCompatActivity {
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTextView.post(new Runnable() {
@Override
public void run() {
mTextView.setText("更新UI");
}
});
}
}.start();
}
}
檢視其原始碼,一樣是採用Handler.post()方法更新UI
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
十、在非UI執行緒中更新UI的方法 先看一個Demo
public class MainActivity extends AppCompatActivity {
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
new Thread(){
@Override
public void run() {
mTextView.setText("更新UI了");
}
}.start();
}
}
結果:
public class MainActivity extends AppCompatActivity {
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTextView.setText("更新UI了");
}
}.start();
}
}
更新失敗,丟擲異常
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
發現,沒有Looper,就會丟擲這個執行時異常。