安卓开发_深入理解Handler消息传递机制

简介: 一、概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面   二、消息类(Message) 消息类是存放在MessageQueue中的,而一个MessageQueue中可以包含多个Message对象 每一个Message对象可以通过Message.

一、概述

因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面

 

二、消息类(Message)

消息类是存放在MessageQueue中的,而一个MessageQueue中可以包含多个Message对象

每一个Message对象可以通过Message.obtain()或者Handler.obtainMessage()方法获得

一个Message具有的属性:

属性

类型

介绍

arg1

int

存放整型数据

arg2

int

存放整型数据

obj

Object

存放Object类型的任意对象

replyTo

Message

指定此Message发送到哪里的可选Message对象

what          

int

指定用户自定义的消息代码,接受者可以了解这个消息的信息

 

 

一个Message对象可以携带int类型的数据,而如果要携带其他类型的数据,可以将要携带的数据保存到Bundle对象中,然后通过Message类的setDate()方法将其添加到Message中

注:

1、尽量使用Message.what标识信息,方便用于不同的方式处理Message

2、通常情况下,尽量使用Message.obtain()或者Handler.obtainMessage()方法从消息池中获得空消息对象,以便节省资源,而不是使用Message的默认构造方法

3、当一个Message对象只需要携带int型数据的时候,优先使用Message.arg1或Message.arg2属性,这要比用Bundle携带int数据节省内存

 

三、消息处理类(Handler)

Handler 允许 发送或者处理 Message或者Runnable 类的对象到其(Handler)所在线程的MessageQueue中

主要有两个作用:

1、连接主线程和子线程进行通信(UI线程和工作线程通信)

2、将Message对象 通过post()或者sendMessage()方法发送到MessageQueue中,

当MessageQueue循环到该对象时,调用相应的Handler对象的handlerMessage()方法进行处理

Handler类提供的部分方法:

方法

介绍

handleMessage(Message msg)

处理消息的方法。

通常使用该方法处理消息,

在发送消息时,该方法会自动回调

Post(Runnable r)

立即发送Runnable对象,

注:该Runnable对象最终将被封装成Mwssage对象

PostAtTime(Runnable r,long uptimeMillis)

定时发送Runnable对象,

注:该Runnable对象最终将被封装成Mwssage对象

postDelayed(Runnable r,long delayMillis)

延迟发送Runnable对象,

注:该Runnable对象最终将被封装成Mwssage对象

sendEmptyMessage(int what)

发送空消息

sendMessage(Message msg)

立即发送消息

sendMessageAtTime(Message msg)

定时发送消息

sendMessageDelayed(Message msg,long delayMillis)

延迟发送消息

注:在一个线程中,只能有一个Looper和MessageQueue,却可以有多个Handler,这些Handler共享同一个Looper和MessageQueue

 

四、循环着(Looper)

1、从上面只是可以知道:一个线程中,只能有一个Looper和MessageQueue。

也就是说一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(消息队列),一个MessageQueue又存放着多条Message

注意:MessageQueue中,消息的存放时FIFO(先进先出)的。

 

2、Looper对象是为了一个线程开启一个消息循环,来操作MessageQueue

注:在主线程中,系统会为主线程自动创建一个Looper对象来开启消息循环。而在非主线程中,是没有Looper对象的,没有Looper对象就不能创建Handler对象

so,在主线程中直接创建Handler对象是可以的。但是在非主线程中创建Handler对象则会产生异常

 

3、如果需要在非主线创建Handler对象

(1)使用Looper类的prepare()方法来初始化一个Looper对象

(2)创建Handler对象

(3)使用Looper类的loop()方法启动Looper,开启消息循环

 

                   //需要先有Looper对象
            Looper.prepare();//会创建一个Looper对象,并把该对象放入到该线程的本地变量中,在Looper的构造方法中创建了MessageQueue对象
            //在子线程中实例化handler,子线程中没有Looper对象 
            
            handler = new Handler(){
                
            };//如果直接实例化Handler,会异常 new RuntimeException,原因是子线程中没有Looper对象 
            
            //让Looper对象循环读取MessageQueue中的消息
            //循环从队列中读取消息,当读到消息时,会去调用   msg.target.dispatchMessage(msg);
            //在Message类中有一个target成员,当发送消息时,target成员被赋值为当前的 handler对象
            Looper.loop();

 

 

Looper提供的部分方法:

方法

描述

prepare()

用于初始化Looper

loop()

启动Looper线程,线程循环消息队列(MessageQueue)获取和处理信息

myLooper()

获得当前线程的Looper对象

getThread

获得Looper对象所属的线程

quit()

结束Looper循环

*注*:Looper.loop()方法,这个方法是循环消息队列!即这个方法内部相当于正在执行一个死循环,所以在Looper.loop()代码之后的代码都不会执行

,而只有再调用 Looper.quit()之后才会执行后面的代码

 

-------------------------------------------------------------华丽的分割线------------------------------------------------------------------

 

让我们看几个例子来深入理解下Handler消息传递机制

1、子线程向主线程发送消息

在主线程中启动一个子线程下载图片,子线程传消息递给主线程,让主线程处理。(了解子线程发送Runnable对象和发送Message对象的两种方法)

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:gravity="center_horizontal"
 7      >
 8 
 9     <ImageView 
10         android:layout_width="200dp"
11         android:layout_height="200dp"
12         android:id="@+id/show_image"
13         android:src="@drawable/ic_launcher"
14         />
15     
16     <Button 
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:id="@+id/down_image"
20         android:text="下载"
21         />
22 
23 </LinearLayout>
布局文件代码

(1)发送Message对象的方法

 1 package com.xqx.handle;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.http.HttpResponse;
 6 import org.apache.http.client.ClientProtocolException;
 7 import org.apache.http.client.HttpClient;
 8 import org.apache.http.client.methods.HttpGet;
 9 import org.apache.http.impl.client.DefaultHttpClient;
10 import org.apache.http.util.EntityUtils;
11 
12 import android.app.Activity;
13 import android.graphics.Bitmap;
14 import android.graphics.BitmapFactory;
15 import android.os.Bundle;
16 import android.os.Handler;
17 import android.os.Message;
18 import android.view.View;
19 import android.view.View.OnClickListener;
20 import android.widget.Button;
21 import android.widget.ImageView;
22 
23 
24 public class MainActivity extends Activity {
25     private String path = "http://images2015.cnblogs.com/blog/493196/201509/493196-20150911220222090-1272536050.jpg";//图片下载地址
26     private ImageView showImage;
27     private Button downImage;
28     //哪一个线程是接受消息的,就在哪个线程中实例化Handler对象
29     //因为主线程中,系统会为自动创建Looper对象,开启循环消息,所以只需要在主线程中定义Handler对象
30     private Handler handler = new Handler(){
31         @Override //处理消息的方法,当消息发送过来时,该方法自动回调
32         public void handleMessage(android.os.Message msg) {
33             //处理方法,将图片显示在ImageView中
34             showImage.setImageBitmap((Bitmap) msg.obj);
35             
36         };
37     };
38     @Override
39     protected void onCreate(Bundle savedInstanceState) {
40         // TODO Auto-generated method stub
41         super.onCreate(savedInstanceState);
42         setContentView(R.layout.activity_main);
43         
44         showImage = (ImageView) findViewById(R.id.show_image);
45         downImage = (Button) findViewById(R.id.down_image);
46         
47         downImage.setOnClickListener(new OnClickListener() {
48             
49             @Override
50             public void onClick(View v) {
51                 // TODO Auto-generated method stub
52                 //开启一个线程下载图片
53                 new Thread(new Runnable() {
54                     
55                     @Override
56                     public void run() {
57                         // TODO Auto-generated method stub
58                         HttpGet get = new HttpGet(path);
59                         HttpClient client = new DefaultHttpClient();
60                         HttpResponse response = null;
61                         
62                         try {
63                             response = client.execute(get);
64                             if(response.getStatusLine().getStatusCode()==200)
65                             {
66                                 //获得下载后的图片的字节数据
67                                 byte b[] = EntityUtils.toByteArray(response.getEntity());
68                                 //将图片字节数据转换成Bitmap格式
69                                  Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
70                                  //下载完成,将图片发送给主线程
71                                  //从MessageQueue中获取可用的Message对象,如果没有可用的,则会创建一个新的Message对象
72                                  Message msg = Message.obtain();
73                                  //把发送的图片放到msg对象中
74                                  msg.obj = bitmap;
75                                  //使用Handler.sendMessage(Message msg)方法传递消息
76                                  handler.sendMessage(msg);
77                             }
78                             
79                             
80                         } catch (ClientProtocolException e) {
81                             // TODO Auto-generated catch block
82                             e.printStackTrace();
83                         } catch (IOException e) {
84                             // TODO Auto-generated catch block
85                             e.printStackTrace();
86                         }
87                         
88                         
89                     }
90                 }).start();//不要忘记开启线程
91             }
92         });
93         
94     }
95     
96     
97 }
Handler.sendMessage(Message msg)

(2)、发送Runnable对象的方法

 1 package com.xqx.handle;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.http.HttpResponse;
 6 import org.apache.http.client.ClientProtocolException;
 7 import org.apache.http.client.HttpClient;
 8 import org.apache.http.client.methods.HttpGet;
 9 import org.apache.http.impl.client.DefaultHttpClient;
10 import org.apache.http.util.EntityUtils;
11 
12 import android.app.Activity;
13 import android.graphics.Bitmap;
14 import android.graphics.BitmapFactory;
15 import android.os.Bundle;
16 import android.os.Handler;
17 import android.os.Message;
18 import android.view.View;
19 import android.view.View.OnClickListener;
20 import android.widget.Button;
21 import android.widget.ImageView;
22 
23 
24 public class Handler2 extends Activity {
25     private String path = "http://images2015.cnblogs.com/blog/493196/201509/493196-20150911220222090-1272536050.jpg";//图片下载地址
26     private ImageView showImage;
27     private Button downImage;
28     //哪一个线程是接受消息的,就在哪个线程中实例化Handler对象
29     //因为主线程中,系统会为自动创建Looper对象,开启循环消息,所以只需要在主线程中定义Handler对象
30     private Handler handler = new Handler();
31     @Override
32     protected void onCreate(Bundle savedInstanceState) {
33         // TODO Auto-generated method stub
34         super.onCreate(savedInstanceState);
35         setContentView(R.layout.activity_main);
36         
37         showImage = (ImageView) findViewById(R.id.show_image);
38         downImage = (Button) findViewById(R.id.down_image);
39         
40         downImage.setOnClickListener(new OnClickListener() {
41             
42             @Override
43             public void onClick(View v) {
44                 // TODO Auto-generated method stub
45                 //开启一个线程下载图片
46                 new Thread(new Runnable() {
47                     
48                     private Bitmap bitmap;
49 
50                     @Override
51                     public void run() {
52                         // TODO Auto-generated method stub
53                         HttpGet get = new HttpGet(path);
54                         HttpClient client = new DefaultHttpClient();
55                         HttpResponse response = null;
56                         
57                         try {
58                             response = client.execute(get);
59                             if(response.getStatusLine().getStatusCode()==200)
60                             {
61                                 //获得下载后的图片的字节数据
62                                 byte b[] = EntityUtils.toByteArray(response.getEntity());
63                                 bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
64                                  //下载完成,将图片发送给主线程
65                                 //Handler.post(Runnable r)方法
66                                  handler.post(new Runnable() {
67                                     
68                                     @Override
69                                     public void run() {
70                                         // TODO Auto-generated method stub
71                                         showImage.setImageBitmap(bitmap);
72                                     }
73                                 });
74                             }
75                             
76                             
77                         } catch (ClientProtocolException e) {
78                             // TODO Auto-generated catch block
79                             e.printStackTrace();
80                         } catch (IOException e) {
81                             // TODO Auto-generated catch block
82                             e.printStackTrace();
83                         }
84                         
85                         
86                     }
87                 }).start();//不要忘记开启线程
88             }
89         });
90         
91     }
92     
93     
94 }
Handler.post(Runnable r)

最后因为下载网络图片,不要忘记在清单文件中 添加网络访问权限

    <uses-permission android:name="android.permission.INTERNET"/>

效果图:

下载前:

下载后:

 

2、主线程向子线程发送消息

布局还是上面的那个,就不贴代码了

 1 package com.xqx.handle;
 2 
 3 import android.app.Activity;
 4 import android.graphics.Color;
 5 import android.os.Bundle;
 6 import android.os.Handler;
 7 import android.os.Looper;
 8 import android.os.Message;
 9 import android.view.View;
10 import android.view.View.OnClickListener;
11 import android.widget.Button;
12 import android.widget.ImageView;
13 import android.widget.Toast;
14 
15 public class Handler3 extends Activity{
16     
17     private Button changeImage;
18     private ImageView showImage;
19     private Handler handler;
20     
21     @Override
22     protected void onCreate(Bundle savedInstanceState) {
23         // TODO Auto-generated method stub
24         super.onCreate(savedInstanceState);
25         setContentView(R.layout.activity_main);
26         
27         changeImage = (Button) findViewById(R.id.down_image);
28         showImage = (ImageView) findViewById(R.id.show_image);
29         MyThread mt = new MyThread();
30         Thread h = new Thread(mt);
31         h.start();
32         changeImage.setOnClickListener(new OnClickListener() {
33             
34             @Override
35             public void onClick(View v) {
36                 // TODO Auto-generated method stub
37                 //主线程发送消息
38                 Message msg = Message.obtain();
39                 msg.what = 1;
40                 handler.sendEmptyMessage(1);
41             }
42         });
43         
44     }
45     
46     class MyThread implements Runnable
47     {
48 
49         @Override
50         public void run() {
51             // TODO Auto-generated method stub
52             Looper.prepare();
53             handler = new Handler(){
54                 @Override
55                 public void handleMessage(Message msg) {
56                     // TODO Auto-generated method stub
57                     super.handleMessage(msg);
58                     int number = msg.what;
59                     Toast.makeText(getApplicationContext(), "子线程接收到了主线程的消息,消息内容为:"+number, 1).show();
60                 }
61             };
62             Looper.loop();
63         }
64         
65     }
66 
67 }
主线程项子线程发送消息

效果图:

 

相关文章
|
6天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
5天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
18 5
|
3天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
5天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
16 3
|
7天前
|
存储 IDE 开发工具
探索Android开发之旅:从新手到专家
【10月更文挑战第26天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索如何在Android平台上从零开始,最终成为一名熟练的开发者。通过简单易懂的语言和实际代码示例,本文将引导你了解Android开发的基础知识、关键概念以及如何实现一个基本的应用程序。无论你是编程新手还是希望扩展你的技术栈,这篇文章都将为你提供价值和启发。让我们开始吧!
|
13天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
42 5
|
12天前
|
设计模式 IDE Java
探索安卓开发:从新手到专家的旅程
【10月更文挑战第22天】 在数字时代的浪潮中,移动应用开发如同一座金矿,吸引着无数探险者。本文将作为你的指南针,指引你进入安卓开发的广阔天地。我们将一起揭开安卓平台的神秘面纱,从搭建开发环境到掌握核心概念,再到深入理解安卓架构。无论你是初涉编程的新手,还是渴望进阶的开发者,这段旅程都将为你带来宝贵的知识和经验的财富。让我们开始吧!
|
2天前
|
移动开发 Java Android开发
探索Android与iOS开发的差异性与互联性
【10月更文挑战第32天】在移动开发的大潮中,Android和iOS两大平台各领风骚。本文将深入浅出地探讨这两个平台的开发差异,并通过实际代码示例,展示如何在各自平台上实现相似的功能。我们将从开发环境、编程语言、用户界面设计、性能优化等多个角度进行对比分析,旨在为开发者提供跨平台开发的实用指南。
18 0
|
12天前
|
搜索推荐 Android开发 UED
安卓开发中的自定义视图:打造个性化用户界面
【10月更文挑战第22天】在安卓应用的海洋中,如何让你的应用脱颖而出?一个独特且直观的用户界面(UI)至关重要。本文将引导你通过自定义视图来打造个性化的用户体验,从基础的视图绘制到触摸事件的处理,我们将一步步深入探讨。准备好了吗?让我们开始吧!
|
12天前
|
Android开发
我是一位Android工程师,用通义灵码的AS插件做开发工作助手,对比之前没有灵码,现在提效了60%
我是一位Android工程师,用通义灵码的AS插件做开发工作助手,对比之前没有灵码,现在提效了60%
28 0