zoukankan      html  css  js  c++  java
  • Handler消息传递机制(一)

    为什么要用Handler:

    出于性能优化考虑,Android的UI操作并非线程安全的,这意味着假设有多个线程并发操作UI组件。可能导致线程安全问题。

    为了解决问题,Android制定了一条简单的原则:仅仅同意UI线程(亦即主线程)改动Activity中的UI组件。
    当一个程序第一次启动时。Android会同一时候启动一条主线程,主线程主要负责处理与UI相关的事件,如用户的按键事件、用户接触屏幕的事件、屏幕画图事件,并把相关的事件分发到对应的组件进行处理,所以主线程通常又叫做UI线程。

    Handler的概念:

    1)运行计划任务。能够在预定的时间运行某些任务。能够模拟定时器
    2)线程间通信
    在Android的应用启动时,会创建一个主线程,主线程会创建一个

    消息队列来处理各种消息。当你创建子线程时。你能够在你的子线程中拿到父线程中
    创建的Handler 对象。就能够通过该对象向父线程的消息队列发送消息了
    。因为Android要求在UI线程中更新界面,因此,能够通过该方法在其他线程中更新界面。


    Handler类包括例如以下方法用于发送、处理消息:

    ♦ void handlerMessage(Message msg):处理消息的方法,该方法通经常使用于被重写。


       ♦ final boolean hasMessage(int what):检查消息队列中是否包括what属性为指定值的消息。
    ♦ sendEmptyMessage(int what):发送空消息
    ♦ final boolean sendMessage(Message msg):马上发送消息,注意这块返回值,假设message成功的被放到message queue里面则返回true,反之,返回false;(个人建议:对于这类问题不必主观去记它,当实际使用时,直接查看源代码就可以,源代码中有具体的凝视)

    Handler的作用:
    (1)在一个线程中发送消息。
    (2)在还有一个线程中获取、处理消息。


    Handler处理的基本原理:

    为了让主线程能及时处理子线程发送的消息,显然仅仅能通过回调的方法来实现----开发人员仅仅要重写Handler类中的方法,当新启动的线程发送消息时,消息会发送至与之关联的MessageQueue,而Handler会不断的从MessageQuere中获取并处理消息-----这将导致Handler类中处理消息的方法被回调。
    在线程中使用Handler的基本过程例如以下
    在被调用线程中完毕下面内容:
    (1)调用 Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。


    (2)有了Looper之后,创建Handler子类的实例,重写HandlerMessage()方法,该方法负责处理来自其他线程的消息。
    (3)调用Looper的loop()方法启动Looper。

    注:若被调用线程是主线程类,因为系统自己主动为主线程创建了Looper的实例,因此第一、三步骤可省略。而仅仅须要做第2步就可以。


    在调用线程中完毕:
    (1)创建message。并填充内容。
    (2)使用被调用类创建的Handler实例,调用sendMessage(Message msg)方法。

    Handler 与线程的关系
    Handler 一般执行于主线程内,也能够执行在子线程中。可是一定要创建Looper对象。主线程中Android已经为之创建了Looper对象


    使用Handler的两种常见情况:
    1、仅仅能在主UI中改动UI。但实际上,有部分UI须要在子线程中控制其改动逻辑,因此子线程须要通过handler通知主线程改动UI

    这在游戏开发中尤其常见,比方须要让新启动的线程周期性的改变UI。、

    子线程通知主UI线程改动UI组件的样例,新线程周期性的改动ImageView所显示的图片:

    这个样例是Handler在主线程中获取。处理消息,在子线程中发送消息

    public class HandlerTest extends Activity
    {
    	// 定义周期性显示的图片的ID
    	int[] imageIds = new int[]
    	{
    		R.drawable.java,
    		R.drawable.ee,
    		R.drawable.ajax,
    		R.drawable.xml,
    		R.drawable.classic
    	};
    	int currentImageId = 0;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		final ImageView show = (ImageView) findViewById(R.id.show);
    		final Handler myHandler = new Handler()//在主线程中,获取。处理消息,更新UI组件。能够改动UI组件
    		{
    			@Override
    			public void handleMessage(Message msg)
    			{
    				// 假设该消息是本程序所发送的
    				if (msg.what == 0x1233)
    				{
    					// 动态地改动所显示的图片
    					show.setImageResource(imageIds[currentImageId++
    						% imageIds.length]);
    				}
    			}
    		};
    		// 定义一个计时器,让该计时器周期性地运行指定任务。子线程通知主线程改动UI组件。实现进程间通信
    		new Timer().schedule(new TimerTask()
    		{
    			@Override
    			public void run()
    			{
    				// 发送空消息
    				myHandler.sendEmptyMessage(0x1233);在线程中发送消息
    			}
    		}, 0, 1200);
    	}
    }

    2、为避免ANR。应该在子线程中运行耗时较长的操作,而此操作完毕后,有可能须要通知主线程改动UI

    在子线程中运行耗时任务后。通知主线程改动UI组件的样例:使用新进程计算质数,并用Toast显示

    这个样例是在主线程中发送消息。在子线程中获取,处理消息

    public class CalPrime extends Activity
    {
    	static final String UPPER_NUM = "upper";
    	EditText etNum;
    	CalThread calThread;
    	// 定义一个线程类
    	class CalThread extends Thread
    	{
    		public Handler mHandler;
    
    		public void run()
    		{
    			Looper.prepare();//创建Looper对象,每一个线程使用Handler时都要有一个Looper对象
    			mHandler = new Handler()//在子线程中用handler获取,处理消息
    			{
    				// 定义处理消息的方法
    				@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();
    					}
    				}
    			};
    			Looper.loop();//启动Looper
    		}
    	}
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		etNum = (EditText)findViewById(R.id.etNum);
    		calThread = new CalThread();
    		// 启动新线程
    		calThread.start();
    	}
    	// 为button的点击事件提供事件处理函数
    	public void cal(View source)
    	{
    		// 创建消息
    		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.mHandler.sendMessage(msg);//在主线程中发送消息
    	}
    }











  • 相关阅读:
    SVN安装配置与使用
    ext中对json数据的处理解析
    matlab保存数据
    DLL编程总结
    【MFC 】关于对话框中的OnVScroll() 和 OnHScroll
    OpenCV cvReleaseImage把图像怎么样了?
    [code] if (x<0)x=0;else if (x>255)x=255;
    【DM642学习笔记十】DSP优化记录
    DSP日志打印 LOG_printf
    【MFC】MFC文本框中显示浮点数
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7234928.html
Copyright © 2011-2022 走看看