线程的基本用法
Android多线程编程并不比Java多线程编程特殊,基本都是使用相同的语法。
那么如何创建一个线程呢?下面有几种方法可以使用:
- 定义一个类继承自Thread,重写run()方法,在其中编写耗时逻辑
class Thread1 extends Thread{
@Override
public void run() {
//处理具体逻辑
}
}
最后使用其start()方法就可以开启这个线程
new Thread1().start();
- 使用Runnable接口的方式来定义一个线程
class Thread2 implements Runnable{
@Override
public void run() {
//处理具体逻辑
}
}
Thread的构造方法中传入了实现了Runnable接口的Thread2对象,然后调用Thread的start()方法
Thread2 thread2 = new Thread2();
new Thread(thread2).start();
- 当然更多的时候我们使用匿名类的方式
new Thread(new Runnable() {
@Override
public void run() {
//处理具体逻辑
}
}).start();
异步消息处理机制
因为Android是线程不安全的,也就是说更新UI只能在主线程中进行。Android提供了一套异步消息处理机制,很好的解决了这个问题,先看一下是如何写的:
- 先新建一个项目,修改activity_main.xml中的代码
- 定义了两个控件,TextView用于显示字符,Button用于改变TextView中显示的内容。点击Button后可以将TextView中的字符串更改成“这是更新过的UI界面”。
修改MainActivity中的代码
public class MainActivity extends Activity implements View.OnClickListener {
private TextView textView;
private Button button;
public static final int UPDATE_UI = 1;
private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UPDATE_UI :
textView.setText("这是更新过的UI界面");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text_view);
button = (Button) findViewById(R.id.update_ui);
textView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.update_ui:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_UI;
myHandler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
}
点击了button之后,在子线程中创建了一个Message对象,并将一个整型常值UPDATE_UI 赋值给Message.what,通过Handler的sendMessage()方法把Message对象发送出去。在主线程中的Handler收到这个Message对象,并在handleMessage()中处理这条Message。对Message.what值进行判断,如果相符,就更新UI。
这样,异步处理的基本用法就掌握了,接下来分析一下Android异步消息处理机制是如何工作的。
分析异步消息处理机制
Android异步消息处理主要是由四个部分组成,Message,Handler,MessageQueue和Looper。那么,MessageQueue和Looper是什么呢?接下来分析一下源码,之后就会对这四个部分的功能有了基本的了解。
- 创建Message对象,并将整型常值UPDATE_UI传入Message.what,之后调用Handler的sendMessage()方法将消息发送出去。查看sendMessage()的源码,最终会调用到以下方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
- sendMessageAtTime()方法传入两个值,第一个就是我们传入的Message对象,第二个是延迟时间。然后创造了MessageQueue对象,并调用enqueueMessage()方法。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
- 该方法将传入的Message对象按照时间排序。那么现在很明显了,MessageQueue是消息队列,主要用于存放Handler发送的消息,这部分消息会一直存在消息队列中,每个线程中只有一个MessageQueue。
那么,有存消息,当然也有处理消息的,那就是Looper,他从MessageQueue中取出Message对象,并对其处理。代码如下:
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();
}
}
- 调用queue.next()方法,拿出了最新的Message对象,之后调用msg.target.dispatchMessage()方法,那么msg.target是什么,查看代码发现msg.target是Handler类,之后调用Handler的dispatchMessage()方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
到这里,发现了熟悉的方法,handleMessage()方法,这个方法也就是之前处理Message,更新UI的方法。
那么现在Android异步消息处理的流程就是如下:
- 创建一个Message对象,通过Handler将消息发送出去
- 这条消息存放在MessageQueue中等待被处理
- Looper从MessageQueue中取出待处理的消息,分发回Handler的handleMessage()方法中。
参考资料:http://blog.csdn.net/guolin_blog/article/details/9991569 郭神的博客
《第一行代码》 郭神的书
PS1:Looper源码是很棒的单例设计模式
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
PS2:写博客好难啊,即使我基本上是实现郭神博客和书上的内容。这篇很大程度上借鉴了郭神的内容,无法称原创。很感谢郭神,stormzhang等大神写的原创博客和书,让我收获很多。