Android多线程及异步任务消息处理机制(一)--Handler的使用

转载请注明出处:http://blog.csdn.net/yegongheng/article/details/38845953


前言


        我们知道,不管在任何的语言或操作系统平台(Android系统也不例外),多线程、多进程和异步任务的相关技术地讨论都是永恒的话题,很多的开发需求都需要使用多线程及异步任务以便实现多任务的同时执行和灵活的用户操作界面,而很多系统在使用过程中出现诸多莫名其妙的问题,都是由于开发人员使用多线程不当造成的,因此掌握在各个平台上的多线程及异步任务的原理和使用方法非常有必要。那从今天开始,我们将通过一系列的博文来学习在Android平台上的多线程以及异步任务的特点及使用方法,并一步步深入其源码,了解其实现机制。


Handler的概念


         每个Android应用程序都运行在单独的Davlik虚拟机中,当进程开启时会自动启动一个主线程(Main Thread),主线程主要负责与UI相关事件的交互,因此我通常将主线程又称之为UI线程。由于Android采用UI单线程模型,一般情况下,当应用程序执行比较耗时的操作时(例如文件读写、数据库操作和音频视频得下载等),为了防止由于线程执行时间过长而产生的UI线程阻塞,从而导致应用程序出现ANR(Application not response)的情况,我们通常会将该耗时操作放在新建的一个子线程中执行。但是因为UI是非线程安全的,我们又不能在子线程中对操作的结果进行UI元素的更新,只能在UI线程中进行,那怎么办呢?别急,Android类库提供的Handler类很好的解决了该问题。接下来我们将详细讲解Handler消息处理机的使用方法和原理:

概念:Handler是Android类库提供的用于接受、传递和处理消息或Runnable对象的处理类,它结合Message、MessageQueue和Looper类以及当前线程实现了一个消息循环机制,用于实现任务的异步加载和处理。下面有一张用于描述消息循环处理机制的关系图,如下:


上面这一张消息处理机制的关系图清晰地描述了使用Handler实现消息任务的异步加载和处理的一个整体流程,其中还涉及到了除Handler类之外的Message、MessageQueue和Looper类。那为了更进一步地使读者了解其中运行原理,我们来看看Handler、Message、MessageQueue和Looper这几个类的作用,如下:

       Handler:将Message或Runnable对象传递到MessageQueue中,接收从MessageQueue分发出来的Message或Runnable对象;

       MessageQueue:用于存放Message或Runnable对象的消息队列。它由对应的Looper对象创建,并由Looper对象管理;

       Message:消息体,用于装载需要发送的对象;

       Looper:循环不断地管理MessageQueue接收和分发Message或Runnable的工作。

了解了这些类的作用后,我们再来学习下它们之间有哪些关系。首先要明白的是,Handler和Looper对象是属于线程内部的数据,不过也提供与外部线程的访问接口,Handler就是公开给外部线程的接口,用于线程间的通信。Looper是由系统支持的用于创建和管理MessageQueue的依附于一个线程的循环处理对象,而Handler是用于操作线程内部的消息队列的,所以Handler也必须依附一个线程,而且只能是一个线程。基本了解了这四个类的各自职责和相互之间的关系后,我们再使用文字来描述一下整体消息处理机制的运行流程:

       当应用程序开启时,系统会自动为UI线程创建一个MessageQueue(消息队列)和Looper循环处理对象,用户通过在UI线程创建的Handler不断地将Message(消息)或Runnable对象传递到MessageQueue中,Looper循环处理对象在系统的支持下管理着MessageQueue对Message或Runnable的接收和分发,另外Handler也接收从MessageQueue分发出来的特定的Message或Runnable对象。通俗地来讲,一般我们在实际的开发过程中用的比较多一种情况的就是主线程的Handler将子线程中处理过的耗时操作的结果封装成Message(消息),并将该Message(利用主线程里的MessageQueue和Looper)传递到主线程中,最后主线程再根据传递过来的结果进行相关的UI元素的更新,从而实现任务的异步加载和处理,并达到线程间的通信。


Handler的用途


     通过上一小节对Handler的一个初步认识后,我们可以很容易总结出Handler的主要用途,下面是Android官网总结的关于Handler类的两个主要用途:
(1)线程间的通信
在执行较为耗时的操作时,Handler负责将子线程中执行的操作的结果传递到UI线程,然后UI线程再根据传递过来的结果进行相关UI元素的更新。(上面已有说明)
(2)执行定时任务
指定任务时间,在某个具体时间或某个时间段后执行特定的任务操作,例如使用Handler提供的postDelayed(Runnable r,long delayMillis)方法指定在多久后执行某项操作,比如当当、淘宝、京东和微信等手机客户端的开启界面功能,都是通过Handler定时任务来完成的。


Handler的使用实例


      上面我们学习Handler概念时讲了Handler和Looper是属于线程内部的数据,意思就是说每一个实例化的Handler和Looper的对象都是依附于一个线程,这里称为宿主线程。我们知道, Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队列,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行。我们在启动应用程序时,系统会自动为UI线程创建Looper对象和MessageQueue,用于接受、管理和分发不同消息。而对于其它非UI线程来讲,就必须要手动创建了。
废话不多说,先来总结一下使用Handler实现异步消息处理的编程步骤,一般分为如下6个步骤:
    (1)调用Looper.prepare()方法,实例化Looper和MessageQueue对象;(系统自动主线程创建,此步骤省略)
    (2)实例化Handler对象,并重写对象中的handleMessage()方法,用于处理Looper对象分发过来的消息;
    (3)实例化Message对象,并将需要传递的结果对象添加到Message对象中;
    (4)调用目标Handler对象的sendMessage(Object obj)等方法,将Message传递到MessageQueue中;
    (5)调用Looper.loop()方法,启动消息循环管理;(UI线程会自动自动启动loop()方法,此步骤省略)
    (6)在handleMessage()中接收Looper对象分发过来的消息(Message),并执行相关操作UI线程更新等操作。

我们下面来做几个实验,通过在不同线程中创建Looper和Handler,然后利用它们来实现异步消息的处理,看一下运行的结果,以便我们在后面的学习中更好地理解异步任务消息处理机制的实现原理。我们将做如下几个实例:
    (1)在主线程中创建Handler和Looper对象,并使用它们执行异步任务的操作;
    (2)在子线程中创建Handler对象,在主线程创建Looper,并使用它们执行异步任务的操作;
    (3)在子线程中创建Handler和Looper对象,并使用它们执行异步任务的操作;
    (4)使用Handler对象的定时任务的功能实现APP首页欢迎界面的功能。
下面是实例的主界面,如图:


接下来我们实现代码,实现代码的步骤按照上面所罗列出的编码步骤实现。首先看下主界面MainActivity.java的代码(源代码在文末尾可下载),如下:
public class MainActivity extends Activity{
	
	private ImageView mImageView ;
	private static final String TAG = "androidleaf";
	
	private  String urlStr = "https://img-my.csdn.net/uploads/201408/25/1408936379_4781.png" ;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mImageView = (ImageView)findViewById(R.id.dialog_imageview );
	}
	
	public void mainHanlderMainLooper(View view){
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
			 Bitmap mBitmap = HttpUtils.getBitmapFromNetWork(urlStr);
             //1、实例化一个Message对象
             Message message = Message.obtain();
             //将图片流赋值给Message对象
             message. obj = mBitmap;
             //定义标签
             message. what = 0;
             //3、发送消息到主线程中的Handler
             mHandler .sendMessage(message);
			}
		}).start();
		
	}
	//2、在主线程中实例化Handler对象
	Handler  mHandler = new Handler(){

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			 //4、接收消息并执行UI的更新操作
            if (msg.obj != null)
            {
                Bitmap mBitmap = (Bitmap) msg.obj;
                mImageView .setImageBitmap(mBitmap);
            }
            else
            {
                Log. i( TAG, "Can not download the image source!!");
            }
        }
			
	};
	
	public void subHanlderMainLooper(View view){
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Bitmap mBitmap = HttpUtils.getBitmapFromNetWork(urlStr);
		        //实例化一个Message对象
		        Message message = Message.obtain();
		        //将图片流赋值给Message对象
		        message. obj = mBitmap;
				 Handler mHandler3 = new Handler(Looper.getMainLooper())
	             {
	                  @Override
	                  public void handleMessage(Message msg) {
	                       // TODO Auto-generated method stub
	                      super .handleMessage(msg);
	                       if (msg.obj != null)
	                      {
	                          Log. i( TAG, "使用子线程的Handler和主线程的Looper" );
	                      }
	                 }
	             };
	            mHandler3.sendMessage(message);
			}
		}).start();
		
	}
	
	public void subHanlderSubLooper(View view){
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Bitmap mBitmap = HttpUtils.getBitmapFromNetWork(urlStr);
				//1、创建Looper和MessageQueue对象
                Looper. prepare();
			    //2、实例化Handler对象
                Handler mHandler4 = new Handler()
                {
                     @Override
                     public void handleMessage(Message msg) {
                          // TODO Auto-generated method stub
                         super .handleMessage(msg);
                          //6、接收消息并执行UI的更新操作
                          if (msg.obj != null)
                         {
                             Log. i( TAG, "使用子线程的Handler和子线程的Looper" );
                         }
                    }
                };
                //3、实例化一个Message对象
                Message message = Message.obtain();
                //将图片流赋值给Message对象
                message. obj = mBitmap;
                //4、发送消息
                mHandler4.sendMessage(message);
                //5、开启消息循环机制
                Looper. loop();
			}
		}).start();
	}
	
	public void welcome(View view){
		Intent mIntent = new Intent();
        mIntent.setClass(getApplicationContext(), WelcomeActivity.class );
        MainActivity. this .startActivity(mIntent);
	}
}
主界面的布局界面activity_main.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" >

    <ScrollView 
        android:layout_width= "fill_parent"
	    android:layout_height= "fill_parent"
        >
	    <LinearLayout 
	    android:layout_width= "fill_parent"
	    android:layout_height= "fill_parent"
	    android:orientation= "vertical" >
		    <TextView
		        android:layout_width="fill_parent"
		        android:layout_height="wrap_content"
		        android:textSize="30sp"
		        android:textColor="#FF0000"
		        android:text="Handler应用详解" />
		    <Button
		        android:layout_width="fill_parent"
		        android:layout_height="wrap_content"
		        android:text="主线程Handler主线程Looper"
		        android:onClick="mainHanlderMainLooper"
		      />
		  	<ImageView
		        android:layout_width="fill_parent"
		        android:layout_height="wrap_content"
		        android:layout_marginTop="10dip"
		        android:id="@+id/dialog_imageview"
		        />
		  	<Button
		        android:layout_width="fill_parent"
		        android:layout_height="wrap_content"
		        android:text="子线程Handler主线程Looper"
		        android:onClick="subHanlderMainLooper"
		      />
		 
		  	<Button
		        android:layout_width="fill_parent"
		        android:layout_height="wrap_content"
		        android:text="子线程Handler子线程Looper"
		        android:onClick="subHanlderSubLooper"
		      />
			<Button
		        android:layout_width="fill_parent"
		        android:layout_height="wrap_content"
		        android:text="开启欢迎界面"
		        android:onClick="welcome"
		      />
		</LinearLayout>
	</ScrollView>
</LinearLayout>
针对上面的实现代码,我们一一简要地分析这几个实验的原理和操作效果:
(1)第一个实验实现的功能是从网络上下载一个图片并展示在界面上,该功能实现的思路是将下载的耗时操作放在子线程中进行,然后使用在主线程中创建的Handler将子线程下载完成的图片流数据传递到主线程的Looper中MessageQueue里,然后主线程中的Handler接受从MessageQueue分发出来的Message,在主线程中为ImageView设置图片资源对象。将耗时操作放在新建的线程中进行,UI的更新操作由主线程承担,这样就避免了主线程的阻塞而导致了程序的ANR而严重影响用户体验的现象,并且在Android4.0之后系统不允许网络访操作在UI线程中执行,否则会抛出NetworkOnMainThreadException异常。该方法也是在程序开发中最常用的一种方法。点击按钮下载图片后的效果是如图:


(2)第二个实验是使用子线程中创建的Handler和主线程中的Looper、MessageQueue对象来实现任务的异步加载和处理。首先,同样是将下载图片的耗时操作放在子线程中进行,然后将下载的图片流数据包装成消息对象,传递给主线程中的Looper、MessageQueue对象,注意在程序子线程中实例化Handler时使用Looper.getMainLooper()静态方法得到了主线程的Looper和MessageQueue对象。由于是在子线程中创建的Handler,因此Handler从MessageQueue接收的Message也在子线程中,而又由于UI视图组件不是线程安全的,所以不能在子线程中更新相关的视图操作,那么为了检验Handler确实接收到了从MessageQueue分发出来的Message,程序中打了一句Log信息,点击第二个Button后Log信息栏显示的信息为如图:

(3)第三个实验是使用子线程中创建的Handler和子线程中创建的Looper、MessageQueue对象来实现任务的异步加载和处理。应用程序开启时,系统为主线程自动创建Looper和MessageQueue对象,而其他线程默认是没有Looper和MessageQueue对象的,因此要在子线程中使用必须首先调用Looper.prepare()静态方法,这样便在子线程中实例化创建了Looper和MessageQueue对象。而当使用Handler将Message传递到MessageQueue后,Looper所管理的MessageQueue循环机制并没有开启,因此应调用Looper.loop()静态方法来开启子线程中的循环消息管理机制。点击第三个Button后Log信息栏显示的信息如图:

(4)接着我们来看一下第四个实验的具体逻辑代码,代码在WelcomeActivity.java中,如下:
public class WelcomeActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
          // TODO Auto-generated method stub
          super.onCreate(savedInstanceState);
        //设置全屏
        requestWindowFeature(Window. FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN );
        setContentView(R.layout.welcome);
        welcomeMain();
    }
	  //欢迎界面方法
	public void welcomeMain()
	{
	     new Handler().postDelayed(new Runnable() {
	         
	          public void run() {
	        	  //3000毫秒后跳转至WelcomeMainActivity界面
	              Intent mIntent = new Intent(WelcomeActivity.this , WelcomeMainActivity.class );
	              WelcomeActivity. this.startActivity(mIntent);
	              WelcomeActivity. this.finish();
	         }
	       //定时3000毫秒
	     }, 3000);
	}
}
界面的布局代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/welcome_bg"
    tools:context="com.androidleaf.weixin.activity.SplashActivity">

     <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/welcome_earth" />
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@drawable/welcome_people" />

</RelativeLayout>
这里我们通过使用postDelayed(Runnable runnable)方法来完成定时任务,即设置定时3000毫秒(3秒钟)后跳转至WelcomeMainActivity界面。我们这里模仿的是微信APP的欢迎界面,下面我们来看看程序运行的效果吧,如图下

观察效果,是不是和官方微信APP的开启欢迎界面是一样的效果!呵呵,感兴趣的读者也来试一下吧。

小结:消息循环的核心是Looper,Looper持有MessageQueue(消息队列),一个线程可以把Looper设为局部变量,就相当于该线程建立了一个对应的消息队列,而Handler对象是用于操作线程内部的消息队列的,所以Handler必须依附于一个线程。Looper、MessageQueue和Handler是属于线程内部的数据,不过也提供与外部线程的访问接口,Handler就是公开给外部线程的接口,用于线程间的通信。那么,在实际开发过程中,使用Handler最常见的一种情况就是Handler、looper和MessageQueue都依附或对应主线程,Handler将子线程中处理的结果传递到主线的消息循环队列,然后队列将处理完的消息分发给Handler,Handler接收到Message对象后再进行相关的UI更新操作。
     今天我们只是简单地熟悉了一下Handler的概念和原理,并通过实例学习了如何使用Handler的消息循环机制实现了线程间的通信和定时任务的操作。那下一篇博文我们将通过源码剖析,来更深入地分析Handler消息循环处理机制的实现原理,敬请期待!

源代码下载,请戳下面:
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值