Handler、Loop和MessageQueue

来源:互联网 发布:人丑还颜控 知乎 编辑:程序博客网 时间:2024/05/18 02:47

一、Android之线程信使

曾在网上看到一个段子。

面试时,面试官问码农:xxx是线程安全的吗?

码农果断答道:是线程安全的。

面试官:(⊙o⊙)…我正准备接着问为什么不是线程安全的

……

然后就没有然后了。

那么,同样的问题:Android的UI操作是线程安全的吗?

(不要回答的太果断啊)

答案肯定是:不是线程安全的。具体吗?我想这个链接已经说的基本清楚了:http://chenjinhua1595515.blog.163.com/blog/static/21583214720140834012400/

回归正题,Android中有一条规则:只允许UI线程修改Activity里的UI组件,而如果需要在新线程中更新UI线程的组件,则需要通过一个信使:Handler。

Handler类主要有两个作用:

(1)在新线程中发送消息。

(2)在UI线程中获取、处理消息。

好,so easy!完活儿。

二、Handler、Loop和MessageQueue组件

真的这么简单吗?新启动的线程何时发送消息?UI线程何时获取消息?以及消息的传递机制呢?这就有必要了解一下和Handler一起工作的小伙伴啦。

Message:消息(直译,哈哈)。Handler接收和处理的就是它。

Looper:(这个不太好直译)刚开始学的时候,一上来就是东西最难理解,后来发现这个东西就是连接MessageQueue和Handler的中间人,负责把MessageQueue中的Message取出来,送给Handler去处理。需要注意的是:每个线程只有一个Looper。

MessageQueue:消息队列。大家肯定都知道队列的实现机制,不知道也没事,一句话,先进先出。

在线程中使用Handler的步骤有以下三步:

(1)通过Looper的prepare()方法为当前线程创建Looper对象

private Looper() {     mQueue = new MessageQueue();     mRun = true;     mThread = Thread.currentThread();}
可见,同时创建了与之配套的MessageQueue。

(2)创建Handler子类的实例,重写handleMessage()方法,该方法负责处理其他线程的消息。

Handler mHandler = new Hander() {     @Override     public void handleMessage(Message msg) {     if(msg.what == VALUE)     // WHAT YOU WANT TO DO }}
(3)调用Looper的loop方法来启动Looper。loop方法是一个静态方法,网上有它的源代码,是一个死循环不断的取出MessageQueue中的消息,并将其分给该消息对应的Handler处理。代码较长,这里不再贴出。

三、网络编程时的多线程

在Android端的网络编程时(关于网络编程,后续博客中会具体阐述),多线程是必须的编程方法,比如:与服务器进行通信的代码需要在新线程中完成,响应用户操作的代码需要在UI线程中完成……(当然,根据需求,可能这里有非常多的线程,比如多线程下载),这里,仅举例说明网络连接的线程和主线程。

以最简单的登陆服务器为例:

loginButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {final String name = ((EditText) findViewById(R.id.log_in_name_input)).getText().toString();final String password = ((EditText) findViewById(R.id.log_in_password_input)).getText().toString();new Thread() {@Overridepublic void run() {try {HttpPost post = new HttpPost("http://xxx.xxx.xx.xxx:8080/APPLICATION_NAME/login.jsp");List<NameValuePair> params = new ArrayList<NameValuePair>();params.add(new BasicNameValuePair("name", name));params.add(new BasicNameValuePair("pass", password));post.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));HttpResponse response = httpClient.execute(post);if (response.getStatusLine().getStatusCode() == 200) {String msg = EntityUtils.toString(response.getEntity());<strong>Looper.prepare();Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();Looper.loop();</strong>}}catch (Exception e) {e.printStackTrace();}}}.start();}});}
在监听登陆按钮时,需要新启动一个线程,进行与服务器的连接,在监听到服务器成功返回响应之后,首先,通过response.getEntity()方法取得返回的内容,然后通过prepare()方法创建Looper对象,最后调用loop()方法启动Looper。


0 0