Android Handler

来源:互联网 发布:2004年3d开奖数据 编辑:程序博客网 时间:2024/06/15 05:13

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;@Overridepublic 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 {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubbar.setVisibility(View.VISIBLE);updateBarHandler.post(updateThread);}}// 使用匿名内部类来复写Handler当中的handleMessage方法Handler updateBarHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {bar.setProgress(msg.arg1);updateBarHandler.post(updateThread);}};// 线程类,该类使用匿名内部类的方式进行声明Runnable updateThread = new Runnable() {int i = 0;@Overridepublic 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 blocke.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() {// 定义处理消息的方法@Overridepublic 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();}}@Overridepublic 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;@Overridepublic 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() // ①{@Overridepublic 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() {@Overridepublic void onClick(View v) {try {// 当用户按下发送按钮后,将用户输入的数据封装成Message,// 然后发送给子线程的HandlerMessage 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() {@Overridepublic 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();// 为当前线程初始化LooperLooper.prepare();// 创建revHandler对象revHandler = new Handler() {@Overridepublic 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();}}}};// 启动LooperLooper.loop();} catch (SocketTimeoutException e1) {System.out.println("网络连接超时!!");} catch (Exception e) {e.printStackTrace();}}}


0 0
原创粉丝点击