三剑客各自的绝招
Handler在消息传递机制里扮演着很重要的角色,它是用来发送消息与处理消息的。
MessageQueue则是用来存储Message信息的队列,它采用的是先进先出的方式。
Looper则用来负责读取MessageQueue中的Message消息的,在读到消息后再将消息改善给该消息的Handler进行处理。
三剑客的关系及合作的方式
首先我们来看看Looper的构造器代码
private Looper
{
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
从上面我们可以看到当我们新建初始化一个Looper的时候,默认就会帮我们加上一个与之对应的MessageQueue,想想也是,如果没有对应的消息队列,那么Looper还怎么读取得到信息呢?
再进一步,Handler是用来发送消息的,那么消息一旦发送出去,就像信封发出去的时候,你需要一个信箱吧,那么android里面的消息传递机制里面信箱的角色是由谁来扮演的呢?这个MessageQueue就当仁不让地承担下这个重任了。
既然Handler离不开MessageQueue,而MessageQueue又是由Looper生成,那么可以说Handler也就跟Looper有着难舍难分的关系了。由此可以看出,它们三者合而生,分则完。只有三者三剑合璧,才能将消息传递的任务给做好。
或者有人会问,那么为什么有时在主线程里面看不到Looper与MessageQueue的身影呢,只要定义Handler也可以发送消息与处理消息?其实这个是因为主线程里面已经提前给我们定义了一个Looper对象,也就有了相应的MessageQueue。这样,我们当然可以只定义Handler这一个了。但如果我们要想让自己定义的线程也加入到整个消息传递里面来,毫无疑问,我们需要自己一个Looper对象,才能正常地发送并处理消息。
接下来就让我们来看看一个自定义的线程的处理的信息。
//定义一个自定义的需要改变UI界面的线程,
class MyThread extends Thread{
public Handler mHandler;
public void run(){
//调用looper的prepare()可以进行Looper的初始化
Looper.prepare();
mHandler = new Handler(){
//定义处理消息的方法
@Override
public void handleMessage(Message msg){
if(msg.what == 0x123){
//在此处可以定义自己要执行的代码,一般为请求网络数据或者计算量大的计算
Message mainMessage = new Message();
//都可以将message的what赋值为0x123,并不会互相干扰,因为它们隶属不同的messageQueue,因为它们由不同的handler发送。handler发送消息到指定的message里面。这里是将参数发回主线程去修改UI界面
mainMessage.what = 0x123;
Bundle mainBundle = new Bundle();
//可以在message里面放许多的数据,比如ArrayList,借用bundle
mainBundle.putIntegerArrayList("nums",(ArrayList)nums);
mainMessage.setData(mainBundle);
MainActivity.this.mainHandler.sendMessage(mainMessage);
}
}
};
//Looper.loop()执行后,Looper开始不断循环从MessageQueue读取消息。
Looper.loop();
}
}
myThread = new MyThread();
//启动新线程,当线程启动后,线程里面的消息循环也就开始运行,并时刻接受来自于myThread.handler发送来的消息。
listPrimeThread.start();
Message msg = new Message();
msg.what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt(UPPER_NUM,
Integer.parseInt(et.getText().toString()));
msg.setData(bundle);
//向自定义的线程中的Handler发送消息
myThread.mHandler.sendMessage(msg);
我们需要定义一个Handler来接受来自自定义线程发来的修改UI界面的消息。必须清楚的是主线程与自定义线程是不同的线程,它们各自拥有一套消息传递机制。
Handler mainHandler = new Handler(){
public void handleMessage(Message msg){
//msg.what赋值为一样的值不会有所干扰。
if(msg.what == 0x123){
et = (EditText) findViewById(R.id.et);
ArrayList<Integer>nums = msg.getData().getIntegerArrayList("nums");
et.setText(nums.toString());
}
}
};
顺便提一提,如果我们直接在刚才自定义的myThread里面通过下面语句修改UI界面,虽然编译通过,但执行时会报一个错误。如图所示,这意味着你在除主线程外修改了UI界面,这个是android里面不允许的。所以有关UI界面的修改都要返回到主线程去修改。
EditText et = (EditText) MainActivity.this.findViewById(R.id.et);
et.setText(nums.toString());