参考:http://blog.csdn.net/lmj623565791/article/details/24015867
1、请解释下在单线程模型中Message,Handler,Message Queue,Looper之间的关系。
个人理解:关于这四个单词,在学习第一行代码中的关于多线程操作UI时粗略的了解过,因UI操作是线性不安全的,所以使用hanlder机制,但是对于hanlder是本质是什么并没有了解,我只能猜测为是安卓提供的一个可以解决多线程的不安全的一种技术手段。而Message是信息的意思,也就是说可以在线程之间进行传递的内容,而Message Quenue可以看着是多个信息的缓冲队列,而Looper从信息缓存队列中是逐一取出信息的对象,而hander本身是提供方法对信息进行解析处理。似乎每一个线程都有一个独立的Message Quenue和Looper、handler,但是关于以上不正确的理解都是在多线程的前提,这里强调了是单线程,不知道与多线程有何区别。
标准答案:拿主线程来说,主线程启动时会调用Looper.prepare()方法,会初始化一个Looper,放入Threadlocal中,接着调Looper.loop()不断遍历Message Queue,Handler的创建依赖与当前线程中的Looper,如果当前线程没有Looper则必须调用Looper.prepare()。Handler , sendMessage到MessageQueue,Looper不断从MessageQueue中取出消息,回调handleMessage方法。
二次理解:以标准答案为基础,给我提供的信息,线程启动的是会自动创建一个Looper对象,并与当前线程是绑定的(一一对应的关系)
然后Looper对象会自动遍历当前线程的对象的Message Queue,然后就是Handler是依赖的于Looper对象的存在,这里所谓的依赖应该是
指Handler对象的操作是跟Looper的循环是同步的,即调用Handler对象的handleMessage操作Message时,是一个循环过程。流程大致是这样:首先当前线程自动创建Looper对象,然后我们在程序中可以创建自己的Handler、Message对象,这是调用Handler的sendMessage方法把Message对象发送给所有线程的MessageQueue(问题一:MessageQueue是什么时候创建的),接着该线程所对应
的Looper对象自动从Message取出Message,然后回调我们程序中创建的Handler对象的handleMessage方法(问题二:线程中Handler对象是怎么绑定到该线程的,只要在该线程中去创建这个Handler对象就可以在让该线程去自动回调该Handler的方法嘛)。
代码示例:以我对一行代码中示例的学习回忆,尝试结合以上的理解和问题,创建一个自己的测试代码。
UIActivity.java:
package com.noodles.uipractice;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class UIActivity extends AppCompatActivity {
private TextView tv;
/**
* 这里是主线程,执行到这里肯定已经创建了该线程,所以也应该创建了Looper对象。
*/
/**
* 因为线程对应Handler对象不会自动创建,因为Handler是对Message进行操作的对象,一般需要完成一些
* 程序所需的自主操作,所以由程序员去创建,然后程序通过自动调用该对象方法方式的去执行操作(并没有写回调,
* 因为我认为很多专业术语都是你在读取很多专业书籍后,加深理解才可以更合理和有益的使用,菜鸟我还是放慢脚步好了)。
*
* 1、在该线程中创建一个Handler子类对象,并重写HandleMessage方法自定义对接收到Message的对象的处理。
*/
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//5、因为Message Queue和Looper关系,后台其实是循环的调用handleMessage方法,所以加入swith case判断
switch (msg.what){
case 0:
tv = (TextView) findViewById(R.id.tv);
tv.setText((CharSequence) msg.obj);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ui);
/**
* 2、获取一个按钮,并且定义事件中创建一个新的线程去发送一段文本
*/
findViewById(R.id.send_text).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建一个新的线程
new Thread(
new Runnable() {
@Override
public void run() {
/**
* 3、在该线程中,创建一个Message对象
*/
Message msg = new Message();
//添加一个标识,这样在Message Queue中可以变成唯一。
msg.what = 0 ;
//添加内容,比如一段文本。obj变量可以接受一个object类型的对象,所以简单的数据形式都可以通过它装载。
msg.obj = "来自另外一个线程的内容";
/*
* 调用Handler对象的sendMessage来发送,这样当前程序的所有线程中的Message Queue中都会收到这个Message对象
* 至于Message Queue什么时候创建的暂时没有阅读到想关文档.
*/
/**
* 4、调用创建Handler对象sendMessage方法Message对象
*/
handler.sendMessage(msg);
/*
* 在该线程中可以调用主线程中创建的Handler对象,说明hanlder并没有与线程绑定的关系,是可以共享的。
* 不过这是确实线程与进程的区别之一。即进程是完全独立的,不可以进行数据共享,而线程可以是。只是
* 线程共享数据存在不安全性,得出不安全性即导致程序不按逻辑或者个人意愿的结果。
*/
}
}
).start();
}
});
}
}
activity_ui.xml:
点击后
总结:Handler是Android提供的一种多线程的通讯机制,该机制的原理是Handler类有两个方法sendMessage和handleMessage,通过方法名就知道,就是对Message对象(可以看做是数据的载体)操作,另外所有线程启动时会自动创建一个Message Queue和Looper(有的文章是说在创建Handler时创建),前者是消息队列用来存放可以存在的多个Message对象,而Looper对象,即循环,自动遍历每一个Message,同时回传给Handler对象handleMessage方法。其中如果调用某一个Handler的sendMessage方法,程序自动回调同一个Handler对象handleMessage方法。
参考内容:
见:第一行代码:第9章 后台默默的劳动者,探究服务,9.2.3 解析异步消息处理机制
见:http://blog.csdn.net/lmj623565791/article/details/38377229
见:http://blog.csdn.net/luoshengyang/article/details/6817933/
错误1:并不是每一天线程启动时自动创建一个Message Queue和Looper,只有主线程是自动创建的。
错误2:Handler的sendMessage并不会向所有线程发送Message信息,只会向创建该Handler对象的线程的发送。
吐槽:对于没有太多计算机基础的我来说,大多是知其然不知其所以然,所有总是有些不自信,门外汉的感觉。另外对我自己来说,不给自己太大压力,实在不行就休息,承认自己的不足和能力有限,反而可以提高学习的效率,人生已经如此艰难,试着理解自己。