《Android进阶》之第三篇 深入理解Handler

Handler相关说明:

A Handler allows you to send and process Message and Runnable objects associated with a thread‘s MessageQueue. Each Handler instance is associated with a single thread and that thread‘s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue. 
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own. 

类似的还有一个类:

android.os

Class HandlerThread

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

 

解释:安卓的UI线程(即OnCreate函数创建的线程)是线程非安全的。也就是说,在UI线程中,使用sleep这样的函数会导致整个线程延迟,但是我们在安卓开发中,往往会经常遇到一些延迟比较厉害的操作,(例如通过HTTP获取数据信息)如果放在主线程中,则会影响UI界面的渲染。但是如果另外新开一个线程,则由于UI线程只能在主线程中修改,而导致无法修改主线程的UI界面。这个时候Handler就出来解决这个问题。

handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制定一些操作的顺序。

Handler主要两大作用:

1. 提供post操作。post操作主要将Runnable对象放进主线程(UI)线程中的队列中操作。post还支持延迟操作。使用post后,Runnable是按照队列的形式逐个执行的。

2. handlerMessage操作。主要用于新开一个线程与主线程中的通信。新开的线程执行完毕后,可以通过sendMessage给主线程发送消息,并且传递一些参数,然后主线程就可以修改UI界面了。

 

Handler提供的函数:

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

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

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

sendEmptyMessage(int)

sendMessage(Message)

 sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

 

应用实例:

   1,传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI

          在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,用Handler中的handlerMessge方法处理传过来的数据信息,并操作UI。类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Messgae并进行相关操作。

 2,传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。

Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。

另外,Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。而Runnable是一个接口,Thread是Runnable的子类。所以说,他俩都算一个进程。xml文件:

技术分享View Code

 

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


public class MainActivity extends Activity {  
       
    private String TAG = "MainActivity";
      //声明两个按钮控件  
     private Button startButton = null;  
      private Button endButton = null;  
      @Override 
      public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
          setContentView(R.layout.activity_main);  
        //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器  
        startButton = (Button)findViewById(R.id.startButton);  
        startButton.setOnClickListener(new StartButtonListener());  
        endButton = (Button)findViewById(R.id.endButton);  
        endButton.setOnClickListener(new EndButtonListener());  
        Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); 
           
     }  
     class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //调用Handler的post方法,将要执行的线程对象添加到队列当中  
            handler.post(updateThread);  
         }  
           
     }  
       
     class EndButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
            handler.removeCallbacks(updateThread);  
         }  
           
   }  
    //创建一个Handler对象  
    Handler handler  = new Handler();
    //将要执行的操作写在线程对象的run方法当中  
    Runnable updateThread =  new Runnable(){  
  
         @Override 
        public void run() {  
            Log.i(TAG,"UpdateThread");  
            Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); 
             //在run方法内部,执行postDelayed或者是post方法  
            handler.postDelayed(updateThread, 3000);  
        }  
           
    };  
 } 

 

点击start后,程序的运行结果就是每隔3秒钟,就会在控制台打印一行UpdateTread。这是因为实现了Runnable接口的updateThread对象进入了空的消息队列即被立即执行run方法,而在run方法的内部,又在3000ms之后将其再次发送进入消息队列中。

注意这种方法创建Handler对象并不需要重写handlerMessage方法。

技术分享

从输出结果能看出来:

   post方法虽然发送的是一个实现了Runnable接口的类对象,但是它并非创建了一个新线程,而是执行了该对象中的run方法。也就是说,整个run中的操作和主线程处于同一个线程。这样对于那些简单的操作,似乎并不会影响。但是对于耗时较长的操作,就会出现“假死”。为了解决这个问题,就需要使得handler绑定到一个新开启线程的消息队列上,在这个处于另外线程的上的消息队列中处理传过来的Runnable对象和消息。Runnable对象只是作为一个封装了操作的对象被传递,并未产生新线程。

下面这种写法也是可以的:

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {
    
     TestThread t = null;
     private String TAG = "MainActivity";
      //声明两个按钮控件  
     private Button startButton = null;  
      private Button endButton = null;  
      @Override 
      public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         setContentView(R.layout.activity_main);  
        //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器  
        startButton = (Button)findViewById(R.id.startButton);  
        startButton.setOnClickListener(new StartButtonListener());  
        endButton = (Button)findViewById(R.id.endButton);  
        endButton.setOnClickListener(new EndButtonListener());  
        Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); 
        t= new TestThread(1);
           
     }  
     class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //调用Handler的post方法,将要执行的线程对象添加到队列当中  
            handler.post(t);  
         }  
           
     }  
           
     class EndButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
            handler.removeCallbacks(t);  
         }  
           
   }  
    //创建一个Handler对象  
    Handler handler  = new Handler();
    
    class TestThread extends Thread{
            int prime;
            TestThread(int prime) {
                this.prime = prime;
            }
        @Override
        public void run() {
                 //在run方法内部,执行postDelayed或者是post方法  
                   handler.postDelayed(t, 3000);  
                   Log.i(TAG,"TestThread-->" + Thread.currentThread().getId()); 
            } 
        }            
 } 

 

技术分享

虽然创建了一个Thread,但是并没有执行Thread的start()方法。考虑到Thread和Runnable之间的关系,上面的两种代码并无实质差别,所以logcat中甚至都没出现启动新线程的日志。

然而,如果稍加修改:加上启动方法

  class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //调用Handler的post方法,将要执行的线程对象添加到队列当中  
            handler.post(t); 
            t.start();
         }           
     }  
         

技术分享

可以明显看到,虽然启动了新线程,但post仍然可以把这个线程推到主线程里面去,线程由虚拟机自动结束。

所以,在UI线程(主线程)中:

    mHandler=new Handler();

    mHandler.post(new Runnable(){

    void run(){

       //执行代码..

     }

    });

 这个线程其实是在UI线程之内运行的,并没有新建线程。

 

    常见的新建线程的方法是:参考J2SE文档的

   1、

 class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
  }


  • The following code would then create a thread and start it running:

     

         PrimeThread p = new PrimeThread(143);
         p.start();
    

   2、

   class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
  • The following code would then create a thread and start it running:

     

         PrimeRun p = new PrimeRun(143);
         new Thread(p).start();
    

尽量按照上面给出的两种方式做,不要受网上影响简单的从Threa创建,那样不能做到传递参数。

static void sleep(long millis)
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.

代码验证:

 

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


public class MainActivity extends Activity {  
       
    private String TAG = "MainActivity";
      //声明两个按钮控件  
    private Button startButton = null;  
    private Button endButton = null; 
    TestThread t = null;
    int flag = 0;
      @Override 
      public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
          setContentView(R.layout.activity_main);  
        //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器  
        startButton = (Button)findViewById(R.id.startButton);  
        startButton.setOnClickListener(new StartButtonListener());  
        endButton = (Button)findViewById(R.id.endButton);  
        endButton.setOnClickListener(new EndButtonListener());  
        Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); 
        
        t= new TestThread(1);
           
     }  
     class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //调用Handler的post方法,将要执行的线程对象添加到队列当中  
             t.start();
         }  
           
     }  
       
     class EndButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             handler.sendEmptyMessage(33);
             flag = 5;
         }  
           
   }  
     
     
     class TestThread extends Thread{
         int prime;
         TestThread(int prime) {
             this.prime = prime;
         }
        @Override
        public void run() {
             //在run方法内部,执行postDelayed或者是post方法             
            try {
                while(true) {
                    Log.i(TAG,"TestThread-->" + Thread.currentThread().getId()); 
                    //handler.sendEmptyMessageDelayed(22,3000);
                    Thread.sleep(3000);
                    handler.sendEmptyMessage(22);
                    if(flag  == 5)  //线程最佳的退出方法,就是自杀,也就是在线程的函数里面自然的return 出来
                        return;
                }             
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
         
     }
    //创建一个Handler对象  
    Handler handler  = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            switch(msg.what) {
                case 22:
                    Log.i(TAG,"StartButton");  
                    Log.i(TAG,"Handler-->" + Thread.currentThread().getId()); 
                    break;
                case 33:
                    Log.i(TAG,"EndButton");  
                    Log.i(TAG,"Handler-->" + Thread.currentThread().getId()); 
                    break;
            }
                    
        }
        
    };

 } 

技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。