为什么Android只能在UI线程更新UI?
- 解决多线程并发问题
- 提高界面更新的性能
- 架构设计简单
Handler消息模型
Looper类
主要成员变量和方法:
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
volatile boolean mRun;
private Printer mLogging;
Looper:prepare()
每个线程只能有一个Looper类的实例对象,Looper类的实例必须通过prepare()方法创建,保存在sThreadLocal中。
一个线程多次调用prepare()方法会抛出异常。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Looper:loop()
loop()方法:静态方法,分发消息队列中的消息。其中有一个无限for循环,取出消息队列中的消息,分发消息。
/**
* 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) {
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.recycle();
}
}
Handler类
- 构造Handler对象,需要两个参数:线程的Looper对象、消息的处理函数。若不指定Looper对象,会使用当前线程的Looper对象。消息的处理函数不是必须的。
简单的Handler代码
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
button = (Button) findViewById(R.id.button1);
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
textView.setText("单击,过了5s");
}
if (msg.what == 2) {
textView.setText("长按,过了5s");
}
}
};
// button单击,发送1
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new Thread() {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
};
}.start();
}
});
// button长按,发送2
button.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
new Thread() {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(2);
};
}.start();
// 返回true,不会继续触发click
return true;
}
});
}
}
主线程向子线程发送消息
我们常用的,都是子线程向主线程发送消息。那么,我们能够从主线程向子线程中发送消息么?
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyThread myThread = new MyThread();
myThread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.handler.sendEmptyMessage(2);
}
}
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
public class MyThread extends Thread{
Handler handler;
@Override
public void run() {
Looper.prepare();
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
System.out.println("主线程向子线程发送了消息:1");
break;
case 2:
System.out.println("主线程向子线程发送了消息:2");
break;
default:
break;
}
}
};
Looper.loop();
}
}