Android:在子线程中更新UI,解析异步消息处理机制(Handler)

这篇文章是通过阅读“第一行代码”这个书,学习总结的,整理出自己的想法和借鉴作者的思想来充分理解:Android中的异步消息处理机制。

众所周知,Android的UI也是线程不安全的。也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。

我们举一个形象的例子来说明这个问题:

功能实现:当我们点击Button按钮时,用于改变TextView中显示的内容。

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="点击修改文本内容" />
    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是菜鸟" />
</LinearLayout>
MainActivity.java

package com.xbmu.handler;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {

	private Button button;
	private TextView textView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button = (Button) findViewById(R.id.btn);
		textView = (TextView) findViewById(R.id.text);
		button.setOnClickListener(this);
	}
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btn:
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					textView.setText("我是一匹黑马");
					
				}
			}).start();
			break;
		}
		
	}
}
以上程序,我们可以看到在button按钮中我们设置了点击事件,在点击事件里面开启了一个子线程,然后在子线程中调用TextView的setText()方法将显示的字符串改为“我是一匹黑马”。说白了就是在子线程中更新了UI组件。

运行程序:

会发现程序直接崩溃了。并且查看log cat日志,发现报错信息:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

大概意思就是:只能在原生线程(也就是主线程)中创建一个视图或者触摸(更改)这个视图


由此可见,以上的例子充分证明了:Android确实是不允许在子线程中进行UI操作的。但是有时候,我们在Android项目开发过程中,有时候需要进行一些耗时的操作(比如:通过联网,请求网络服务,从后台获取一些数据),然后将获取到的数据用来更新相应的UI控件,这该怎么办啊?

对于这种问题, google工程师,在Android底层提供了一套异步消息处理机制,完美的解决了在子线程中进行UI的操作。

修改MainActivity.java

package com.xbmu.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {

	protected static final int UPDATE_TEXT = 1;
	private Button button;
	private TextView textView;
	//创建handler对象,并重写父类handleMessage方法
	private Handler handler = new Handler(){
		//处理传递过来的消息
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case UPDATE_TEXT:
				//在这里更新UI操作
				textView.setText("我是一匹黑马");
				break;
			}
		};
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button = (Button) findViewById(R.id.btn);
		textView = (TextView) findViewById(R.id.text);
		button.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btn:
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					Message message = new Message();
					message.what = UPDATE_TEXT;
					handler.sendMessage(message);//将message对象发送出去
				}
			}).start();
			break;
		}
		
	}
}

运行成功:



这里只是简单的总结了一下我们在项目开发过程中经常遇到的问题及其解决方法,学会使用“异步消息处理机制”来在子线程中进行UI操作。

但是,我们不能仅满足于这些,学习一门技术,必须学会总结,归纳,深究其原理,我们才能掌握这门技术并能在适应的场景最后那个应用。

这里我们来介绍下“异步消息处理机制”,有什么不妥之处,请各位大神提出宝贵意见,毕竟我只是一个菜鸟,这是记录下自己学习的笔记,

希望能得到大家的指教与认可。

Android中的异步消息处理主要有四个部分组成:Message,Handler,MessageQueue,Looper。

Message:顾名思义,是消息的意思,主要作用在线程之间传递的消息,充当媒介作用,它可以在其内部携带少量的信息,

用于在不同线程之间交换数据,,比如:上面那个例子中就是通过Message对象携带数据在子线程和主线程之间传递消息。

Handler:顾名思义也就是处理者的意思,你想,我们通过Message将数据携带过来,肯定要将该数据进行处理,如果不打算处理,那我们

传递过来干什么啊。 handler主要用于发送和处理消息。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列

的处理后,最终会传递到handler的handlerMessage()方法中。

MessageQueue:是消息队列的意思。它主要用于存放所有通过handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中

只会右一个MessageQueue对象

Looper:是每个线程中的MessageQueue的管家,通过Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一个消息,

就会将它取出,并传递到Handler的handlerMessage()方法中。每个线程只有一个Looper对象。


我们整理梳理一下异步消息处理机制运行的流程:

首先需要在主线程当中创建一个Handler对象,并重写handleMessage()方法。然后当子线程中需要进行UI操作的时,就创建一个Message对象,并通过

Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MesssageQueue中取出待处理消息,

最后发回Handler的HandleMessage()方法中。

由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码页会在主线程中运行,于是我们在这里就可以进行UI操作了。




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上善若水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值