浅析Handler、Looper机制。通过自定义Handler、Looper,让你有更直观的了观!

转载请注明出处:http://blog.csdn.net/liu470368500/article/details/40625333

Handler、Looper。面试的时候被问到的机率非常高的一个问题。当然。我说的是像我们这样的初级。。。一般讲解Handler、Looper机制的都是通过源码去讲解。这里我来通过自定义Handler、Looper。让各位看官能有个更直观的了解。相信有了这篇博文的基础。再看Handler、Looper的源码。理解起来就更容易了。


为了与安卓原生的相接轨。这里自定义的Handler-Looper使用的逻辑基本与系统原生的相一致。


先贴MyHanlder代码:


[java] view plain copy
  1. /** 
  2.  * 自定义Handler 
  3.  *  
  4.  * @author lzh 
  5.  *  
  6.  */  
  7. public class MyHandler {  
  8.     // 用于进行线程间通信的阻塞队列  
  9.     private BlockingQueue<MyMessage> mQueue;  
  10.     // 处理消息的回调  
  11.     private CallBack callBack;  
  12.   
  13.     public MyHandler(CallBack callBack) {  
  14.         super();  
  15.         MyLooper looper = MyLooper.myLooper();  
  16.         if (looper == null) {  
  17.             throw new RuntimeException(  
  18.                     "在新开的线程中。创建MyHandler对象需要先调用MyLooper.prepare()方法。");  
  19.         }  
  20.         mQueue = looper.mQueue;  
  21.         this.callBack = callBack;  
  22.     }  
  23.   
  24.     /** 
  25.      * 消息接收的回调 
  26.      *  
  27.      * @author Administrator 
  28.      *  
  29.      */  
  30.     public interface CallBack {  
  31.         /** 
  32.          * 处理消息 
  33.          *  
  34.          * @param msg 
  35.          */  
  36.         void handleMessage(MyMessage msg);  
  37.     }  
  38.   
  39.     /** 
  40.      * 发送消息 
  41.      *  
  42.      * @param msg 
  43.      */  
  44.     public void sendMessage(MyMessage msg) {  
  45.         msg.target = this;  
  46.         try {  
  47.             mQueue.put(msg);  
  48.         } catch (InterruptedException e) {  
  49.         }  
  50.     }  
  51.   
  52.     /** 
  53.      * 派发消息 
  54.      *  
  55.      * @param msg 
  56.      */  
  57.     public void dispatchMessage(MyMessage msg) {  
  58.         callBack.handleMessage(msg);  
  59.     }  
  60.   
  61. }  

再看MyLooper的代码:


[java] view plain copy
  1. public class MyLooper {  
  2.   
  3.     private static ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<MyLooper>();  
  4.     private static MyLooper myLooper;  
  5.     /** 一个线程对应一个阻塞队列。 */  
  6.     public BlockingQueue<MyMessage> mQueue = null;  
  7.   
  8.     private MyLooper() {  
  9.         super();  
  10.         mQueue = new LinkedBlockingQueue<MyMessage>();  
  11.     }  
  12.   
  13.     /** 
  14.      * 为本线程准备对应的MyLooper对象 
  15.      */  
  16.     public static void prepare() {  
  17.         if (sThreadLocal.get() != null) {  
  18.             throw new RuntimeException(  
  19.                     "Only one MyLooper may be created per thread");  
  20.         }  
  21.         sThreadLocal.set(new MyLooper());  
  22.     }  
  23.   
  24.     /** 
  25.      * 获取当前线程相对应的Looper对象 
  26.      *  
  27.      * @return 当未调用prepare()方法时。ThreadLocal.get()方法返回的为null; 
  28.      */  
  29.     public static MyLooper myLooper() {  
  30.         return sThreadLocal.get();  
  31.     }  
  32.   
  33.     /** 
  34.      * 这里启动消息循环 
  35.      */  
  36.     public static void loop() {  
  37.         while (true) {  
  38.             try {  
  39.                 myLooper = myLooper();  
  40.                 BlockingQueue<MyMessage> mQueue = myLooper.mQueue;  
  41.                 // take()方法是个阻塞方法。线程运行到此会阻塞住。以准备接收发过来的消息  
  42.                 MyMessage msg = mQueue.take();  
  43.                 msg.target.dispatchMessage(msg);  
  44.             } catch (InterruptedException e) {  
  45.                 // 当线程关闭的时候会出现此异常。此时退出循环  
  46.                 return;  
  47.             }  
  48.         }  
  49.     }  
  50.   
  51. }  

本来安卓原生中使用的是MessageQueue。但是却死活创建不出来。这里只有使用BlockQueue代替了。


中的ThreadLocal可能有部分朋友有点陌生。这是线程局部变量。它的set方法和get()方法比较有意思。是和线程相关的。你在哪个线程里面set变量进去。你在哪个线程里面get()出来的就是哪个。所以在MyLooper中得先调用prepare()方法。先将与此线程相关的MyLooper实例创建出来加入进去。这样便能保存一个线程只有一个Looper。相应的也只有一个阻塞队列。


接下来看MyMessage代码:


[java] view plain copy
  1. public class MyMessage {  
  2.     public int msg1;  
  3.     public int msg2;  
  4.     public int what;  
  5.     public Object obj;  
  6.     public MyHandler target;  
  7.     public Runnable runnable;  
  8.   
  9. }  

生的Message由于是final标记的。而Message里面存的Handler对象又比较重要。得要依靠它来指定最终的消息应该发送给哪个Handler来接收。所以。这个也自定义了。


下面开始来测试。由于安卓不允许在UI线程中有阻塞操作。所以这里我们使用SurfaceView在子线程中画图来测试是否可进行线程间通信。


[java] view plain copy
  1. /** 
  2.  * 测试自定义的Handler与Looper的测试工程,由于内部有阻塞队列。而安卓的机制是不允许此类的阻塞行为在主线程中出现。 
  3.  * 所以此处用SurfaceView在子线程中进行测试 
  4.  *  
  5.  * @author Administrator 
  6.  *  
  7.  */  
  8. public class MainActivity extends Activity {  
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(new MySurfaceView(this));  
  14.     }  
  15.   
  16.     class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,  
  17.             CallBack {  
  18.         private static final String TAG = "MySurfaceView";  
  19.         private SurfaceHolder mHolder;  
  20.         Thread mThread;  
  21.         MyHandlerCreateRunnable mRunnable;  
  22.         private Paint mPaint;  
  23.         private MyHandler handler = null;  
  24.   
  25.         public MySurfaceView(Context context) {  
  26.             super(context);  
  27.             // 初始化holder:  
  28.             mHolder = getHolder();  
  29.             mHolder.addCallback(this);  
  30.             mRunnable = new MyHandlerCreateRunnable();  
  31.   
  32.             mPaint = new Paint();  
  33.             mPaint.setColor(Color.BLACK);  
  34.             mPaint.setAntiAlias(true);  
  35.             mPaint.setTextAlign(Align.CENTER);  
  36.             mPaint.setTextSize(45);  
  37.             new Thread(mRunnable).start();  
  38.         }  
  39.   
  40.         public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {  
  41.             super(context, attrs, defStyle);  
  42.         }  
  43.   
  44.         public MySurfaceView(Context context, AttributeSet attrs) {  
  45.             super(context, attrs);  
  46.         }  
  47.   
  48.         @Override  
  49.         public void surfaceCreated(SurfaceHolder holder) {  
  50.             Log.d(TAG, "==surfaceCreated==");  
  51.             // mQueue = new LinkedBlockingQueue<String>();  
  52.             new Thread(new MyTimerRunnable()).start();  
  53.         }  
  54.   
  55.         @Override  
  56.         public void surfaceChanged(SurfaceHolder holder, int format, int width,  
  57.                 int height) {  
  58.             Log.d(TAG, "==surfaceChanged==");  
  59.         }  
  60.   
  61.         @Override  
  62.         public void surfaceDestroyed(SurfaceHolder holder) {  
  63.             Log.d(TAG, "==surfaceDestroyed==");  
  64.         }  
  65.   
  66.         @Override  
  67.         public void handleMessage(MyMessage msg) {  
  68.             synchronized (mHolder) {  
  69.                 Canvas mCanvas = null;  
  70.                 System.out.println("==lockCanvas==");  
  71.                 mCanvas = mHolder.lockCanvas();// 锁定画布。之后就可以在此画布上画图了。  
  72.                 String content = (String) msg.obj;  
  73.                 mCanvas.drawColor(Color.WHITE);  
  74.                 mCanvas.drawText(content, 100400, mPaint);  
  75.                 mHolder.unlockCanvasAndPost(mCanvas);//  
  76.             }  
  77.         }  
  78.   
  79.         /** 
  80.          * 定时发送消息的线程 
  81.          *  
  82.          * @author Administrator 
  83.          *  
  84.          */  
  85.         class MyTimerRunnable implements Runnable {  
  86.             int index = 0;  
  87.   
  88.             @Override  
  89.             public void run() {  
  90.                 while (true) {  
  91.                     MyMessage msg = new MyMessage();  
  92.                     msg.obj = "这是第" + index + "个";  
  93.                     index++;  
  94.                     handler.sendMessage(msg);  
  95.                     if (index >= 50) {  
  96.                         break;  
  97.                     }  
  98.                     try {  
  99.                         Thread.sleep(1000);  
  100.                     } catch (InterruptedException e) {  
  101.                         e.printStackTrace();  
  102.                     }  
  103.   
  104.                 }  
  105.             }  
  106.   
  107.         }  
  108.   
  109.         /** 
  110.          * 创建MyHandler的线程 
  111.          *  
  112.          * @author Administrator 
  113.          *  
  114.          */  
  115.         class MyHandlerCreateRunnable implements Runnable {  
  116.   
  117.             @Override  
  118.             public void run() {  
  119.                 MyLooper.prepare();  
  120.                 handler = new MyHandler(MySurfaceView.this);  
  121.                 MyLooper.loop();  
  122.             }  
  123.   
  124.         }  
  125.   
  126.     }  
  127.   
  128. }  

了。代码都贴完了。现在结合到一块来看。在MyHandlerCreateRunnable中。我们对调用了MyLooper.prepare()方法对当前线程进行了线程局部变量保存。再创建出MyHandler对象。最后让消息循环启动。这时。在此线程中如果没有消息到来。就会在MyLooper的loop()方法中。被阻塞队列的take()方法所阻塞。直到有消息到来。


然后我们在MyTimerRunnable中。对消息进行创建。并使用在MyHandlerCreateRunnable线程中创建的handler对象。对消息进行发送。为了方便。下面贴出局部代码继续分析


[java] view plain copy
  1. /** 
  2.      * 发送消息 
  3.      *  
  4.      * @param msg 
  5.      */  
  6.     public void sendMessage(MyMessage msg) {  
  7.         msg.target = this;  
  8.         try {  
  9.             mQueue.put(msg);  
  10.         } catch (InterruptedException e) {  
  11.         }  
  12.     }  

此handler的sendMessage方法处。将MyHandler自身作为msg对象的一个成员变量赋值。再将些消息存放入此消息队列中。放入之后。MyLooper.loop()方法中的take()方法就会获取到些msg对象并解除阻塞。继续运行。


[java] view plain copy
  1. /** 
  2.      * 这里启动消息循环 
  3.      */  
  4.     public static void loop() {  
  5.         while (true) {  
  6.             try {  
  7.                 myLooper = myLooper();  
  8.                 BlockingQueue<MyMessage> mQueue = myLooper.mQueue;  
  9.                 // take()方法是个阻塞方法。线程运行到此会阻塞住。以准备接收发过来的消息  
  10.                 MyMessage msg = mQueue.take();  
  11.                 msg.target.dispatchMessage(msg);  
  12.             } catch (InterruptedException e) {  
  13.                 // 当线程关闭的时候会出现此异常。此时退出循环  
  14.                 return;  
  15.             }  
  16.         }  
  17.     }  


运行之后可以看到。通过调用msg.target.dispatchMessage(msg)方法将此message发送给之前我们用来发送消息的MyHandler对象。


[java] view plain copy
  1. /** 
  2.      * 派发消息 
  3.      *  
  4.      * @param msg 
  5.      */  
  6.     public void dispatchMessage(MyMessage msg) {  
  7.         callBack.handleMessage(msg);  
  8.     }  

接着立马就将此消息对象发送给了自己定义的回调方法中。也就是我们handler用来处理消息的回调方法。handleMessage。

所以。搞了半天。真正用来对线程间进行通信的其实就是一个阻塞队列。。。相信这个结论够简洁明了。。。这是几乎完全仿照安卓原生的handler-looper逻辑来写的。所以。如果你理解了这篇博客。相信更进一步的看Handler-Looper源码会通畅不少。。。

下面提供demo下载。

点击下载demo

你可能感兴趣的:(浅析Handler、Looper机制。通过自定义Handler、Looper,让你有更直观的了观!)