Android Handler

Handler的作用:

主要接受子线程发送的数据, 并用此数据配合主线程更新UI. Android主线程 (也就是UI线程) 操作5秒钟还没有完成的话,界面会出现假死现象,会收到Android系统的一个错误提示  "强制关闭".  这个时候我们需要把这些耗时的操作,放在一个子线程中

Android不允许子线程更新主线程维护的UI组件,这样对主线程是不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的. 

Handler为android系统的线程通信工具,承担着主线程与分线程,分线程之间的通信功能,经常用到的有post(),

sendMessage() 方法,前者是将一个线程加入线程队列,后者是发送一个消息到消息队列中 

Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外

个部分逐个的在消息队列中将消息取出,然后对消息进行处理,就是发送消息和接收消息不是同步的处理。这种机制通

常用来处理相对耗时比较长的操作。

Handler的特点:

Handler绑定的有两个队列,一个为消息队列,另一个为线程队列。Handler可以通过这两个队列来分别:

发送、接受、处理消息–消息队列; 

启动、结束、休眠线程–线程队列;

Android OS中,在主线程中创建新的线程,这些新的线程都通过Handler与主线程进行通信。通信通过新线程调用

 Handler的post()方法和sendMessage()方法实现,分别对应功能:

post()  将一个线程加入线程队列; 

sendMessage() 发送一个消息对象到消息队列;

post()方法和sendMessage()还有一些变体,比如postDelayed()、postAtTime()、 sendMessageDelayed ()分别用来延

迟发送、定时发送;


SendMessage()将Message压入消息队列,当消息队列中有消息,HandleMessage()回调函数能自动回调.

Post()将线程对象压入线程队列,队列中的线程会依次弹出运行.


Handler的使用方法:

Handler 中分发消息的一些方法  

post(Runnable)  

postAtTime(Runnable,long)  

postDelayed(Runnable long)  

sendEmptyMessage(int)  

sendMessage(Message)  

sendMessageAtTime(Message,long)  

sendMessageDelayed(Message,long)

以上 post 类方法允许你排列一个 Runnable 对象到主线程队列中 

sendMessage 类方法 , 允许你安排一个带数据的 Message 对象到队列中


与Handler一起工作的组件:

Message:Handler接收和处理的消息对象

Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给发送该消息的Handler进行处理

MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建MessageQueue对象

Handler:它有两个作用--发送消息和处理消息,程序使用Handler发送消息,被Handler发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler正常工作,必须在当前线程中有一个MessageQueue。不过MessageQueue是由Looper负责管理,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象。


归纳起来,Looper,MessageQueue,Handler各自的作用如下:

Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。

MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。

Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。


为了保证当前线程中有Looper对象可以分如下两种情况处理,

  主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息,处理消息。(如下第一个程序)

  程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它。创建Looper对象调用它的prepare()方法即可。(如下第二个程序)

分别对应如下两个例子程序。

public class TestBarHandler extends Activity {
	/** Called when the activity is first created. */
	// 声明控件变量
	ProgressBar bar = null;
	Button startButton = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 根据控件的ID得到代表控件的对象,并为按钮设置监听器
		bar = (ProgressBar) findViewById(R.id.bar);
		startButton = (Button) findViewById(R.id.startButton);
		startButton.setOnClickListener(new ButtonListener());
	}

	// 当点击startButton按钮时,就会执行ButtonListener的onClick方法
	class ButtonListener implements OnClickListener {

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			bar.setVisibility(View.VISIBLE);
			updateBarHandler.post(updateThread);
		}

	}

	// 使用匿名内部类来复写Handler当中的handleMessage方法
	Handler updateBarHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			bar.setProgress(msg.arg1);
			updateBarHandler.post(updateThread);
		}

	};
	// 线程类,该类使用匿名内部类的方式进行声明
	Runnable updateThread = new Runnable() {
		int i = 0;

		@Override
		public void run() {
			System.out.println("Begin Thread");
			i = i + 10;
			// 得到一个消息对象,Message类是有Android操作系统提供
			Message msg = updateBarHandler.obtainMessage();
			// 将msg对象的arg1参数的值设置为i,用arg1和arg2这两个成员变量传递消息,优点是系统性能消耗较少
			msg.arg1 = i;
			try {
				// 设置当前显示睡眠1秒
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 将msg对象加入到消息队列当中
			updateBarHandler.sendMessage(msg);
			if (i == 100) {
				// 如果当i的值为100时,就将线程对象从handler当中移除
				updateBarHandler.removeCallbacks(updateThread);
			}
		}
	};

}

在线程中使用Handler的步骤如下:

调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。

有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自其他线程的消息。

调用Looper的Looper()方法启动Looper。

public class CalPrime extends Activity {
	static final String UPPER_NUM = "upper";
	EditText etNum;
	CalThread calThread;

	// 定义一个线程类
	class CalThread extends Thread {
		public Handler mHandler;

		public void run() {
			Looper.prepare();
			mHandler = new Handler() {
				// 定义处理消息的方法
				@Override
				public void handleMessage(Message msg) {
					if (msg.what == 0x123) {
						int upper = msg.getData().getInt(UPPER_NUM);
						List<Integer> nums = new ArrayList<Integer>();
						// 计算从2开始、到upper的所有质数
						outer: for (int i = 2; i <= upper; i++) {
							// 用i处于从2开始、到i的平方根的所有数
							for (int j = 2; j <= Math.sqrt(i); j++) {
								// 如果可以整除,表明这个数不是质数
								if (i != 2 && i % j == 0) {
									continue outer;
								}
							}
							nums.add(i);
						}
						// 使用Toast显示统计出来的所有质数
						Toast.makeText(CalPrime.this, nums.toString(),
								Toast.LENGTH_LONG).show();
					}
				}
			};
			Looper.loop();
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		etNum = (EditText) findViewById(R.id.etNum);
		calThread = new CalThread();
		// 启动新线程
		calThread.start();
	}

	// 为按钮的点击事件提供事件处理函数
	public void cal(View source) {
		// 创建消息
		Message msg = new Message();
		msg.what = 0x123;
		Bundle bundle = new Bundle();
		bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
		msg.setData(bundle);
		// 向新线程中的Handler发送消息
		calThread.mHandler.sendMessage(msg);
	}
}

如果想在主线程类中回调另一个线程类中的Handler(发送消息),只需将另一个线程类中的Handler设为public

如果想在另一个线程类中向主线程类中的Handler发送消息,只需将主线程类中的Handler对象用参数传递给另一个线程中的构造方法

例子程序如下:

主线程类:

package org.crazyit.net;

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.EditText;
import android.widget.TextView;

public class MultiThreadClient extends Activity {
	// 定义界面上的两个文本框
	EditText input;
	TextView show;
	// 定义界面上的一个按钮
	Button send;
	Handler handler;
	// 定义与服务器通信的子线程
	ClientThread clientThread;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		input = (EditText) findViewById(R.id.input);
		send = (Button) findViewById(R.id.send);
		show = (TextView) findViewById(R.id.show);
		handler = new Handler() // ①
		{
			@Override
			public void handleMessage(Message msg) {
				// 如果消息来自于子线程
				if (msg.what == 0x123) {
					// 将读取的内容追加显示在文本框中
					show.append("\n" + msg.obj.toString());
				}
			}
		};
		clientThread = new ClientThread(handler);
		// 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
		new Thread(clientThread).start(); // ①
		send.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				try {
					// 当用户按下发送按钮后,将用户输入的数据封装成Message,
					// 然后发送给子线程的Handler
					Message msg = new Message();
					msg.what = 0x345;
					msg.obj = input.getText().toString();
					clientThread.revHandler.sendMessage(msg);
					// 清空input文本框
					input.setText("");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
}
另一个线程类:
/**
 *
 */
package org.crazyit.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

public class ClientThread implements Runnable {
	private Socket s;
	// 定义向UI线程发送消息的Handler对象
	private Handler handler;
	// 定义接收UI线程的消息的Handler对象
	public Handler revHandler;
	// 该线程所处理的Socket所对应的输入流
	BufferedReader br = null;
	OutputStream os = null;

	public ClientThread(Handler handler) {
		this.handler = handler;
	}

	public void run() {
		try {
			s = new Socket("192.168.1.2", 30000);
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			os = s.getOutputStream();
			// 启动一条子线程来读取服务器响应的数据
			new Thread() {
				@Override
				public void run() {
					String content = null;
					// 不断读取Socket输入流中的内容。
					try {
						while ((content = br.readLine()) != null) {
							// 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
							Message msg = new Message();
							msg.what = 0x123;
							msg.obj = content;
							handler.sendMessage(msg);
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
			// 为当前线程初始化Looper
			Looper.prepare();
			// 创建revHandler对象
			revHandler = new Handler() {
				@Override
				public void handleMessage(Message msg) {
					// 接收到UI线程中用户输入的数据
					if (msg.what == 0x345) {
						// 将用户在文本框内输入的内容写入网络
						try {
							os.write((msg.obj.toString() + "\r\n")
									.getBytes("utf-8"));
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			};
			// 启动Looper
			Looper.loop();
		} catch (SocketTimeoutException e1) {
			System.out.println("网络连接超时!!");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值