Android线程间通信机制

当android应用程序运行时,一个主线程被创建(也称作UI线程),此线程主要负责处理UI相关的事件,由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作,如果在非UI线程直接对UI进行了操作,则会报错,另外,对于运算量较大的操作和IO操作,我们需要新开线程来处理这些工作,以免阻塞UI线程,子线程与主线程之间是怎样进行通信的呢?此时就要采用消息循环机制(Looper)与Handler进行处理。

一、基本概念

Looper:每一个线程都可以产生一个Looper,用来管理线程的Message,Looper对象会建立一个MessgaeQueue数据结构来存放message。

Handler:与Looper沟通的对象,可以push消息或者runnable对象到MessgaeQueue,也可以从MessageQueue得到消息。

查看其构造函数:

Handler()

Default constructor associates this handler with the queue for the current thread.//如不指定Looper参数则默认利用当前线程的Looper创建

Handler(Looper looper)

Use the provided queue instead of the default one.//使用指定的Looper对象创建Handler

线程A的Handler对象引用可以传递给别的线程,让别的线程B或C等能送消息来给线程A。

线程A的Message Queue里的消息,只有线程A所属的对象可以处理。

注意:Android里没有global的MessageQueue,不同进程(或APK之间)不能通过MessageQueue交换消息。

二、Handler通过Message通信的基本方式

使用Looper.myLooper可以取得当前线程的Looper对象。

使用mHandler = new Handler(Looper.myLooper()); 可产生用来处理当前线程的Handler对象。

使用mHandler = new Handler(Looper.getMainLooper()); 可诞生用来处理main线程的Handler对象。

使用Handler传递消息对象时将消息封装到一个Message对象中,Message对象中主要字段如下:

public int

arg1

当需要传递的消息是整形时arg1 和 arg2 是一种低成本的可选方案,他使用 setData()/getData()访问或修改字段。

public int

arg2

同上

public Object

obj

可传送的任意object类型.

public int

what

Int类型用户自定义的消息类型码

Message对象可以通过Message类的构造函数获得,但Google推荐使用Message.obtain()方法获得,该方法会从全局的对象池里返回一个可复用的Messgae实例,API中解释如下:

Message()

Constructor (but the preferred way to get a Message is to call Message.obtain()).

Handler发出消息时,既可以指定消息被接受后马上处理,也可以指定经过一定时间间隔之后被处理,如sendMessageDelayed(Message msg, long delayMillis),具体请参考API。

Handler消息被发送出去之后,将由handleMessage(Message msg)方法处理。

注意:在Android里,新诞生一个线程,并不会自动建立其Message Loop

可以通过调用Looper.prepare()为该线程建立一个MessageQueue,再调用Looper.loop()进行消息循环。

下面举例说明:

在main.xml中定义两个button及一个textview

<Button

    android:id="@+id/a"

    android:layout_width="80dp"

    android:layout_height="60dp"

    android:padding="6dp"

    android:layout_marginTop="10dp"

    android:text="Test looper"

    android:hint="Test looper" />

<Button

    android:id="@+id/b"

    android:layout_width="80dp"

    android:layout_height="60dp"

    android:padding="6dp"

    android:layout_marginTop="10dp"

    android:text="Exit" />

<TextView

    android:id="@+id/tv"

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:layout_marginTop="10dp"/>

Activity源代码如下:

  1 public class HandlerTestActivity extends Activity implements Button.OnClickListener{  2   3     public TextView tv;  4   5     private myThread myT;  6   7     Button bt1, bt2;  8   9     /** Called when the activity is first created. */ 10  11     @Override 12  13     public void onCreate(Bundle savedInstanceState) { 14  15         super.onCreate(savedInstanceState); 16  17         setContentView(R.layout.main); 18  19         bt1 = (Button)findViewById(R.id.a); 20  21         bt2 = (Button)findViewById(R.id.b); 22  23         tv = (TextView)findViewById(R.id.tv); 24  25         bt1.setId(1);//为两个button设置ID,此ID用于后面判断是哪个button被按下 26  27         bt2.setId(2); 28  29         bt1.setOnClickListener(this);//增加监听器 30  31         bt2.setOnClickListener(this); 32  33     } 34  35     36  37     @Override 38  39     public void onClick(View v) { 40  41        // TODO Auto-generated method stub 42  43        switch(v.getId()){//按键事件响应,如果是第一个按键将启动一个新线程 44  45        case 1: 46  47            myT = new myThread(); 48  49             myT.start(); 50  51             break; 52  53        case 2: 54  55         finish(); 56  57            break; 58  59        default: 60  61            break; 62  63        } 64  65     }   66  67         68  69     class myThread extends Thread 70  71     { 72  73     private EHandler mHandler; 74  75          public void run() 76  77          { 78  79              Looper myLooper, mainLooper; 80  81              myLooper = Looper.myLooper();//得到当前线程的Looper 82  83              mainLooper = Looper.getMainLooper();//得到UI线程的Looper 84  85              String obj; 86  87              if(myLooper == null)//判断当前线程是否有消息循环Looper 88  89              { 90  91               mHandler = new EHandler(mainLooper); 92  93               obj = "current thread has no looper!";//当前Looper为空,EHandler用mainLooper对象构造 94  95               } 96  97               else 98  99               {100 101                  mHandler = new EHandler(myLooper);//当前Looper不为空,EHandler用当前线程的Looper对象构造102 103                  obj = "This is from current thread.";104 105               }106 107               mHandler.removeMessages(0);//清空消息队列里的内容108 109               Message m = mHandler.obtainMessage(1, 1, 1, obj);110 111               mHandler.sendMessage(m);//发送消息112 113            }114 115     }116 117    118 119     class EHandler extends Handler120 121     {122 123         public EHandler(Looper looper)124 125         {126 127             super(looper);128 129         }130 131         @Override132 133         public void handleMessage(Message msg) //消息处理函数134 135         {136 137            tv.setText((String)msg.obj);//设置TextView内容138 139         }140 141     }142 143  144 145 }

程序运行后点击TestLooper按键,TextView输出如下,说明新创建的线程里Looper为空,也就说明了新创建的线程并不会自己建立Message Looper。

修改myThread类:

 1 class myThread extends Thread 2  3     { 4  5     private EHandler mHandler; 6  7          public void run() 8  9          {10 11             Looper myLooper, mainLooper;12 13             Looper.prepare();14 15              myLooper = Looper.myLooper();16 17              mainLooper = Looper.getMainLooper();18 19             ......20 21            ......22 23               mHandler.sendMessage(m);24 25             Looper.loop();26 27            }28 29 }

Looper.prepare为当前线程创建一个Message Looper,Looper.loop()开启消息循环。这样修改是OK呢?

答案是否定的!运行时Logcat将抛出CalledFromWrongThreadException异常错误,提示如下:

意思就是说“只有原始创建这个视图层次的线程才能修改它的视图”,本例中的TextView是在UI线程(主线程)中创建,因此,myThread线程不能修改其显示内容!

一般的做法是在子线程里获取主线程里的Handler对象,然后通过该对象向主线程的消息队列里发送消息,进行通信。

如果子线程想修改主线程的UI,可以通过发送Message给主线程的消息队列,主线程经行判断处理再对UI经行操作,具体可以参考之前的代码。

三、Handler通过runnable通信的基本方式

我们可以通过Handler的post方法实现线程间的通信,API中关于post方法说明如下

public final boolean post (Runnable r)

Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

Post方法将安排runnable对象在主线程的某个位置运行,但是并不会开启一个新的线程,验证代码如下:

 1 public class HandlerTestActivity extends Activity { 2  3     4  5     private Handler handlerTest; 6  7     Runnable runnableTest = new Runnable() 8  9     {10 11        public void run()12 13        {14 15            String runID = String.valueOf(Thread.currentThread().getId());//输出Runnable 线程的ID号16 17            Log.v("Debug",runID);18 19        }20 21     };22 23     /** Called when the activity is first created. */24 25     @Override26 27     public void onCreate(Bundle savedInstanceState) {28 29         super.onCreate(savedInstanceState);30 31         setContentView(R.layout.main);32 33         handlerTest = new Handler();34 35         String mainID = String.valueOf(Thread.currentThread().getId());36 37         Log.v("Debug",mainID);//输出主线程的ID号38 39         handlerTest.post(runnableTest);40 41     }42 43 }

Logcat里输出如下:

说明只是把runnable里的run方法放到UI线程里运行,并不会创建新线程

因此我们可以在子线程中将runnable加入到主线程的MessageQueue,然后主线程将调用runnable的方法,可以在此方法中更新主线程UI。

将之前的代码修改如下:

 1 public class HandlerTestActivity extends Activity { 2  3     private Handler handlerTest; 4  5     private TextView tv; 6  7     /** Called when the activity is first created. */ 8  9     @Override10 11     public void onCreate(Bundle savedInstanceState) {12 13         super.onCreate(savedInstanceState);14 15         setContentView(R.layout.main);16 17         tv = (TextView)findViewById(R.id.tv);//TextView初始化为空18 19         handlerTest = new Handler();20 21         myThread myT = new myThread();22 23         myT.start();//开启子线程24 25     }26 27  28 29     class myThread extends Thread{30 31     public void run(){32 33         handlerTest.post(runnableTest);//子线程将runnable加入消息队列34 35     }36 37     }38 39    40 41     Runnable runnableTest = new Runnable()42 43     {44 45         public void run()46 47        {48 49            tv.setText("此信息由子线程输出!");50 51        }52 53     };54 55 }

Android线程间通信机制相关推荐

  1. android线程间通信的几种方法_Android线程间通信机制

    讲解Handler机制的博文很多,我也看了很多,但说实话,在我对Handler几乎不怎么了解的情况下,每一篇文章我都没太看懂,看完之后脑子里还是充满了疑问.究其原因,是因为几乎每一篇文章一上来就开始深 ...

  2. Android线程间通信

    Android线程间通信 前言:内容主要从android线程间通信方式进行入手,首先分析了线程间通信的几种方式,再深入分析线程间通信的机制,主要分析工作线程与工作线程间通信,因为主线程和工作线程间通信 ...

  3. android线程间通信的几种方法_Android 技能图谱学习路线

    Java基础 Java Object类方法 HashMap原理,Hash冲突,并发集合,线程安全集合及实现原理 HashMap 和 HashTable 区别 HashCode 作用,如何重载hashC ...

  4. android线程间通信的几种方法_Android进程间和线程间通信方式

    进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位 ...

  5. Java多线程编程-(4)-线程间通信机制的介绍与使用

    上一篇: Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-线程本地Th ...

  6. Android线程间通信之handler

    本文来一起讨论下Android的handler机制. 相信写过android的童鞋,一定对handler很熟悉.因为使用频率实在太高了.尤其是在非ui线程,想要刷新ui控件的时候.因为ui控件的刷新只 ...

  7. Android线程间通信的几种实现方式

    1. 通过Handler机制: private void one() {handler=new Handler(){@Overridepublic void handleMessage(Message ...

  8. android线程间通信的四种实现方式

    1,通过Handler机制. private void one() {handler=new Handler(){@Overridepublic void handleMessage(Message ...

  9. android 线程间通信几种方式

    1.共享变量(内存) 2.管道 3.handle机制 runOnUiThread(Runnable) view.post(Runnable)

最新文章

  1. android 访问http地址吗,浅谈android访问http原理
  2. IBM 2019-2020数据科学校招
  3. 网站优化之如何提升快照的更新时间?
  4. golang包math/rand使用示例
  5. sklearn模型的训练(上)
  6. 原型模式codeing
  7. P1131 [ZJOI2007] 时态同步
  8. STL源码剖析 迭代器的概念和traits编程技法
  9. Git 几个重要操作指令对比
  10. caffeine 淘汰策略
  11. 纯VB代码取得硬盘的物理序列号
  12. 怎么虚拟机做成服务器,虚拟机怎么做成服务器
  13. java 高级工程师面试题集锦,持续更新~
  14. 2020-12-10 PMP 群内练习题 - 光环
  15. Domain Driven Design 领域驱动设计
  16. 史上最给力客厅风水 让你躲过血光之灾
  17. 多智能体强化学习:合作关系设定下的多智能体强化学习
  18. Tiled Based Deferred Shading与Forward+
  19. css一些零零散散的问题
  20. 以下选项中不是python对文件的写操作方法的是_以下选项中,不是 Python 对文件的读操作方法的是...

热门文章

  1. 第三章 运算方法与运算器
  2. linux下禁用或者启用独显(Nvidia)-使用BBSwitch
  3. android将图片宽高定死,Android 图片裁剪功能实现详解(类似QQ自定义头像裁剪) - 酷_莫名简单、KNothing - 51CTO技术博客...
  4. # Python 字符串数据(二进制编码数据)格式转换及解析
  5. C#---CSkin
  6. Python学习系列之错误和异常
  7. 一首好听的音乐推荐给大家.《Scarborough Fair》.
  8. 02《需求模式——软件建模与分析》
  9. 【简单】果果的ATM取款机
  10. saiku java_saiku3.8二次开发项目搭建(非maven)