标签:
主要接受子线程发送的数据, 并用此数据配合主线程更新UI。
解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发,比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。
如果此时需要一个耗时的操作,例如:联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。
这个时候,Handler就出现了。来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。
handler类允许你发送消息和处理线程消息队列中的消息及runnable对象。handler实例都是与一个线程和该线程的消息队列一起使用,一旦创建了一个新的handler实例,系统就把该实例与一个线程和该线程的消息队列捆绑起来,这将可以发送消息和runnable对象给该消息队列,并在消息队列出口处处理它们。
handler类有两种主要用途:1。按照时间计划,在未来某时刻,对处理一个消息或执行某个runnable实例。2。把一个对另外线程对象的操作请求放入消息队列中,从而避免线程间冲突。
时间类消息通过如下方法使用: post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)
methods.post之类函数可以传输一个runnable对象给消息队列,并在到达消息队列后被调用。sendmessage之类函数可以传送一个包含数据的message对象,该message对象可以被Handler类的handleMessage(Message) 方法所处理。
post之类函数和sendmessage之类的函数都可以指定消息的执行时机,是立即执行、稍后一段时间执行,还是在某个确定时刻执行。这可以用来实现超时、消息或其他时间相关的操作。
当一个进程启动时,主线程独立执行一个消息队列,该队列管理着应用顶层的对象(如:activities、broadcast receivers等等)和所有创建的窗口。你可以创建自己的一个线程,并通过handler来与主线程进行通信。这可以通过在新的线程中调用主线程的handler的post和sendmessage操作来实现。
HandlerThread/Looper & MessageQueue & Message的关系:
handler负责将需要传递的信息封装成Message,通过handler对象的sendMessage()来将消息传递给Looper,由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。
handler可以分发Message对象和Runnable对象到主线程中,每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:
(1): 安排消息或Runnable 在某个主线程中某个地方执行。
(2)安排一个动作在不同的线程中执行。
post(Runnable)postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post类方法允许你排列一个Runnable对象到主线程队列中,
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.
public class HandlerTest extends Activity { /** Called when the activity is first created. */ private Button startButton; private Button endButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 根据id获得控件对象 startButton = (Button) findViewById(R.id.startButton); endButton = (Button) findViewById(R.id.endButton); // 为控件设置监听器 startButton.setOnClickListener(new StartButtonListener()); endButton.setOnClickListener(new EndButtonListener()); } class StartButtonListener implements OnClickListener { public void onClick(View v) { // 调用Handler的post()方法,将要执行的线程对象放到队列当中 handler.post(updateThread); } } class EndButtonListener implements OnClickListener { public void onClick(View v) { // 调用Handler的removeCallbacks()方法,删除队列当中未执行的线程对象 handler.removeCallbacks(updateThread); } } // 创建Handler对象 Handler handler = new Handler(); // 新建一个线程对象 Runnable updateThread = new Runnable() { // 将要执行的操作写在线程对象的run方法当中 public void run() { System.out.println("updateThread"); // 调用Handler的postDelayed()方法 // 这个方法的作用是:将要执行的线程对象放入到队列当中,待时间结束后,运行制定的线程对象 // 第一个参数是Runnable类型:将要执行的线程对象 // 第二个参数是long类型:延迟的时间,以毫秒为单位 handler.postDelayed(updateThread, 3000); } }; }
一个应用程序中有一个进度条和一个按钮,当点击按钮后,每隔一秒钟进度条前进一部分。
下图为应用程序的运行效果图:
public class ProgressBarHandlerTest extends Activity { /** Called when the activity is first created. */ private ProgressBar progressBar; private Button startButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progressBar = (ProgressBar) findViewById(R.id.progressbar); startButton = (Button) findViewById(R.id.startButton); startButton.setOnClickListener(new ProgressBarOnClickListener()); } class ProgressBarOnClickListener implements OnClickListener { public void onClick(View v) { // 设置进度条为可见状态 progressBar.setVisibility(View.VISIBLE); updateBarHandler.post(updateThread); } } // 使用匿名内部类来复写Handler当中的handlerMessage()方法 Handler updateBarHandler = new Handler() { @Override public void handleMessage(Message msg) { progressBar.setProgress(msg.arg1); updateBarHandler.post(updateThread); // 将要执行的线程放入到队列当中 } }; // 线程类,该类使用匿名内部类的方式进行声明 Runnable updateThread = new Runnable() { int i = 0; public void run() { // TODO Auto-generated method stub System.out.println("Begin Thread"); i += 10; // 得到一个消息对象,Message类是android系统提供的 Message msg = updateBarHandler.obtainMessage(); // 将Message对象的arg1参数的值设置为i msg.arg1 = i; // 用arg1、arg2这两个成员变量传递消息,优点是系统性能消耗较少 try { Thread.sleep(1000); // 让当前线程休眠1000毫秒 } catch (InterruptedException ex) { ex.printStackTrace(); } // 将Message对象加入到消息队列当中 updateBarHandler.sendMessage(msg); // 如果i的值等于100 if (i == 100) { // 将线程对象从队列中移除 updateBarHandler.removeCallbacks(updateThread); } } }; }
Handler在默认情况下,实际上它和调用它的Activity是处于同一个线程的。
例如在Handler的使用(一)的示例1中,虽然声明了线程对象,但是在实际调用当中它并没有调用线程的start()方法,而是直接调用当前线程的run()方法。
通过一个例子来证实一下
一个Android应用程序,在Activity中创建Handler和线程对象,并且在Activity的onCreate()方法中输出当前线程的id和名字,然后在线程对象的run方法中也打印输出下当前线程的id和名字。如果说,Activity输出的结果与线程对象输出的结果是一样的,那么就表示它们使用的是同一个线程。
下面是Activity代码:
public class HandlerTwo extends Activity { /** Called when the activity is first created. */ Handler handler = new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 在设置布局文件之前先调用post方法, // 表示在执行完线程之后才会显示布局文件中的内容,而线程中又设置了休眠10秒钟, // 所以最终效果为,先显示应用程序主界面,等待10秒钟之后才显示布局文件中的内容 handler.post(r); setContentView(R.layout.main); System.out.println("activity id--->" + Thread.currentThread().getId()); System.out.println("activity name---> + Thread.currentThread().getName()); } Runnable r = new Runnable() { public void run() { // 输出当前线程的id和name // 如果这里输出的线程id、name与上面onCreate()方法中输出的线程id、name相同的话, // 那么则表示,他们使用的是同一个线程 System.out.println("runnable_id---> + Thread.currentThread().getId()); System.out.println("runnable_name---> + Thread.currentThread().getName()); try { Thread.sleep(10000); // 让线程休眠10秒 } catch (InterruptedException e) { e.printStackTrace(); } } }; }
根据结果可以看出,两个输出的id和name都相同,它们使用的是同一个线程。
现在将Activity中的代码修改一下,新建一个线程Thread,然后调用线程的start()方法,再观察一下控制台的输出结果。
这里只要将上面的代码稍微修改一下就可以了。
1、先将handler.post(r)注释掉。
2、再添加下面两句代码就OK了。
//handler.post(r); Thread t = new Thread(r); t.start();
从这个输出结果中可以看出,这次线程对象的id、name与activity里的线程id、name完全不一样了,由此可见,它们现在使用的不是同一个线程。
这个例子中还掩饰了一个效果,就是平时我们是将Handler的post()方法放在setContentView(R.layout.main)这个方法之后调用,将设置完布局之后再执行其他的操作,而在这个例子中,是将Handler的post()方法放在setContent()方法之前调用,而post里传递的线程对象的run()方法呢,又执行了休眠线程10秒钟,所以运行实现的效果会是,当程序运行后,首先Activity上没有任何内容,过来10秒之后,才会显示Activity里的内容。
欢迎阅读下篇:
Android 基础总结:( 十四)Handler详解(上)
标签:
原文地址:http://blog.csdn.net/wenwins/article/details/51570555