Android学习 handler机制的原理

参考自:

http://blog.csdn.net/itachi85/article/details/8035333

http://blog.csdn.net/wyp880303/article/details/6285193

http://www.cnblogs.com/dawei/archive/2011/04/09/2010259.html

以下为自己对handler的理解,可能会有错误的理解,请指正


1.线程+handler+looper内涵

线程:同时处理不同的业务

handler:用来满足线程间的通信

looper:用来管理特定线程内的对象之间的消息交换(处理消息队列)

2.1.线程+handler+looper关系

1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。 
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
3) Message Queue(消息队列):用来存放线程放入的消息
。 

4)一个Looper可以对应多个handler

5)handler.post()执行runnable里的代码,handler.post不需要放在Runnable中,handler.sendMessage();一定要放在线程run()方法中,否则会有问题

6)Hander handler = new Handler(Loop)在哪个线程里创建就会匹配到哪个线程的Looper

注意:一个handler可处理多个线程,一个线程只有一个Looper,handler有两种使用方法

1)handler.post(new Runnable());把线程push到消息队列里,为什么要把线程push到消息队列里?(handler.post(runnable) 与thread.start()功能一样,运行该句后会运行run()里面动作)

2)handler.sendMessage();类似ajax处理返回的数据,Looper会从消息队列里取出消息,发送给handler处理?

答:子线程通过handler才能创建消息,子线程通过handler才能把消息发送给主线程消息队列,Looper会循环查询消息队列,要是有消息了,主线程会调用handler处理消息

3.Handler处理消息

UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

子线程通过Handler、Looper与UI主线程通信的流程如图所示。(类似ajax返回)

附加说明:

<span style="font-size:10px;">ProgressDialog.show(this, prompt_state, prompt_tip);
</span>
<span style="font-size:10px;">new Thread(new Runnable)(){
    @Override
     public void run() {
        promptMsg = "很抱歉获取不到您的位置,请寻找GPS信号良好的地方,重新尝试";
         Looper.prepare(); 
         creatMsgDialog(title, promptMsg);
         Looper.loop();
     }
}).start();</span>
第一段代码是在UI主线程中,所以可以实现对UI的更新,第二段代码创建了一个子线程,此时无法直接更新UI,需要通过Looper来更新,looper循环查询UI主线程消息队列,要是有消息,looper会把消息发送给handler处理。注意:这里looper为UI主线程的Looper。当然也可以handler.sendMessage()在重写的handleMessage()方法里更新UI,

一、UI更新只能是UI主线程实现,Handler 主要接受子线程发送的数据, 并用此数据配合主线程更新UI

解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button, Android会分发事件到Button上,来响应你的操作。  如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示  "强制关闭".  这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,Handler就出现了来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据)  , 把这些消息放入主线程队列中,配合主线程进行更新UI。

二、Handler一些特点
        handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),
        它有两个作用: (1):  安排消息或Runnable 在某个主线程中某个地方执行, (2)安排一个动作在不同的线程中执行
      
        Handler中分发消息的一些方法
        post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)
        以上post类方法允许你排列一个Runnable对象到主线程队列中,
        sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

三、实例
     (1) 子类需要继承Handler类,并重写handleMessage(Message msg) 方法, 用于接受线程数据
      以下为一个实例,它实现的功能为 : 通过线程修改界面Button的内容

package com.yangyu.myactionbar;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;

public class MainActivity extends Activity {

	Button button;
	MyHandler handler;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		init();
	}


	private void init() {
		// TODO Auto-generated method stub
		button = (Button) findViewById(R.id.btn);
		/*
		 * 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
		 * Handler有两个作用
		 * 1)定时执行Message和Runnalbe 对象
		 * 2) 让一个动作,在不同的线程中执行.
		 * 
		 * Handler它安排消息,用以下方法
		 * post(Runnable)
		 * postAtTime(Runnable,long)
		 * postDelayed(Runnable, long)
		 * sendEmptyMessage(int)
		 * sendMessage(Message)
		 * sendMessageAtTime(Message, long)
		 * sendMessageDelayed(Message, long)
		 * 
		 * 以上方法以post开头的处理Runnable对象
		 * sendMessage()处理Message,Message里可包含数据
		 * */
		handler = new MyHandler();
		MyRunnable runnable = new MyRunnable();
		new Thread(runnable).start();
		
	}

	class MyHandler extends Handler{
		public MyHandler(){
			
		}
		
		public MyHandler(Looper looper){
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			//此处更新UI
			Bundle b = msg.getData();
			String color = b.getString("color");
			button.append(color);
			
			
		}
		
	}
	
	class MyRunnable implements Runnable{
		@Override
		public void run() {
			// TODO Auto-generated method stub
				try {
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Message msg = handler.obtainMessage();
				Bundle b = new Bundle();
				b.putString("color", "我的");
				msg.setData(b);
				handler.sendMessage(msg);//向handler发送消息,更新ui
		}
		
	}
	
}
匿名方式写法

package com.yangyu.myactionbar;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;

public class MainActivity extends Activity {

	Button button;
	MyHandler handler;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		init();
	}


	private void init() {
		// TODO Auto-generated method stub
		button = (Button) findViewById(R.id.btn);
		/*
		 * 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
		 * Handler有两个作用
		 * 1)定时执行Message和Runnalbe 对象
		 * 2) 让一个动作,在不同的线程中执行.
		 * 
		 * Handler它安排消息,用以下方法
		 * post(Runnable)
		 * postAtTime(Runnable,long)
		 * postDelayed(Runnable, long)
		 * sendEmptyMessage(int)
		 * sendMessage(Message)
		 * sendMessageAtTime(Message, long)
		 * sendMessageDelayed(Message, long)
		 * 
		 * 以上方法以post开头的处理Runnable对象
		 * sendMessage()处理Message,Message里可包含数据
		 * */
		handler = new MyHandler();
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					Thread.sleep(10000);
					Message msg = handler.obtainMessage();
					String color = "我的";
					msg.what = 1;
					msg.obj = color;
					handler.sendMessage(msg);//向handler发送消息,更新ui
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();

	}

	class MyHandler extends Handler{
		public MyHandler(){

		}

		public MyHandler(Looper looper){
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			//此处更新UI
			if(msg.what == 1){
				try{
					String color =(String)msg.obj;
					button.append(color);
				}catch(Exception e){
					e.printStackTrace();
				}
			}




		}

	}


}

(2)handler.post,handler.post(Runnable)才会开始运行线程,例子为按开始button,每3s打印一次“更新线程”,按结束button结束打印(结束线程)。

//注意:执行postDelayed方法,由于里面设置的间隔时间,所以每3秒会调价一个handler对象到线程队列中

package com.yangyu.myactionbar;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private Button mybutton1;
	private Button mybutton2;
	private TextView text;
	private Handler handler;
	private Runnable updateThread;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mybutton1 = (Button)findViewById(R.id.btn1);
		mybutton2 = (Button)findViewById(R.id.btn2);
		text = (TextView) findViewById(R.id.content);
		

		//创建Handler对象
		handler = new Handler();
		/**
		 * 将要执行的操作卸载写入线程对象的run()方法当中
		 */
		updateThread = new Runnable()
		{
			public void run()
			{
				text.append("更新线程\n");
				//当前还未执行,在run方法内部,执行postXX的方法,每隔3秒会执行一次
				handler.postDelayed(updateThread, 3000);//postDelayed()使得每隔3s执行一次
			}
		};

		
		
		
		mybutton1.setOnClickListener(new Button.OnClickListener()
		{

			@Override
			public void onClick(View arg0) {
				/**
				 * 调用Handler的post方法,将要执行的线程对象添加到
				 * 线程队列中
				 */
				handler.post(updateThread);//立即执行runnbale里面内容
			}


		});
		mybutton2.setOnClickListener(new Button.OnClickListener()
		{

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				//移除runnable
				handler.removeCallbacks(updateThread);
			}

		});  

	}//end OnCreate



}

(3)参考 http://www.iteye.com/topic/1062942

说明:Handler.post(Runnable)会直接把线程放入UI主线程消息队列,由于消息队列是先进先出,所以handler.post(Runnbale)先执行runnable里的所有代码,runnable里的代码执行完毕,才会去执行HandlerMessage()里面的代码,如:当i=100时,handler.sendMessage(msg)把消息放进消息队列,但是要等待runnable其他代码执行完毕,因此先执行判断是否i=100,如果i=100,handler.removeCallback(runnable),执行完之后再去执行handleMessage里的代码,由于之前的消息已经在队列里了,所以不是我这里先执行了判断就不执行handleMessage里的内容了。可以DEBUG一下。具体流程为

1、当点击按钮后,会执行按钮的onClick方法中的
bar.setVisibility(View.VISIBLE);  
handler.post(handlerThread);  
 将进度条显示出来,并且将线程对象加入到线程队列中
2、线程对象对先打印出一个“开始线程”,然后i的值增加10,然后从系统中获取一个Message对象
3、将i赋给Message对象的参数arg1
4、当前线程休眠5秒,然后通过sendMessage方法发送一个Message对象发送到消息队列中
5、然后再执行,通过handleMessage方法设置进度条的值,并且将其加入到进程队列中

Handler handler = new Handler()  
    {  
        public void handleMessage(Message msg)  
        {  
            bar.setProgress(msg.arg1);  
            handler.post(handlerThread);  
        }  
    };  
 6、循环执行,直到i=100,进度条隐藏,并将线程对象从线程队列中取出

package com.yangyu.myactionbar;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

	private ProgressBar bar = null;
	private Button start = null;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		bar = (ProgressBar)findViewById(R.id.progress1);
		start = (Button)findViewById(R.id.start);
		start.setOnClickListener(new Button.OnClickListener()
		{

			@Override
			public void onClick(View v) {
				bar.setVisibility(View.VISIBLE);
				handler.post(handlerThread);
			}

		});
	}
	/**
	 * 使用匿名内部类来复写hanlder当中的hanldrMessage方法
	 * 这里的msg对象就是从线程部分发送过来的对象
	 */
	Handler handler = new Handler()
	{
		public void handleMessage(Message msg)
		{
			bar.setProgress(msg.arg1);
			handler.post(handlerThread);
		}
	};
	//线程类,该类使用的是匿名内部类的方式进行声明
	Runnable handlerThread = new Runnable()
	{
		int i = 0;
		public void run()
		{
			System.out.println("开始线程");
			i = i + 10;
			/**
			 * 得到一个消息对象,Message类是由android操作系统提供
			 * obtainMessage方法用来得到Message对象
			 */
			Message msg = handler.obtainMessage();
			/**
			 * Message中有个成员变量,即msg独享的arg1参数
			 * 将其值设置为i。用arg1或arg2这两个成员变量传递
			 * 消息,优点是系统性能消耗较少
			 */
			msg.arg1 = i;
			try {
				//当前线程休眠1秒
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			/**
			 * 发送一个消息,用sendMessage是将msg加入到消息
			 * 队列中。而post是将线程加入到线程队列中
			 */
			handler.sendMessage(msg);
			if( i == 100)
			{
				/**
				 * 如果i=100的时候,就将线程对象
				 * 从handler当中移除
				 */
				handler.removeCallbacks(handlerThread);
				bar.setVisibility(View.GONE);
			}
		}
	};
}





xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ProgressBar
        android:id="@+id/progress1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:visibility="gone" />

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="点击我" />

</LinearLayout>



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值