zoukankan      html  css  js  c++  java
  • 解析Android的 消息传递机制Handler

    1. 什么是Handler:

    Handler 网络释义“机械手。经理”意思,在Android它用于管理多个线程UI操作;

    2. 为什么会出现Handler:

    在Android里面的设计机制。只同意主线程(动时所移动的线程,由于此线程主要是完毕对UI相关事件的处理,所以也称UI线程)

    对UI进行改动等操作,这是一种规则的简化,之所以这样简化是由于Android的UI操作时线程不安全的。为了避免多个线程同一时候操作UI造成线程安全

    问题,才出现了这个简化的规则。

    由此以来,问题就出现了,由于仅仅同意主线程改动UI,那么假设新线程的操作须要改动原来的UI该怎样进行的?举个常见的样例就是:假设新线程的操作是更新UI中TextView

    的值。那么该怎样操作?

    这时候就须要Handler在新线程和主线程(UI线程)之间传递歇息;

    3. Handler的功能:

    主要就是两个:

    1)在新启动的线程中发送消息;

    2)在主线程中获取,处理消息;

    看似简单。可是怎样处理同步问题却是一个难题。即怎样把握新线程发送消息的时机和主线程处理消息的时机。这个问题的解决方式是:

    在主线程和新线程之间使用一个叫做MessageQueue的队列,新启动的线程发送消息时将消息先发送到与之关联的MessageQueue,然后主线程的Handler方法会被调用

    从MessageQueue中去取对应的消息进行处理。

    4. Handler的实现机制

    Handler的实现主要是依靠以下的几个方法:

    读取消息使用到的方法是;  

    void handleMessage(Message msg) 。进程通过重写这种方法来处理消息。

    final boolean hasMessage(int what), 检查消息队列中是否包括what属性为指定值的消息。

    final boolean hasMessage(int what,Object 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)指定多少毫秒之后发送空消息

    public class HandlerTest extends Activity
    {
    	ImageView show;
    	// 代表从网络下载得到的图片
    	Bitmap bitmap;
    	Handler handler = new Handler()
    	{
    		@Override
    		<span style="color:#ff0000;">public void handleMessage(Message msg)</span>
    		{
    			if(msg.what == 0x123)  //假设该消息是本程序发的
    			{
    				// 使用ImageView显示该图片
    				show.setImageBitmap(bitmap);
    			}
    		}
    	};
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		show = (ImageView) findViewById(R.id.show);
    		<span style="color:#ff0000;">new Thread()</span>
    		{
    			public void run()
    			{
    				try
    				{
    					// 定义一个URL对象
    					URL url = new URL("http://img001.21cnimg.com/photos"+
    <span style="white-space:pre">					</span>"/album/20140626/o/C164BDB0B24F59929C2113C0A9910636.jpeg");
    					// 打开该URL相应的资源的输入流
    					InputStream is = url.openStream();
    					// 从InputStream中解析出图片
    					bitmap = BitmapFactory.decodeStream(is);
    					// 发送消息、通知UI组件显示该图片
    					<span style="color:#ff0000;">handler.sendEmptyMessage(0x123);</span>
    					is.close();
    				}
    				catch (Exception e)
    				{
    					e.printStackTrace();
    				}
    			}
    		}.start();
    	}
    }

    新的进程在将图片从网上解析下来之后向主进程发送空消息,之后主线程中handleMessage()的方法会被自己主动调用,更新UI。

    5.  深入理解Handler的工作机制

    上面我们看到了一个简单的Handler的工作过程。当中Handler是在主线程中定义的。假设Handler是在子线程中定义的那么能够更深入的理解他的工作原理。

    由于有的时候我们须要将主线程中的消息传递给子线程。让子线程去处理一些计算量比較大的任务。由于应用程序应尽量避免在UI线程中运行耗时操作,否则会

    导致ANR异常(Application Not Responding)。这种话,我们上面所讲的 主线程和新线程的角色就发生了颠倒,主线程须要向新线程发送消息,然后新线程

    进行消息的处理。在这样的情况下,Handler须要定义在新线程中,在这样的情况下须要做一些额外的工作。

    我们先来解释一下配合Handler的其他组件:

    Looper: 每一个线程相应一个looper,它负责管理MessageQueue。将消息从队列中取出交给Handler进行处理。

    MessageQueue:负责管理Message,接收Handler发送过来的message;

    下图是整个工作的流程:


    以下我们详细阐述一下工作的过程:

    在创建一个Handler之前须要先创建Looper,创建的方式是Looper.prepare();

    在创建Looper的同一时候会自己主动创建MessageQueue;

    以下 创建一个Handler的对象

    然后调用Looper的loop()方法

    以下的这段代码是一个实例,摘自 疯狂android讲义。主进程将上限发送给子线程计算2-上限之间的素数

    public class CalPrime extends Activity
    {
    	static final String UPPER_NUM = "upper";
    	EditText etNum;
    	CalThread calThread;
    	// 定义一个线程类
    	class CalThread extends Thread
    	{
    		<span style="color:#cc0000;">public Handler mHandler;</span>
    
    		public void run()
    		{
    			<span style="color:#ff0000;">Looper.prepare();</span>
    			<span style="color:#ff0000;">mHandler = new Handler()</span>
    			{
    				// 定义处理消息的方法
    				@Override
    				public void handleMessage(Message msg)
    				{
    					if(msg.what == 0x123)
    					{
    						int upper = msg.getData().getInt(UPPER_NUM);
    						List<Integer> nums = new ArrayList<Integer>();
    						// 计算从2開始、到upper的全部质数
    						outer:
    						for (int i = 2 ; i <= upper ; i++)
    						{
    							// 用i处于从2開始、到i的平方根的全部数
    							for (int j = 2 ; j <= Math.sqrt(i) ; j++)
    							{
    								// 假设能够整除,表明这个数不是质数
    								if(i != 2 && i % j == 0)
    								{
    									continue outer;
    								}
    							}
    							nums.add(i);
    						}
    						// 使用Toast显示统计出来的全部质数
    						Toast.makeText(CalPrime.this , nums.toString()
    							, Toast.LENGTH_LONG).show();
    					}
    				}
    			};
    			<span style="color:#ff0000;">Looper.loop();</span>
    		}
    	}
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		etNum = (EditText)findViewById(R.id.etNum);
    		<span style="color:#ff0000;">calThread = new CalThread();</span>
    		// 启动新线程
    		<span style="color:#ff0000;">calThread.start();</span>
    	}
    	// 为button的点击事件提供事件处理函数
    	public void cal(View source)
    	{
    		// 创建消息
    		<span style="color:#ff0000;">Message msg = new Message();</span>
    		msg.what = 0x123;
    		Bundle bundle = new Bundle();
    		bundle.putInt(UPPER_NUM ,
    			Integer.parseInt(etNum.getText().toString()));
    		<span style="color:#ff0000;">msg.setData(bundle);</span>
    		// 向新线程中的Handler发送消息
    		<span style="color:#ff0000;">calThread.mHandler.sendMessage(msg);</span>
    	}
    }

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    DS博客作业02--线性表
    DS博客作业01--日期抽象数据类型设计与实现
    C语言博客作业06--结构体&文件
    C语言博客05--指针
    DS博客作业07--查找
    DS博客作业06--图
    DS博客作业05--树
    DS博客作业03--栈和队列
    DS博客作业02--线性表
    DS博客作业01--日期抽象数据类型设计与实验
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4750886.html
Copyright © 2011-2022 走看看