Android UI是線程不安全的,如果在子線程中嘗試進行UI操作,程序就有可能會崩潰。解決這一問題,即創建一個Message對象,然後藉助Handler發送出去,之後在Handler的handleMessage()方法中獲得剛纔發送的Message對象,然後在這裏進行UI操作就不會再出現崩潰了。這種處理方式被稱爲異步消息處理線程。
一.Handler的基本使用 在主線程定義Handler
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private TextView texttitle = null;
/**
* 在主線程中定義Handler,並實現對應的handleMessage方法
*/
public static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//接收消息
if (msg.what == 101) {
texttitle.setText("接收到handler消息...");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
texttitle = (TextView) findViewById(R.id.texttitle);
texttitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
// 在子線程中發送異步消息 通知主線程更新UI
mHandler.sendEmptyMessage(101);
}
}.start();
}
});
}
}
二.在子線程中定義Handler
主要用於進程之間通訊
2.1 直接在子線程第一handler會報錯誤
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//每一個線程只能調用一次
mHandler = new Handler() {
public void handleMessage(Message msg) {
Log.i(TAG, "在子線程中定義Handler,並接收到消息。。。");
super.handleMessage(msg);
System.out.println("這個消息是從-->>" + msg.obj+"過來的,在第二個子線程當中" );
}
}}
Looper.loop();//開始輪循
}).start();
在子線程中創建的Handler是會導致程序崩潰的,提示的錯誤信息爲 Can't create handler inside thread that has not called Looper.prepare() 。說是不能在沒有調用Looper.prepare() 的線程中創建Handler,那麼可以理解當我們在new Handler的時候還需要有個Looper,
2.2查看Handler代碼
- public Handler() {
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- mLooper = Looper.myLooper(); //從Looper.myLooper方法中取個looper對象
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()"); //如果爲空則報異常
- }
- mQueue = mLooper.mQueue;
- mCallback = null;
- }
查看Looper.myLooper(); 代碼
public static final Looper myLooper() { return (Looper)sThreadLocal.get(); } //從sThreadLocal中得到looper並返回與下邊Looper.perpare中sThreadLocal.set方法想吻合查看perpare方法代碼可看到,自動創建一個Looper
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
可以看到,首先判斷sThreadLocal中是否已經存在Looper了,如果還沒有則創建一個新的Looper設置進去。這樣也就完全解釋了爲什麼我們要先調用Looper.prepare()方法,才能創建Handler對象。同時也可以看出每個線程中最多隻會有一個Looper對象。
再創建第二個子線程
new Thread(new Runnable() {
@Override
public void run() {
Looper loop = Looper.myLooper();
Message msg = childHandler.obtainMessage();
msg.obj = "第二個線程信息";
mHandler.sendMessage(msg);
}
}).start();
這樣一個線程發送消息,一個線程接收消息 完成線程之間的通訊,但是在主線程中定義Handler會默認在程序啓動的時候,系統已經幫我們自動調用了Looper.prepare()方法;
只有在子線程定義Handler需要prepare
三.其他異步更新UI方法
1.Activity中的runOnUiThread()方法,代碼如下所示:
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action); //不爲主線程時候
- } else {
- action.run();
- }
- }
2.
然後再來看一下View中的post()方法,代碼如下所示:
- Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
- } else {
- ViewRoot.getRunQueue().post(action);
- return true;
- }
- return handler.post(action); //同樣會調用handler.post();
- }
查看Handler中的post()方法代碼:
- return sendMessageDelayed(getPostMessage(r), 0);
- }
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
這裏調用了sendMessageDelayed()方法去發送一條消息,並且還使用了getPostMessage()方法將Runnable對象轉換成了一條消息,看下這個方法的源碼:
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在這個方法中將消息的callback字段的值指定爲傳入的Runnable對象。callback在Handler的dispatchMessage()方法中原來有做一個檢查,
通過追蹤可以知道就是我們定義的Handler對象,然後我們查看一下Handler類的dispatchMessage方法:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果Message的callback等於null纔會去調用handleMessage()方法,否則就調用handleCallback()方法。那我們快來看下handleCallback()方法中的代碼吧:
message.callback.run(); //調用我們run方法
}
上邊說了getPostMessage(Runnable r)方法中將我們自己的Runnble給了m.callback = r;message.callback相當於我們自己的Runnble 此時調用到了run方法。
自己的一些簡單理解,諸位見笑