17_Android的通信机制Handler


目录

1.什么是Handler、Looper、MessageQueue

2.Handler、Looper和MessageQueue的基本原理

3.通过Handler实现线程通信:(子线程向主线程传输)

4.通过Handler实现线程通信:(主线程向子线程传输)

5.对于Handler-Looper-MessageQueue-线程关系的Android源码分析

 注意:对于上面匿名内部类的说明


1.什么是Handler、Looper、MessageQueue

Handler:就是将消息放入队列的机制。我们在哪个线程中创新handler,handler就将消息放入所在的线程,除非在创建handler对象时是指定具体的线程。通常handler在Main Theread创建。

Looper:相当于消息的载体,它的内部有一个消息队列,也就是MessageQueue,Handler发送的所有消息都会走向这个消息队里。

MessageQueue:就是一个消息队列

2.Handler、Looper和MessageQueue的基本原理

 

一个简单例子阐述Handler、Looper和MessageQueue之间的关系:

实验步骤:

 1.当用户点下按钮后,就会创建一个消息对象,并且给这个消息对象的what属性赋值为2
 2.然后调用handler的sendMessage方法,把msg对象发送到消息队列(MessageQueue)去
 3 当消息队列有数据之后,Looper就会从消息队列中将消息对象取出
 4.Looper将会找到与消息对象对应的Handler对象(生成消息对象msg的handler(handler 是Handler的引用类型))
 5.Looper将会调用Handler对象的handlerMessage(Message,msg)方法,用于处理消息对象

新建一个应用程序,命名为:S17_Handler1

在布局文件增加一个 按钮和一个TextView控件;

声明引用,获取控件的标签用对象表示,实现监听器接口,为button绑定监听器:

 

实现监听器接口,并在onClick方法,编写按钮按下时需要做的工作,然后继承Handler,生成Handler子类的对象

两个小提示:

1.继承Handler时,需要导入包可以用快捷键,注意导入正确的包

2.handlerMessage方法是复写Handler的: 

MainActivity.java源码:

package com.yuan.s17_handler1;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
	//声明引用
	private TextView textView;
	private Button button;
	private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取控件的标签用对象表示
        button =(Button)findViewById(R.id.buttonId);
        textView =(TextView)findViewById(R.id.textViewId);
        handler = new FirstHandler(); 
        //生成监听器对象,为button绑定监听器
        OnButtonListener listener =new OnButtonListener();
        button.setOnClickListener(listener);
    }
    //当用户点下按钮后,就会创建一个消息对象,并且给这个消息对象的what属性赋值为2
    //然后调用handler的sendMessage方法,把msg对象发送到消息队列去
    //当消息队列有东西之后,Looper就会从消息队列中将消息对象取出
    //Looper将会找到与消息对象对应的Handler对象
    //Looper将会调用Handler对象的handlerMessage(Message,msg)方法,用于处理消息对象
    class OnButtonListener implements OnClickListener{

		@Override
		public void onClick(View v) {
			//当用户点击按钮时,创建一个消息对象,并使用Handler发送该对象
			//创建消息对象
			Message msg = handler.obtainMessage();//每个消息对象,都有与之对应的Handler对象
			//调用消息对象的属性 what,赋值为2
			msg.what =2;
			//调用Handler方法,把消息对象放入到消息队列当中去
			handler.sendMessage(msg);
		}	
    }
    //也就是调用这个对象的的HandlerMessage方法
    class FirstHandler extends Handler{
		@Override
		public void handleMessage(Message msg) {
			int what = msg.what;
			textView.setText("what:"+what);
		}
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}

 

 试着运行这个应用程序:

 

 可能看过上面的例子会有点疑惑,为什么通过Handler机制把数据存入消息队列,然后通过在Looper把数据取出来,然后又把送回去给Handler对象去解决呢,这么麻烦,Handler自己去解决了不就行了吗?

 答案就在于线程线程间通讯里,其实我们要解决的正是线程通信里面的一些问题,看完下面这些可能就会解惑了:


3.通过Handler实现线程通信:(子线程向主线程传输)


 

例子:

实现目标:点击发送消息按钮后启动一个线程,让这个线程休眠2s钟口修改TextView里面的内容,抽象例子子:

提示:我们上一节说到,在Worker Thread是不能去修改 UI的属性的,详细点击:点我查看

这次我们通过Handler通信机制去解决这个问题。

新建一个应用程序,命名为:S17_Handler2

 

布局文件代码和视图如下:

绑定按钮的监听器:

然后新建一个名为MyThread的线程,利用按钮去启动线程,详细看上一节: 点我查看

和上面一样,在主线程新建一个Handler,去接受处理与之对象的消息队列的信息,同样去打印线程的名字:

总体代码如下:

package com.yuan.s17_handler2;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends Activity {
	//声明引用
	private TextView textView;
	private Button button;
	private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取代表控件的对象
        textView =(TextView)findViewById(R.id.textViewId2);
        button =(Button)findViewById(R.id.buttonId2);
        //生成一个handler对象
        handler = new MyHandler();
        //生成监听器对象,绑定监听器
        button.setOnClickListener(new OnbuttonListener());
    }
    class MyHandler extends Handler{
		@Override
		public void handleMessage(Message msg) {
			//打印当前线程的名字
	    	System.out.println("handlerMessage-->"+ Thread.currentThread().getName());
		}
    	
    }
    class OnbuttonListener implements OnClickListener{
		@Override
		public void onClick(View v) {
			Thread ft =new NetWorkThread();
			ft.start();
		}
    }
    //模拟一个网络线程
    class NetWorkThread extends Thread{
		@Override
		public void run() {
			//打印当前线程的名字
	    	System.out.println("netWork-->"+ Thread.currentThread().getName());
			//模拟访问网络,线程运行时,休眠2s
			try {
				Thread.sleep(2*1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//变量s的值,模拟从网络当中获取的数据
			//应为这里不是UI线程,所以不能去修改UI的属性,除非特殊控件
			String s = "从网络获取的数据";
			//生成Message对象
			Message msg = handler.obtainMessage();
			//把消息发送到消息队列里面,Looper就会自动把消息取出来
			//然后用相对应的Handler对象的handleMessage去处理Message对象。
			handler.sendMessage(msg);
		}
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}

 运行这个应用程序,在没有运行之前先预测一下运行结果:应该是,进去UI界面——》按发送按钮——》就会启动一个NetWork的线程——》在线程里先打印出该线程的名字——》该线程休眠2s,模拟获取网络数据——》再生成一个Message对象——》通过handler把对象放在消息队列里面——》队列里有消息——》Looper就会把消息取出来——》把消息交给相应Handler对象的handleMessage()方法处理——》此时我们并没有去处理msg,我们只在handleMessage函数打印该线程的名字。

可以看到,和我们预想的一样,先打印NetWork线程名字(为Worker Thread),然后去执行handlerMessage的语句,打印出线程名字,发现其实是主线程(Main Thread):

CatLog的输出结果: 

所以得出一个结论:通过sendMessage()方法,无论是在主线程(Mian Thread)还是(Worker Thread)都是可以的被handleMessage(msg)收到,既然能收到,我们就可以通过这种机制去处理(work Thread)进程的数据了,也就意味着,不管你Worker Thread是否堵塞,只要不堵塞主线程,UI界面就能正常显示,一旦Work Thread处理好数据,就可以通过sendMessage()方法把数据发送到消息队列,Looper就可以去取出数据给HandlerMessage(msg)处理,又因为handleMessage(msg)是主线程的所以他可以去修改UI的属性(比如TextView等控件)。


上面我们已经解决可可以把(Work Thread)的数据发送到(Main Thread),那么就可以把可以把刚刚的 s (Sting)字符串,通过 sendMessage()这种机制把它传输出来,然后再主线程接受,在主线程调用TextView.setText()方法就可以在UI上反馈(Work Thread)线程返回的结果了。

修改NetworkThread类如下:

 修改MyHandler类如下:

运行这个应用程序:

 

 在第一个例子里,留了点疑惑,然后再这里解答,为什么不能直接通过Handler去解决,而是要通过间接解决,到这里就明白了,Handler也是在主线程,如果放在主线程执行,同样会阻塞主线程,而且最重要的一点是,问题不是Handler解决的,问题仍然是(Worker Thread)解决的,Handler只是在Main Thread 和 Worker Thread之间架了一个通讯桥梁而已。

用一个图,做一个抽象的比喻:

 


4.通过Handler实现线程通信:(主线程向子线程传输)


 

  • 如果需要主线程向子线程发送消息的话,就需要在子线程中初始化Looper,然其工作起来,上面已经了解Looper的作用,并在主线程中的Handler需要去引用子线程的Looper,在Handler,引用哪个Looper就是去处理哪个子线程的消息。

步骤:

1.新建一个WorkThread,然后在进程中准备Looper对象

2.在Worker Thread当中生成Handler对象

3.在Mian Thread中发送消息

 

例子:

新建一个应用程序,命名为:S17_Handler3

 

 

修改布局文件:

 

修改MainActivity.java程序

1.新建一个WorkThread,然后在进程中准备Looper对象

2.在Worker Thread当中生成Handler对象

 3.在Mian Thread中发送消息

整体程序:注意下面跟更正了两条语句

System.out.println("onClick---->"+Thread.currentThread().getName());

System.out.println("handleMessage---->"+Thread.currentThread().getName());

package com.yuan.s17_handler3;


import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


public class MainActivity extends Activity {

	private Button button;
	private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       //获取代表控件的对象
        button =(Button)findViewById(R.id.buttonId3);
        //创建线程对象,启动MyThread线程
        Thread ft = new MyThread();
        ft.start();
     
        //生成监听器对象,绑定监听器,使用匿名内部类方法
        button.setOnClickListener(new OnClickListener(){
			@Override  
			public void onClick(View v) {
				System.out.println("onClick---->"+Thread.currentThread().getName());
				//得到一个Message对象
				Message msg = handler.obtainMessage();
				//给msg的what属性赋值
				msg.what=10;
				//这个Message对象就会发送消息队列里
				handler.sendMessage(msg);
			}
        	
        });
    }
   
    //子线程
    class MyThread extends Thread{
		@Override
		public void run() {
			//准备Looper对象
			Looper.prepare();
			//在Work Thread生成一个Handler对象
			//使用匿名内部类实现
			handler = new Handler(){
				@Override
				public void handleMessage(Message msg) {
					System.out.println("handleMessage---->"+Thread.currentThread().getName());
					System.out.println("收到了从MainThread传来的消息:"+msg.what);
				}
			};
			//调用Looper的loop()方法之后,Looper对象不断从消息队列中取出消息对象
			//然后调用handler的handleMessage()方法处理该消息对象
			//如果消息队列中没有消息,则线程阻塞
			Looper.loop();
		}
    	
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}

运行程序:

 

结果下图所示,第一句证明了,消息是行MainThread发送出去的,第二三句证明了消息是在WorkerThread收到的,并且收到了是我们发送的消息:

 

 


5.对于Handler-Looper-MessageQueue-线程关系的Android源码分析

Android源码分析:点我查看 


 

 注意:对于上面匿名内部类的说明

1.对于什么是匿名内部类:点我查看

2.对于上面的程序使用的解释:


附录: 

本博文Demo下载:https://github.com/zGuangYuan/Android- 

github的用法:点我查看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值