Android多线程----Handler

关于Android的多线程知识,请参考本人之前的一篇博客:

在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是今天要讲的Handler的机制,还有一种就是之前讲过的 AsyncTask 机制。

一、handler的引入:

我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。具体实现代码如下:

MainActivity.java的代码如下:


package com.example.androidthreadtest;



import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;



public class MainActivity extends Activity implements OnClickListener {



	public static final int UPDATE_TEXT = 1;



	private TextView text;



	private Button changeText;



	private Handler handler = new Handler() {



		public void handleMessage(Message msg) {

			switch (msg.what) {

			case UPDATE_TEXT:

				text.setText("Nice to meet you");

				break;

			default:

				break;

			}

		}



	};



	@Override

	protected void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_main);

		text = (TextView) findViewById(R.id.text);

		changeText = (Button) findViewById(R.id.change_text);

		changeText.setOnClickListener(this);

	}



	@Override

	public void onClick(View v) {

		switch (v.getId()) {

		case R.id.change_text:

			new Thread(new Runnable() {

				@Override

				public void run() {

					Message message = new Message();

					message.what = UPDATE_TEXT;

					handler.sendMessage(message);

				}

			}).start();

			break;

		default:

			break;

		}

	}



}

上方第50行代码建议换成:


Message msg = handler.obtainMessage();

上面的代码中,我们并没有在子线程中直接进行UI操作,而是创建了一个Message对象,并将它的what字段的值指定为了一个整形常量UPDATE_TEXT,用于表示更新TextView这个动作。然后调用Handler的sendMessage()方法将这条Message发送出去。很快,Handler就会收到这条Message,并在handleMessage()方法,在这里对具体的Message进行处理。如果发现Message的what字段的值等于UPDATE_TEXT,就将TextView显示的内容更新。运行程序后,点击按钮,TextView就会显示出更新的内容。

二、异步消息处理机制:

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

根据上面的图片,我们现在来解析一下异步消息处理机制

  • Message:消息体,用于装载需要发送的对象。
  • handler:它直接继承自Object。作用是:在子线程中发送Message或者Runnable对象到MessageQueue中;在UI线程中接收、处理从MessageQueue分发出来的Message或者Runnable对象。发送消息一般使用Handler的sendMessage()方法,而发出去的消息经过处理后最终会传递到Handler的handlerMessage()方法中。
  • MessageQueue:用于存放Message或Runnable对象的消息队列。它由对应的Looper对象创建,并由Looper对象管理。每个线程中都只会有一个MessageQueue对象。
  • Looper:是每个线程中的MessageQueue的管家,循环不断地管理MessageQueue接收和分发Message或Runnable的工作。调用Looper的loop()方法后,就会进入到一个无限循环中然后每当发现MessageQueue中存在一条消息,就会将它取出,并调用Handler的handlerMessage()方法。每个线程中也只会有一个Looper对象。

了解这些之后,我们在来看一下他们之间的联系:

首先要明白的是,Handler和Looper对象是属于线程内部的数据,不过也提供与外部线程的访问接口,Handler就是公开给外部线程的接口,用于线程间的通信。Looper是由系统支持的用于创建和管理MessageQueue的依附于一个线程的循环处理对象,而Handler是用于操作线程内部的消息队列的,所以Handler也必须依附一个线程,而且只能是一个线程。

我们再来对异步消息处理的整个流程梳理一遍:

当应用程序开启时,系统会自动为UI线程创建一个MessageQueue(消息队列)和Looper循环处理对象。首先需要在主线程中创建一个Handler对象,并重写handlerMessage()方法。然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。之后这条消息就会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,并找到与消息对象对应的Handler对象,然后调用Handler的handleMessage()方法。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI操作了。

通俗地来讲,一般我们在实际的开发过程中用的比较多一种情况的就是主线程的Handler将子线程中处理过的耗时操作的结果封装成Message(消息),并将该Message(利用主线程里的MessageQueue和Looper)传递到主线程中,最后主线程再根据传递过来的结果进行相关的UI元素的更新,从而实现任务的异步加载和处理,并达到线程间的通信。

通过上一小节对Handler的一个初步认识后,我们可以很容易总结出Handler的主要用途,下面是Android官网总结的关于Handler类的两个主要用途:

(1)线程间的通信:

在执行较为耗时的操作时,Handler负责将子线程中执行的操作的结果传递到UI线程,然后UI线程再根据传递过来的结果进行相关UI元素的更新。(上面已有说明)

(2)执行定时任务:

指定任务时间,在某个具体时间或某个时间段后执行特定的任务操作,例如使用Handler提供的postDelayed(Runnable r,long delayMillis)方法指定在多久后执行某项操作,比如当当、淘宝、京东和微信等手机客户端的开启界面功能,都是通过Handler定时任务来完成的。

我们接下来讲一下post。

三、post:

对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。

Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。详细解释如下:

  • boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
  • boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
  • boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
  • void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。

下面通过一个Demo,讲解如何通过Handler的post方式在新启动的线程中修改UI组件的属性

MainActivity.java的代码如下:


package com.example.m03_threadtest01;



import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;



public class MainActivity extends Activity {

    private Button btnMes1,btnMes2;

    private TextView tvMessage;

    // 声明一个Handler对象

    private static Handler handler=new Handler();

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);        

        

        btnMes1=(Button)findViewById(R.id.button1);

        btnMes2=(Button)findViewById(R.id.button2);

        tvMessage=(TextView)findViewById(R.id.TextView1);

        btnMes1.setOnClickListener(new View.OnClickListener() {

            

            @Override

            public void onClick(View v) {

                // 新启动一个子线程

                new Thread(new Runnable() {                    

                    @Override

                    public void run() {

                        // tvMessage.setText("...");

                        // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问

                        // 使用post方式修改UI组件tvMessage的Text属性

                        handler.post(new Runnable() {                    

                            @Override

                            public void run() {

                                tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。");                        

                            }

                        });                                

                    }

                }).start();

            }

        });

        

        btnMes2.setOnClickListener(new View.OnClickListener() {

            

            @Override

            public void onClick(View v) {

                new Thread(new Runnable() {                    

                    @Override

                    public void run() {

                        // 使用postDelayed方式修改UI组件tvMessage的Text属性值

                        // 并且延迟3S执行

                        handler.postDelayed(new Runnable() {

                            

                            @Override

                            public void run() {

                                tvMessage.setText("使用Handler.postDelayed在工作线程中发送一段执行到消息队列中,在主线程中延迟3S执行。");    

                                

                            }

                        }, 3000);                        

                    }

                }).start();

                

            }

        });

    }

    

}

点击按钮,运行结果如下:

有一点值得注意的是,对于Post方式而言,它其中Runnable对象的run()方法的代码,均执行在UI线程上(虽然是写在子线程当中的),所以对于这段代码而言,不能执行在UI线程上的操作,一样无法使用post方式执行,比如说访问网络,下面提供一个例子,使用post方式从互联网上获取一张图片,并且显示在ImageView中。

四、Message:

Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。

对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。

Handler中,与Message发送消息相关的方法有:

  • Message obtainMessage():获取一个Message对象。
  • boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
  • boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。
  • boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。
  • boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。
  • void removeMessage():从消息队列中移除一个未响应的消息。

五、通过Handler实现线程间通信:

1、在Worker Thread发送消息,在MainThread中接收消息:

见另外一篇博客:

2、在MainThread中发送消息,在Worker Thread中接收消息:

见另外一篇博客:

时间: 2024-10-12 22:41:58

Android多线程----Handler的相关文章

Android多线程——Handler (一) 实现图片下载

在UI线程中创建子线程-->在子线程中获取网络图片-->在线程中通过Message 传递二进制图片给Handler,-->Handler在handleMessage()中处理消息 package com.example.android_handler_message; import java.io.IOException; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolEx

Android多线程——Handler (二)

传递消息的集中方式: 一: Message message = Message.obtain(); message.obj = data; message.what = IS_FINISHED; handle.sendMessage(message); 二: Message message = Message.obtain(handle); message.obj = data; message.sendToTarget(); 三:可以在message中传递复杂数据 Message messag

【Android基础知识】【android多线程handler】

handler机制不需要明白太多,我认为可以参照例子来实现,每次使用的时候就用一下例子. 既有线程,又有mhandler对接收到的消息的处理. package com.hengtiansoft.sportnow.news.ui; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import

Android多线程编程之Handler篇(消息机制)

Android多线程编程之Handler篇(消息机制) Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑. MessageQueue 消息队列,以队列的形式(实为单链表结构)对外提供插入和删除的工作, Looper 以无限循环的形式不断获取MessageQueue中的消息,有则处理,无则等待. ThreadLocal ThreadLocal可以在不同的线程互不干扰的存储并提供数据,通过ThreadLocal可以很

Android多线程之图解Handler Looper MessageQueue Message

Android中的多线程可以有多种实现方式,前面我们已经讲过了封装程度较高异步任务(AnsyncTask),这一节我们来看看较为灵活的方式:Handler Looper MessageQueue Message. Message:用于线程之间传递信息,发送的消息放入目标线程的MessageQueue中. MessageQueue:用于简化线程之间的消息传递,MessageQueue接受发送端的Message,并作为消息处理端的输入源.每个线程只有一个实例. Handler:用于处理Message

转载:android笔记--android中的多线程--Handler, Looper, MessageQueue, Message类

什么时候使用多线程: 1. 耗时操作使用多线程, 耗时操作放在UI线程中会导致用户的操作无法得到响应. 2. 阻塞操作使用多线程, 理由同上. 3. 多核CUP的设备使用多线程, 可以有效提高CPU的利用率. 4. 并行操作使用多线程. android中的多线程模型主要涉及的类有:Looper, Handler, MessageQueue, Message等. 一:Looper类: 1 static final ThreadLocal<Looper> sThreadLocal = new Th

Android多线程分析之三:Handler,Looper的实现

Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前文<Android多线程分析之二:Thread的实现>中已经具体分析了Android Thread 是怎样创建,执行以及销毁的,其重点是对对应 native 方法进行分析,今天我将聚焦于 Android Framework 层多线程相关的类:Handler, Looper, MessageQueue, Message 以及它们与

Android多线程----异步消息处理机制之Handler详解

关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是今天要讲的Handler的机制,还有一种就是之前讲过的 AsyncTask 机制. 一.handler的引入: 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创

无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)

1.smartimageview使用 <LinearLayout 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"