- 当主线程(UI线程或ActivityThread)诞生时,就会去执行一个代码循环(Looper),以便持续监视它的信息队列(Message Queue简称MQ)。当UI事件发生了,通常会立即丢一个信息 (Message)到MQ,此时主线程就立即从MQ里面取出该信息,并且处理。
- 例如,用户在UI画面上按下一个Button按 钮时,UI事件发生了,就会把onClick信息丢到 MQ里,于是,主线程会及时从MQ里取出onClick信息,然后调用Activity的onClick()函数去处理之。处理完毕之后,主线程又返回去继续执行信息循环,继续监视它的MQ,一直循环下去,直到主线程的生命周期的终了。 通常是进程被删除时,主线程才会被删除。
- 即一个主线程有它自己专属的Looper对象,此线程诞生时,就会执行此对象里的信息循环。此外,一个主线程还会有其专属的MQ信息结构。如下图所示:
- 由于主线程会持续监视MQ的动态,所以在程序的任何函数,只要将信息(以Message 类别的对象表示)丢入主线程的MQ里, 就能与主线程沟通了。
- 在Android里,定义了一个Handler类别,在程序的任何函数里,可以诞生Handler对象来将Message对象丢入MQ里,与主线程进行沟通。 Handler创建地时候会采用当前线程地Looper来构造消息循环系统,Handler内部使用ThreadLocal(可以在不同地线程中互不干扰地存储并提供数据)来获取当前线程地Looper。
- Handler创建完毕后,这个时候其内部地Looper以及MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理。其实post方法最终也是通过send方法来完成的。当Handler的send方法被调用时,它会调用MQ的enqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handleMessage方法就会被调用。注意,Looper是运行在创建Handler所在的线程中的,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中去执行了。
- 在Android的预设情况下,主线程诞生时,就会拥有自己的Looper对象和MQ(即 Message Queue)数据结构。
- 然而,主线程诞生子线程时,于预设情形 下,子线程并不具有自己的Looper对象和 MQ。由于没有Looper对象,就没有信息回圈(Message Loop),一旦工作完毕了,此子线程就结束了。
- 既然没有Looper对象也没有MQ,也就不能接受外来的Message对象了。则别的线 程就无法透过MQ来传递信息给它了。如果别的线程(如主线程)需要与子线程通讯时,就得要手动替它诞生一 个Looper对象和一个MQ。
Demo展示:
- 主线程丢信息给自己
package com.example.aaa;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private Button btn1,btn2;
private TextView textView;
private Handler h=new Handler(){
public void handleMessage(Message msg) {
if(msg.what==1)
textView.setText((String)msg.obj);
};
};;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
btn1=(Button) findViewById(R.id.btn1);
btn2=(Button) findViewById(R.id.btn2);
textView=(TextView) findViewById(R.id.textview);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.btn1:
h.removeMessages(1);
Message m=h.obtainMessage(1, "this is my message");
h.sendMessage(m);
break;
case R.id.btn2:
this.finish();
default:
break;
}
}
}
运行结果:
- 子线程丢信息给主线程
package com.example.aaa;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private Button btn1,btn2;
private TextView textView;
private Timer timer=new Timer();
private int k=0;
private Handler h=new Handler(){
public void handleMessage(Message msg) {
if(msg.what==1)
textView.setText((String)msg.obj);
};
};;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
btn1=(Button) findViewById(R.id.btn1);
btn2=(Button) findViewById(R.id.btn2);
textView=(TextView) findViewById(R.id.textview);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.btn1:
TimerTask timerTask=new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
h.removeMessages(1);
Message m=h.obtainMessage(1, Thread.currentThread().getName()+":"+String.valueOf(k++));
h.sendMessage(m);
}
};
timer.schedule(timerTask, 500, 1500);
break;
case R.id.btn2:
this.finish();
default:
break;
}
}
}
运行结果:
- 替子线程诞生Looper和MQ
package com.example.aaa;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private Button btn1,btn2,btn3;
private TextView textView;
private Thread t;
private Handler h;
private String str;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
btn1=(Button) findViewById(R.id.btn1);
btn2=(Button) findViewById(R.id.btn2);
btn3=(Button) findViewById(R.id.btn3);
textView=(TextView) findViewById(R.id.textview);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
t=new Thread(new Task());
t.start();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.btn1:
h.removeMessages(1);
Message m=h.obtainMessage(1, Thread.currentThread().getName()+":"+String.valueOf(k++));
h.sendMessage(m);
break;
case R.id.btn2:
btn2.setText(str);
break;
case R.id.btn3:
h.getLooper().quit();
finish();
break;
}
}
class Task implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
h=new Handler(){
public void handleMessage(Message msg) {
if(msg.what==1)
str=Thread.currentThread().getName()+",value="+String.valueOf(msg.what);
};
};
Looper.loop();
}
}
}
运行结果:
MQ的工作原理
- MQ主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往MQ中插入一条消息,而next的作用是从MQ中取出一条消息并将其从MQ中移除。MQ内部实现是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。
- next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。
Looper的工作原理
- Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MQ中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。在构造方法中它会创建一个MQ,然后将当前线程地对象保存起来。
- Looper除了prepare方法外,还提供了prepareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的,其本质也是通过prepare方法来实现的。由于主线程的Looper比较特殊,所以Looper提供了一个getMainLooper方法,通过它可以在任何地方获取到主线程的Looper。Looper也是可以退出的,Looper提供了quit和quitSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待状态,而如果退出Looper以后,这个线程就会立刻终止,因此建以不需要的时候终止Looper。
- Looper的loop方法是一个死循环,唯一跳出循环的方式是MQ的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MQ的quit或quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。也就是说,Looper必须退出。否则loop方法就会无限循环下去。loop方法会调用MQ的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MQ的next方法返回了新消息,Looper就会处理这条消息:msg.target,dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到指定地线程中去执行了。