android学习之——Handler消息传递机制

Android的消息传递机制是另一种形式的“事件处理”,这种机制主要是为了解决Android应用的多线程问题——Android平台只允许UI线程修改Activity里的组件,这样就会导致新启动的线程无法动态改变界面组件的属性值。但实际开发中,需要让新的线程能够改变界面组件的属性值,这就需要借助于Handler的消息传递机制来实现了。


Handler类简介:

Handler类主要作用:

  • 在新启动的线程中发送消息
  • 在主线程中获取、处理消息

通过回调的方法——重写Handler类中处理消息的方法来让主线程能适时地出口i新启动线程所发送的消息。,当新启动的线程发送消息时,消息会发送到与之关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息——这将导致Handler类中处理消息的方法被回调。

Handler类中包含如下用于发送、处理消息的方法:

  • void handleMessage(Message msg):处理消息的方法。通常被重写。
  • final boolean hasMessages(int what):检查消息队列中是否包含what属性为指定值的消息。
  • final boolean hasMessages(int what,Object object):检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息。
  • 多个重载的Message obtainMessage():获取消息。
  • sendEmptyMessage(int what):发送空消息。
  • final boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒后发送空消息。
  • final boolean sendMessage(Message msg ):立即发送消息。
  • final boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒后发送消息。

下面的一个小例子可以通过新线程来周期性地改变ImageView所显示的图片。
public class MainActivity extends Activity {
	
	//定义周期性改变的图片的ID
	int[] imagesId = new int[]{
		R.drawable.a,
		R.drawable.b,
		R.drawable.c,
		R.drawable.d,
		R.drawable.e
	};
	int currentImageId = 0;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final ImageView show = (ImageView) findViewById(R.id.show);
		
		final Handler mHandler = new Handler(){

			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				//判断该消息是否是本程序发送的
				if(msg.what == 0x123){
					//动态地改变显示的图片的ID
					show.setImageResource(imagesId[currentImageId++]);
					if(currentImageId>4){
						currentImageId = 0;
					}
				}
			}
			
		};
		//定义一个计时器,让该计时器周期性地执行指定任务
		new Timer().schedule(new TimerTask() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				//新启动的线程无法访问Activity里的组件
				//所以需要Handler发送消息
				Message msg = new Message();
				msg.what = 0x123;
				
				//发送消息
				mHandler.sendMessage(msg);
			}
		}, 0, 800);
	}
}
当新线程发送消息时,Handler 的handleMessage(Message msg)方法被自动回调,来改变Activity中组件的属性。

与Handler一起工作的几个组件(为了更好地理解Handler的工作原理):

  • Handler:把消息发送给Looper管理的MessageQueue,并负责处理Lopper分给它的消息。
  • Message:Handler接收和处理的消息对象。
  • Looper:每个线程只能有一个Looper。它的loop方法负责读取MessageQueue中的消息,并将消息分给对应的Handler处理。
  • MessageQueue:消息队列,先进先出方式管理Message,程序创建Looper对象时会在它的构造器中创建MessageQueue对象。源码如下:
private Looper(){
	mQueue = new MessageQueue();
	mRn = true;
	mThread = Thread.currentThread();
}
通过源码可知,无法通过构造器来创建Looper对象,程序在初始化Looper时会创建一个与之关联的MessageQueue,这个MessageQueue负责管理消息。

如果要Handler正常工作,就要在当前线程中必须有一个Looper对象:

  • 主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息、处理消息。
  • 我们自己启动的子线程,必须自己创建一个Looper对象,并启动它。创建Looper对象调用它的prepare()方法即可(prepare()方法保证了每一个线程最多只有一个Looper对象),然后调用Looper静态loop()方法来启动它(loop()使用死循环,不断从MessageQueue中取出消息,传递给对应的Handler处理)。
在线程中使用Handler的步骤:

  1. 调用Looper的prepare()方法创建Looper对象,同时会有与之匹配的MessageQueue被创建
  2. 创建Handler子类实例,重写handlerMessage()方法,负责处理来自其他线程的消息。
  3. 调用Looper的loop()方法启动Looper。

例:利用新线程计算质数

public class MainActivity extends Activity {
	
	//2~UPPER_NUM范围内的质数
	static final String UPPER_NUM = "upper";
	EditText etNum;
	
	//定义一个线程类用于计算质数
	CalThread calThread;
	class CalThread extends Thread{
		public Handler handler;
		public void run(){
			//Looper的prepare方法用于创建Looper对象,同时创建与之对应的MessageQueue
			Looper.prepare();
			handler = new Handler(){
				//重写处理消息的方法
				@Override
				public void handleMessage(Message msg) {
					// TODO Auto-generated method stub
					if(msg.what==0x123){
						//获取从2到upper的质数
						int upper = msg.getData().getInt(UPPER_NUM);
						List<Integer> nums = new ArrayList<Integer>();
						outer:
							for(int i = 2;i<upper;i++){
								for(int j = 2;j<Math.sqrt(i);j++){
									if(i!=2&&i%j==0){
										continue outer;
									}
								}
								nums.add(i);
							}
						//把得到的质数显示出来
						Toast.makeText(MainActivity.this, nums.toString(),Toast.LENGTH_LONG).show();
					}
				}
				
			};
			//启动Looper
			Looper.loop();
		}
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		etNum = (EditText) findViewById(R.id.etNum);
		calThread = new CalThread();
		calThread.start();
	}
	
	//
	public void cal(View v){
		//创建消息
		Message msg = new Message();
		msg.what = 0x123;
		
		Bundle bundle = new Bundle();
		bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
		msg.setData(bundle);
		
		//向新线程中的Handler发送消息
		calThread.handler.sendMessage(msg);
	}


}
运行该程序,根据输入的数据计算该范围内的质数都会交给新线程,前台UI不受影响。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章