Handler Android应用开发消息机制

一、背景

当应用程序启动时,Android首先会开启一个主线程(即UI线程),主线程为管理界面中的UI控件、进行事件分发,比如:你要点击一个Button,Android会分发事件到Button上来响应你的操作。如果此时需要一个耗时操作,例如:联网读取数据或读取本地一个较大文本时,你不能把这些操作放在主线程中。如果你放在主线程中,界面会出现假死现象,如果5s后还没有完成的话则会收到Android系统的一个错误提示“强制关闭”。此时,我们则要把这些耗时的操作放在一个子线程中,因此此子线程涉及到了UI更新,而更新UI只能在主线程中更新,这时我们就需要引入Handler来实现该操作。它与子线程可以通过Message对象来传递消息,这个时候Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)的Message对象(里面包含数据且该消息对象会放在主线程的消息队列MQ中),然后在主线程中处理这些消息从而可以对UI控件进行更新等操作。

二、Android的消息处理有三个核心类:Looper、Handler和Message

1、Looper

Looper使一个普通的线程变成Looper线程。Looper线程就是循环工作的线程,创建looper线程代码如下:

public class MyLoopThread extends Thread {

		@Override
		public void run() {
			Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue

			//.....其他处理,如实例化Handler等
			Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理

		}
	}

效果图如下:

              

注:一个线程(Thread)最多有且只能有一个Looper对象,该Looper对象内部包含了一个消息队列MessageQueue

由上面代码可以知道Looper对象的prepare()方法将会初始化当前线程为looper线程,并且实例化Looper和初始化消息队列;

而Looper的loop()方法则是循环取消息,并分发到对应的hangdler处理消息,原理如下图:


Looper类除了prepare()和loop()方法外,还有其他的方法,如下:

Looper.myLooper()    :得到当前线程的Looper对象;

getThread()       :得到Looper对象所属的线程

quit()       :结束looper循环

2、Handler消息处理(异步处理处理)

主要接受子线程发送的消息,并用此数据配合主线程更新UI

解释:Handler扮演了往MQ上添加消息和处理消息(只处理自己发出的消息)的角色,即:通知MQ要要执行一个任务(sendMessage),并在looper.loop()方法关联到此Handler时执行任务(handlerMessage())然后Handler处理消息,整个过程是异步执行的。

注:因为Handler创建时会关联一个looper,默认的构造方法将关联到当前线程的looper,所以上面说的:“looper.loop()方法关联到此Handler”可以理解为handler创建时关联的looper是执行looper.loop()方法的looper线程的looper。所以,Handler关联的looper是可以设置的,可以为子looper线程的looper也可以是主线程(UI线程——也是一个looper线程)的looper,Handler关联的looper是哪个线程的looper它就会在那个线程执行(即:Handler的执行线程只与它关联的looper有关,与它实例化的位置无关)。Handler的实例化代码如下:

public class MyAndroidStudyMainActivity extends Activity {
	private Handler mHandler;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_my_android_study_main);
		
		
		mHandler = new Handler(getMainLooper(), new Callback() {//实例化Handler并关联主线程的looper,当new Callback()
											//前面没有参数时实例化的Handler关联的则是当前所处线程的looper				
			@Override
			public boolean handleMessage(Message msg) {
				
				//.......处理消息(耗时操作)
				
				return true;
			}
		});
	}
}
将Handler加入到looperThread线程类中时,则可以异步处理消息了(如:在子线程中实例化Handler时关联UI线程的looper,我们在子线程中联网取数据后把数据发送给Handler,然后Handler处理消息就可以更新UI界面了)。结构代码如下:

public class MyLoopThread extends Thread {
		private Handler mHandler1;
		private Handler mHandler2;
		@Override
		public void run() {
			Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue
			//实例化两个Handler
			mHandler1 = new Handler();
			mHandler2 = new Handler();
			Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理

		}
	}
效果图如下:


由效果图可见:一个线程最多有且只能有一个looper,但可以有多个Handler。
上面说的在子线程更新UI界面则可以实现了,结构代码如下:

public class MyLoopThread extends Thread {
		private Handler mHandler1;
		@Override
		public void run() {
			Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue
			//实例化Handler
			mHandler1 = new Handler(getMainLooper(),new Callback() {
				
				@Override
				public boolean handleMessage(Message msg) {
					
					//......更新UI界面					
					
					return true;
				}
			});
			Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理

		}
	}
【例】如下图所示,要在子线程中把界面上TextView控件的内容修改


XML文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_dark"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/message_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="我是启动界面"
        android:textColor="@android:color/white"
        android:textSize="24sp" />

    <Button
        android:id="@+id/send_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送消息到主线程执行" />

</LinearLayout>

Java代码:

public class HandlerActivity extends Activity {
	private MyLoopThread myLooper;//定义子线程
	private TextView mMessageTxt;//定义TextView
	private Button mSendMsgBtn;//定义Button
	private int count = 1; // 标识消息

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
		setContentView(R.layout.activity_handler_layout);
		mMessageTxt = (TextView) findViewById(R.id.message_txt);
		mSendMsgBtn = (Button) findViewById(R.id.send_btn);
		myLooper = new MyLoopThread();//实例子线程
		myLooper.start();//启动子线程

		mSendMsgBtn.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Message msg = Message.obtain();
				msg.what = 11;
				msg.arg1 = count++;
				myLooper.mHandler1.sendMessage(msg);//Handler发送消息
			}
		});
	}
	/**
	 * 消息在主线程执行的情况 消息在子线程执行的情况
	 * 
	 */
	public class MyLoopThread extends Thread {
		public Handler mHandler1;

		@Override
		public void run() {
			Looper.prepare();  //实例Looper,初始化消息队列MessageQueue
			mHandler1 = new Handler(getMainLooper(), new Callback() {
				@Override
				public boolean handleMessage(Message msg) {//Handler处理消息,更新UI
					int arg1 = msg.arg1;
					mMessageTxt.setText("主线程执行 :  消息 " + arg1);
					return true;
				}
			});
			Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理
		}
	}
}
运行程序后如下图:



我们可以看见,当点击Button后TextView的内容已经被修改了,所以,Handler能够实现在子线程中对UI界面的更新操作。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值