閱讀五分鐘,每日十點,和您一起終身學習,這裏是程序員Android
本篇文章主要介紹 Android
開發中的部分知識點,通過閱讀本篇文章,您將收穫以下內容:
一、Handler 簡介
二、Handler 消息處理機制原理
三、Handler 機制處理的4個關鍵對象
四、 Handler常用方法
五、子線程更新UI 異常處理
六、主線程給子線程發送消息的方法
七、主線程發送消息給子線程的例子
八、子線程給主線程發送消息的方法
九、 主、子 線程 互發消息方法
十、子線程方法中調用主線程更新UI的方法
十一、移除Handler 發送的消息方法
一、Handler 簡介
在瞭解Handler 之前,我們需要先了解Handler的繼承關係 繼承關係如下:
java.lang.Object
↳ android.os.Handler
Handler
是 Android
中用來更新UI 的一套消息處理機制。Handler
允許線程間發送Message
或Runnable
對象進行通信。在Android
中UI修改只能通過UI Thread
,子線程不能更新UI
。如果子線程想更新UI
,需要通過 Handler
發送消息給主線程,進而達到更新UI
的目的。
二、Handler 消息處理機制原理
當Android
應用程序創建的時候,系統會給每一個進程提供一個Looper
,Looper
是一個死循環,它內部維護一個消息隊列,Looper
不停的從消息隊列中取Message
,取到的消息就發送給handler
,最後Handler
根據接收的消息去修改UI
等。
三、Handler 機制處理的4個關鍵對象
1.Message
線程之間傳遞的消息,可以攜帶一些簡單的數據供子線程與主線程進行交換數據。
2.Message Queue
存放通過Handler
發送的Message
的消息隊列,每一個線程只有一個消息隊列。
3.Handler
消息處理者,主要用於發送跟處理消息。
主要功能:
發送消息SendMessage()
處理消息 HandleMessage()
4.Looper
內部包含一個死循環的MessageQueue
,用於存儲handler
發送的Message
,Looper
則是不斷的從消息隊列中取消,如果有消息就取出發送給Handler
處理,沒有則阻塞。
5.總結:
Handler
負責發送Message
到Message Queue
,Looper
負責從Message Queue
遍歷Message
,然後直接把遍歷的消息回傳給Handler
自己,通過Handler
自身的handleMessage
處理更新UI
等操作。
主線程、子線程間通信簡單流程
四、 Handler常用方法
1.Runnable對象
-
post(Runnable)
使用方法舉例:
public void BtnRunnableMethod(View view) {
// 1.Runnable 對象
RunnableHandlderMethod();
}
/**
* Runnable 對象更新 UI
* **/
private Handler mRunnableHandler = new Handler();
public void RunnableHandlderMethod() {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
mRunnableHandler.post(new Runnable() {
@Override
public void run() {
((Button) findViewById(R.id.btn_runnable))
.setText("Runnable");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
-
postAtTime(Runnable, long)
-
postDelayed(Runnable, long)
2. Message 對象
-
sendEmptyMessage(int)
使用方法舉例:
public void BtnMessageThreadMethod(View view) {
// 2.Message 對象
new MessageHandlerThreadMethod("子線程不能更新UI").start();
}
/**
* Message 對象舉例
* ***/
private int mCount = 0;
private Handler mMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
}
};
class MessageHandlerThreadMethod extends Thread {
String mString;
public MessageHandlerThreadMethod(String str) {
mString = str;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
mCount++;
mMessageHandler.sendEmptyMessage(0);
}
}
}
-
sendMessage(Message)
使用方法舉例:
public void BtnMessageObjMethod(View view) {
HandlerMessageObjMethods();
}
/***
* handler sendmessage 處理方法
* **/
private Handler mHandlerMessageObj = new Handler() {
@Override
public void handleMessage(Message msg) {
((Button) findViewById(R.id.btn_message)).setText("arg1:"
+ msg.arg1 + "\n" + msg.obj);
}
};
private void HandlerMessageObjMethods() {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
// Message message = new Message();
Message message = mHandlerMessageObj.obtainMessage();
message.arg1 = 100;
Person person = new Person();
person.name = "Lucy";
person.age = 12;
message.obj = person;
mHandlerMessageObj.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
class Person {
public int age;
public String name;
public String toString() {
return "Name=" + name + "\n Age=" + age;
}
}
-
sendMessageAtTime(Message, long),
-
sendMessageDelayed(Message, long)
3.接收、處理Message
-
handleMessage(Message)
使用方法舉例:
private Handler mMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
}
};
五、子線程更新UI 異常處理
子線程不能更新UI
,如果在子線程中更新UI
,會出現CalledFromWrongThreadException
異常。
-
CalledFromWrongThreadException
CalledFromWrongThreadException 子線程不能更新UI
解決方法:
子線程通過Handler
發送消息給主線程,讓主線程處理消息,進而更新UI
。
六、主線程給子線程發送消息的方法
此例子中子線程通過Looper
不斷遍歷主線程發送的消息,Looper
使用方法如下:
1. 準備Looper 輪詢器
Looper.prepare();
2. Handler 處理遍歷消息
Handler mHandler = new Handler()
3. 遍歷消息隊列
Looper.loop();
4.Looper
使用方法如下:
// 自定義 Loop 線程 ---> 不停的處理主線程發的消息
class ChildLooperThread extends Thread {
@Override
public void run() {
// 1.準備成爲loop線程
Looper.prepare();
// 2.處理消息
mMainHandler = new Handler() {
// 處理消息
public void handleMessage(Message msg) {
super.handleMessage(msg);
... ...
}
});
}
};
// 3.Loop循環方法
Looper.loop();
}
}
七、主線程發送消息給子線程的例子
1. 啓動 子線程,並再啓動後發送消息
public void BtnMainMessageMethod(View view) {
// 點擊主線程 按鈕,啓動子線程,並在子線程啓動後發送消息
Message msg = new Message();
msg.obj = "主線程:這是我攜帶的信息";
if (mMainHandler != null) {
// 2.主線程發送消息
mMainHandler.sendMessage(msg);
} else {
Toast.makeText(getApplicationContext(), "開啓子線程輪詢消息,請再次點擊發送消息",
Toast.LENGTH_SHORT).show();
// 1.開啓輪詢線程,不斷等待接收主線成消息
new ChildLooperThread().start();
}
}
2. 子線程啓動,不停的變量主線程發送的消息
private Handler mMainHandler;
String mMainMessage;
// 自定義 Loop 線程 ---> 不停的處理主線程發的消息
class ChildLooperThread extends Thread {
@Override
public void run() {
// 1.準備成爲loop線程
Looper.prepare();
// 2.處理消息
mMainHandler = new Handler() {
// 處理消息
public void handleMessage(Message msg) {
super.handleMessage(msg);
mMainMessage = (String) msg.obj;
Log.i("TAG", "子線程:從主線程中接受的消息爲:\n" + mMainMessage);
// 使用 runOnUiThread 在主線程中更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
((Button) findViewById(R.id.btn_main_message))
.setText(mMainMessage);
}
});
}
};
// 3.Loop循環方法
Looper.loop();
}
}
八、子線程給主線程發送消息的方法
1.子線程發送消息給主線程方法
public void BtnChildMessageMethod(View view) {
new Thread() {
public void run() {
while (mCount < 100) {
mCount++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 利用handler 對象發送消息 Message msg=Message.obtain(); Message
* msg=new Message(); 獲取一個消息對象message
* */
Message msg = Message.obtain();
// 消息標記
msg.what = 1;
// 傳遞整型值msg.obj="傳遞object數據"
msg.arg1 = mCount;
Log.i("TAG", "count 值=" + mCount);
if (mhandler != null) {
mhandler.sendMessage(msg);
}
}
}
}.start();
}
2.主線程接收並處理消息的方法
// 定義一個handler 主線程 接收子線程發來的信息
private Handler mhandler = new Handler() {
// 處理消息的方法
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
int value = msg.arg1;
Log.i("TAG", "value值=" + value);
((Button) findViewById(R.id.btn_child_message)).setText("當前值="
+ value);
break;
default:
break;
}
}
};
九、 主、子 線程 互發消息方法
主要實現主、子線程每隔1s
中通信一次
-
實現打印
Log
如下:
主、子線程通信log信息
-
實現方法如下:
1. 啓動子線程併發送給主線程消息
public void BtnMainChildMessageMethod(View view) {
// 創建 名稱爲currentThread 子線程
HandlerThread mChildThread = new HandlerThread("ChildThread");
mChildThread.start();
mChildHandler = new Handler(mChildThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
Log.i("TAG", "主線程對我說:" + msg.obj);
// 子線程攜帶的消息
Message message = new Message();
message.obj = Thread.currentThread() + "我是子線程,小樣,讓我聽你的沒門";
// 向主線程發送消息
mainhandler.sendMessageDelayed(message, 1000);
}
};
// 主線成發送空消息,開啓通信
mainhandler.sendEmptyMessage(1);
}
2.主線程接收並處理子線程發送的消息
// 創建主線程
private Handler mainhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i("TAG", "子線程對我說:" + msg.obj);
// 主線成攜帶的消息內容
Message message = new Message();
message.obj = Thread.currentThread() + "我是主線程:小子你得聽我的。";
// 向子線程發送消息
mChildHandler.sendMessageDelayed(message, 1000);
}
};
十、子線程方法中調用主線程更新UI的方法
1.Activity 中 可以使用 runOnUiThread(Runnable)
// 使用 runOnUiThread 在主線程中更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
((Button) findViewById(R.id.btn_main_message))
.setText(mMainMessage);
}
});
2.子線程使用 Handler.post(Runnable)
mRunnableHandler.post(new Runnable() {
@Override
public void run() {
((Button) findViewById(R.id.btn_runnable))
.setText("Runnable");
}
});
3.View.post()
((Button) findViewById(R.id.btn_runnable)).post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
((Button) findViewById(R.id.btn_runnable)).setText("View.post()方法使用");
}
});
4.Handler.sendMessage(Message)
public void BtnMainMessageMethod(View view) {
// 點擊主線程 按鈕,啓動子線程,並在子線程啓動後發送消息
Message msg = new Message();
msg.obj = "主線程:這是我攜帶的信息";
if (mMainHandler != null) {
// 2.主線程發送消息
mMainHandler.sendMessage(msg);
}
}
十一、移除Handler 發送的消息方法
1.移除 handler 發送的所有消息
private Handler mChildHandler;
mChildHandler.removeCallbacksAndMessages(null);
2.移除 指定消息
private Handler mainhandler;
mainhandler.removeMessages(what);