Android中的线程机制(Handler Looper)(二)

来源:互联网 发布:指南针全赢数据 编辑:程序博客网 时间:2024/05/27 00:39

在上一篇中我们通过handler的发送消息方法实现了计时器的功能。在子线程中发送更新消息,主线程中来处理消息。那么是不是只能是主线程处理消息呢?其他线程要想处理消息又该如何实现呢?

实际上:消息发送和计划任务提交之后,它们都会进入某线程的消息队列中,我们可以把这个线程称之为目标线程。不论是主线程还是子线程都可以成为目标线程。上例中之所以在主线程中处理消息,是因为我们要更新UI,按照Android中的规定我们必须由主线程更新UI。所以我们让主线程成为了目标线程。

那么如何控制让某个线程成为目标线程呢?

这就引出了Looper的概念。Android系统中实现了消息循环机制,Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。Android系统中的通过Looper帮助线程维护着一个消息队列和消息循环。通过Looper.myLooper()得到当前线程的Looper对象,通过Looper.getMainLooper()得到当前进程的主线程的Looper对象。如Looper中的部分源码:

[java] view plain copy
print?
  1. public class Looper {  
  2.     private static final boolean DEBUG = false;  
  3.     private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;  
  4.   
  5.     // sThreadLocal.get() will return null unless you've called prepare().  
  6.     private static final ThreadLocal sThreadLocal = new ThreadLocal();  
  7.   
  8.     final MessageQueue mQueue;  
  9.     volatile boolean mRun;  
  10.     Thread mThread;  
  11.     private Printer mLogging = null;  
  12.     private static Looper mMainLooper = null;  
  13.       
  14.      /** Initialize the current thread as a looper. 
  15.       * This gives you a chance to create handlers that then reference 
  16.       * this looper, before actually starting the loop. Be sure to call 
  17.       * {@link #loop()} after calling this method, and end it by calling 
  18.       * {@link #quit()}. 
  19.       */  
  20.     public static final void prepare() {  
  21.         if (sThreadLocal.get() != null) {  
  22.             throw new RuntimeException("Only one Looper may be created per thread");  
  23.         }  
  24.         sThreadLocal.set(new Looper());  
  25.     }  
  26. public static final void loop() {  
  27.         Looper me = myLooper();  
  28.         MessageQueue queue = me.mQueue;  
  29.         while (true) {  
  30.             Message msg = queue.next(); // might block  
  31.             //if (!me.mRun) {  
  32.             //    break;  
  33.             //}  
  34.             if (msg != null) {  
  35.                 if (msg.target == null) {  
  36.                     // No target is a magic identifier for the quit message.  
  37.                     return;  
  38.                 }  
  39.                 if (me.mLogging!= null) me.mLogging.println(  
  40.                         ">>>>> Dispatching to " + msg.target + " "  
  41.                         + msg.callback + ": " + msg.what  
  42.                         );  
  43.                 msg.target.dispatchMessage(msg);  
  44.                 if (me.mLogging!= null) me.mLogging.println(  
  45.                         "<<<<< Finished to    " + msg.target + " "  
  46.                         + msg.callback);  
  47.                 msg.recycle();  
  48.             }  
  49.         }  
  50.     }  


前面提到每个线程都可以有自己的消息队列和消息循环,然而我们自己创建的线程默认是没有消息队列和消息循环的(及Looper),要想让一个线程具有消息处理机制我们应该在线程中先调用Looper.prepare()来创建一个Looper对象,然后调用Looper.loop()进入消息循环。如上面的源码所示。示例代码如下:

[java] view plain copy
print?
  1. class LooperThread extends Thread {  
  2.        public Handler mHandler;  
  3.          
  4.        public void run() {  
  5.            Looper.prepare();  
  6.              
  7.            mHandler = new Handler() {  
  8.               public void handleMessage(Message msg) {  
  9.                    // process incoming messages here  
  10.                }  
  11.            };  
  12.              
  13.            Looper.loop();  
  14.        }  

当我们用Handler的构造方法创建Handler对象时,指定handler对象与哪个具有消息处理机制的线程(具有Looper的线程)相关联,这个线程就成了目标线程,可以接受消息和计划任务了。Handler中的构造方法如下:

[java] view plain copy
print?
  1. public Handler() {  
  2.        if (FIND_POTENTIAL_LEAKS) {  
  3.            final Class<? extends Handler> klass = getClass();  
  4.            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                    (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                    klass.getCanonicalName());  
  8.            }  
  9.        }  
  10.   
  11.        mLooper = Looper.myLooper();  
  12.        if (mLooper == null) {  
  13.            throw new RuntimeException(  
  14.                "Can't create handler inside thread that has not called Looper.prepare()");  
  15.        }  
  16.        mQueue = mLooper.mQueue;  
  17.        mCallback = null;  
  18.    }  
  19.   
  20. public Handler(Looper looper) {  
  21.        mLooper = looper;  
  22.        mQueue = looper.mQueue;  
  23.        mCallback = null;  
  24.    }  


在上述的计时器的例子中,之所以可以在主线程中处理消息而我们自己并没有调用Looper.prepare()等方法,是因为Android系统在Activity启动时为其创建一个消息队列和消息循环,当我们用无参的Handler构造方法创建对象时又用了当前线程的Looper对象,及将handler与主线程中的Looper对象进行了关联。

android中是使用Looper机制来完成消息循环的,但每次创建线程时都先初始化Looper比较麻烦,因此Android为我们提供了一个HandlerThread类,他封装了Looper对象,是我们不用关心Looper的开启和释放问题。

不管是主线程还是其他线程只要有Looper的线程,别的线程就可以向这个线程的消息队列中发送消息和任务。

我们使用HandlerThread类代替上一篇文章中的子线程,并用HandlerThread类中的Looper对象构造Handler,则接受消息的目标线程就不是主线程了,而是HandlerThread线程。代码如下:

[java] view plain copy
print?
  1. public class clockActivity extends Activity {  
  2.     /** Called when the activity is first created. */  
  3.     private String TAG="clockActivity";  
  4.     private Button endButton;  
  5.     private TextView textView;  
  6.     private int timer=0;  
  7.     private boolean isRunning=true;  
  8.     private Handler handler;  
  9.     @Override  
  10.     public void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         setContentView(R.layout.main);  
  13.         endButton=(Button)findViewById(R.id.endBtn);  
  14.         textView=(TextView)findViewById(R.id.textview);  
  15.         endButton.setOnClickListener(new View.OnClickListener() {  
  16.             @Override  
  17.             public void onClick(View v) {  
  18.                 // TODO Auto-generated method stub  
  19.                 isRunning=false;  
  20.             }  
  21.         });  
  22.         HandlerThread thread=new HandlerThread("myThread");  
  23.         handler=new Handler(thread.getLooper());//与HandlerThread中的Looper对象关联  
  24.         thread.start();  
  25.         Runnable r=new Runnable(){  
  26.   
  27.             @Override  
  28.             public void run() {  
  29.                 // TODO Auto-generated method stub  
  30.                 if(isRunning){  
  31.                     textView.setText("走了"+timer+"秒");  
  32.                     timer++;  
  33.                     handler.postDelayed(this1000);//提交任务r,延时1秒执行  
  34.                 }  
  35.             }  
  36.         };  
  37.         handler.postDelayed(r, 1000);  
  38.         }  
  39. }  

此时处理任务会在handlerThread线程中完成。当然这个例子会出线异常:依然是因为在非主线程中更新了UI。这样做只是为了大家能够理解这种机制。

深入理解Android消息处理机制对于应用程序开发非常重要,也可以让我们对线程同步有更加深刻的认识,希望这篇文章可以对朋友们有所帮

 

0 0