Android线程之间的通信架构(Handler消息机制)

  • 当主线程(UI线程或ActivityThread)诞生时,就会去执行一个代码循环(Looper),以便持续监视它的信息队列(Message Queue简称MQ)。当UI事件发生了,通常会立即丢一个信息 (Message)到MQ,此时主线程就立即从MQ里面取出该信息,并且处理。
  • 例如,用户在UI画面上按下一个Button按 钮时,UI事件发生了,就会把onClick信息丢到 MQ里,于是,主线程会及时从MQ里取出onClick信息,然后调用Activity的onClick()函数去处理之。处理完毕之后,主线程又返回去继续执行信息循环,继续监视它的MQ,一直循环下去,直到主线程的生命周期的终了。 通常是进程被删除时,主线程才会被删除。
  • 即一个主线程有它自己专属的Looper对象,此线程诞生时,就会执行此对象里的信息循环。此外,一个主线程还会有其专属的MQ信息结构。如下图所示:
    在这里插入图片描述
  • 由于主线程会持续监视MQ的动态,所以在程序的任何函数,只要将信息(以Message 类别的对象表示)丢入主线程的MQ里, 就能与主线程沟通了。
  • 在Android里,定义了一个Handler类别,在程序的任何函数里,可以诞生Handler对象来将Message对象丢入MQ里,与主线程进行沟通。 Handler创建地时候会采用当前线程地Looper来构造消息循环系统,Handler内部使用ThreadLocal(可以在不同地线程中互不干扰地存储并提供数据)来获取当前线程地Looper。
  • Handler创建完毕后,这个时候其内部地Looper以及MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理。其实post方法最终也是通过send方法来完成的。当Handler的send方法被调用时,它会调用MQ的enqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handleMessage方法就会被调用。注意,Looper是运行在创建Handler所在的线程中的,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中去执行了。
  • 在Android的预设情况下,主线程诞生时,就会拥有自己的Looper对象和MQ(即 Message Queue)数据结构。
  • 然而,主线程诞生子线程时,于预设情形 下,子线程并不具有自己的Looper对象和 MQ。由于没有Looper对象,就没有信息回圈(Message Loop),一旦工作完毕了,此子线程就结束了。
  • 既然没有Looper对象也没有MQ,也就不能接受外来的Message对象了。则别的线 程就无法透过MQ来传递信息给它了。如果别的线程(如主线程)需要与子线程通讯时,就得要手动替它诞生一 个Looper对象和一个MQ。

Demo展示:

  1. 主线程丢信息给自己
package com.example.aaa;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener{

	private Button btn1,btn2;
	private TextView textView;
	private Handler  h=new Handler(){
		 public void handleMessage(Message msg) {
			 if(msg.what==1)
   				 textView.setText((String)msg.obj);
		 };
	 };;
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main2);
		
		 btn1=(Button) findViewById(R.id.btn1);
		 btn2=(Button) findViewById(R.id.btn2);
		 textView=(TextView) findViewById(R.id.textview);
		 btn1.setOnClickListener(this);
		 btn2.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
		case R.id.btn1:
			h.removeMessages(1);
			Message m=h.obtainMessage(1, "this is my message");
			h.sendMessage(m);
			break;
		case R.id.btn2:
			this.finish();
		default:
			break;
		}
	}
}

运行结果:
在这里插入图片描述

  1. 子线程丢信息给主线程
package com.example.aaa;


import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener{

	private Button btn1,btn2;
	private TextView textView;
	private Timer timer=new Timer();
	private int k=0;
	private Handler  h=new Handler(){
		 public void handleMessage(Message msg) {
			 if(msg.what==1)
   				 textView.setText((String)msg.obj);
		 };
	 };;
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main2);
		
		 btn1=(Button) findViewById(R.id.btn1);
		 btn2=(Button) findViewById(R.id.btn2);
		 textView=(TextView) findViewById(R.id.textview);
		 btn1.setOnClickListener(this);
		 btn2.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
		case R.id.btn1:
			TimerTask timerTask=new TimerTask() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					h.removeMessages(1);
					Message m=h.obtainMessage(1, Thread.currentThread().getName()+":"+String.valueOf(k++));
					h.sendMessage(m);
				}
			};
			timer.schedule(timerTask, 500, 1500);
			break;
		case R.id.btn2:
			this.finish();
		default:
			break;
		}
	}
}

运行结果:
在这里插入图片描述

  1. 替子线程诞生Looper和MQ
package com.example.aaa;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener{

	private Button btn1,btn2,btn3;
	private TextView textView;
	private Thread t;
	private Handler h;
	private String str;
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main2);
		
		 btn1=(Button) findViewById(R.id.btn1);
		 btn2=(Button) findViewById(R.id.btn2);
		 btn3=(Button) findViewById(R.id.btn3);
		 textView=(TextView) findViewById(R.id.textview);
		 btn1.setOnClickListener(this);
		 btn2.setOnClickListener(this);
		 btn3.setOnClickListener(this);
		 t=new Thread(new Task());
		 t.start();
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
		case R.id.btn1:
			h.removeMessages(1);
			Message m=h.obtainMessage(1, Thread.currentThread().getName()+":"+String.valueOf(k++));
			h.sendMessage(m);

			break;
		case R.id.btn2:
			btn2.setText(str);
			break;
		case R.id.btn3:
			h.getLooper().quit();
			finish();
			break;
		}
	}
	class Task implements Runnable{
		@Override
		public void run() {
			// TODO Auto-generated method stub
			Looper.prepare();
			h=new Handler(){
				 public void handleMessage(Message msg) {
					 if(msg.what==1)
		   				 str=Thread.currentThread().getName()+",value="+String.valueOf(msg.what);
				 };
			 };
			 Looper.loop();
		}
	}
}

运行结果:
在这里插入图片描述

MQ的工作原理

  • MQ主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往MQ中插入一条消息,而next的作用是从MQ中取出一条消息并将其从MQ中移除。MQ内部实现是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。
  • next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。

Looper的工作原理

  • Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MQ中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。在构造方法中它会创建一个MQ,然后将当前线程地对象保存起来。
  • Looper除了prepare方法外,还提供了prepareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的,其本质也是通过prepare方法来实现的。由于主线程的Looper比较特殊,所以Looper提供了一个getMainLooper方法,通过它可以在任何地方获取到主线程的Looper。Looper也是可以退出的,Looper提供了quit和quitSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待状态,而如果退出Looper以后,这个线程就会立刻终止,因此建以不需要的时候终止Looper。
  • Looper的loop方法是一个死循环,唯一跳出循环的方式是MQ的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MQ的quit或quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。也就是说,Looper必须退出。否则loop方法就会无限循环下去。loop方法会调用MQ的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MQ的next方法返回了新消息,Looper就会处理这条消息:msg.target,dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到指定地线程中去执行了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值