转载请注明出处:http://blog.csdn.net/liu470368500/article/details/40625333
Handler、Looper。面试的时候被问到的机率非常高的一个问题。当然。我说的是像我们这样的初级。。。一般讲解Handler、Looper机制的都是通过源码去讲解。这里我来通过自定义Handler、Looper。让各位看官能有个更直观的了解。相信有了这篇博文的基础。再看Handler、Looper的源码。理解起来就更容易了。
为了与安卓原生的相接轨。这里自定义的Handler-Looper使用的逻辑基本与系统原生的相一致。
先贴MyHanlder代码:
/** * 自定义Handler * * @author lzh * */ public class MyHandler { // 用于进行线程间通信的阻塞队列 private BlockingQueue<MyMessage> mQueue; // 处理消息的回调 private CallBack callBack; public MyHandler(CallBack callBack) { super(); MyLooper looper = MyLooper.myLooper(); if (looper == null) { throw new RuntimeException( "在新开的线程中。创建MyHandler对象需要先调用MyLooper.prepare()方法。"); } mQueue = looper.mQueue; this.callBack = callBack; } /** * 消息接收的回调 * * @author Administrator * */ public interface CallBack { /** * 处理消息 * * @param msg */ void handleMessage(MyMessage msg); } /** * 发送消息 * * @param msg */ public void sendMessage(MyMessage msg) { msg.target = this; try { mQueue.put(msg); } catch (InterruptedException e) { } } /** * 派发消息 * * @param msg */ public void dispatchMessage(MyMessage msg) { callBack.handleMessage(msg); } }
再看MyLooper的代码:
public class MyLooper { private static ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<MyLooper>(); private static MyLooper myLooper; /** 一个线程对应一个阻塞队列。 */ public BlockingQueue<MyMessage> mQueue = null; private MyLooper() { super(); mQueue = new LinkedBlockingQueue<MyMessage>(); } /** * 为本线程准备对应的MyLooper对象 */ public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException( "Only one MyLooper may be created per thread"); } sThreadLocal.set(new MyLooper()); } /** * 获取当前线程相对应的Looper对象 * * @return 当未调用prepare()方法时。ThreadLocal.get()方法返回的为null; */ public static MyLooper myLooper() { return sThreadLocal.get(); } /** * 这里启动消息循环 */ public static void loop() { while (true) { try { myLooper = myLooper(); BlockingQueue<MyMessage> mQueue = myLooper.mQueue; // take()方法是个阻塞方法。线程运行到此会阻塞住。以准备接收发过来的消息 MyMessage msg = mQueue.take(); msg.target.dispatchMessage(msg); } catch (InterruptedException e) { // 当线程关闭的时候会出现此异常。此时退出循环 return; } } } }
本来安卓原生中使用的是MessageQueue。但是却死活创建不出来。这里只有使用BlockQueue代替了。
当中的ThreadLocal可能有部分朋友有点陌生。这是线程局部变量。它的set方法和get()方法比较有意思。是和线程相关的。你在哪个线程里面set变量进去。你在哪个线程里面get()出来的就是哪个。所以在MyLooper中得先调用prepare()方法。先将与此线程相关的MyLooper实例创建出来加入进去。这样便能保存一个线程只有一个Looper。相应的也只有一个阻塞队列。
接下来看MyMessage代码:
public class MyMessage { public int msg1; public int msg2; public int what; public Object obj; public MyHandler target; public Runnable runnable; }
原生的Message由于是final标记的。而Message里面存的Handler对象又比较重要。得要依靠它来指定最终的消息应该发送给哪个Handler来接收。所以。这个也自定义了。
下面开始来测试。由于安卓不允许在UI线程中有阻塞操作。所以这里我们使用SurfaceView在子线程中画图来测试是否可进行线程间通信。
/** * 测试自定义的Handler与Looper的测试工程,由于内部有阻塞队列。而安卓的机制是不允许此类的阻塞行为在主线程中出现。 * 所以此处用SurfaceView在子线程中进行测试 * * @author Administrator * */ public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new MySurfaceView(this)); } class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, CallBack { private static final String TAG = "MySurfaceView"; private SurfaceHolder mHolder; Thread mThread; MyHandlerCreateRunnable mRunnable; private Paint mPaint; private MyHandler handler = null; public MySurfaceView(Context context) { super(context); // 初始化holder: mHolder = getHolder(); mHolder.addCallback(this); mRunnable = new MyHandlerCreateRunnable(); mPaint = new Paint(); mPaint.setColor(Color.BLACK); mPaint.setAntiAlias(true); mPaint.setTextAlign(Align.CENTER); mPaint.setTextSize(45); new Thread(mRunnable).start(); } public MySurfaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "==surfaceCreated=="); // mQueue = new LinkedBlockingQueue<String>(); new Thread(new MyTimerRunnable()).start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "==surfaceChanged=="); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "==surfaceDestroyed=="); } @Override public void handleMessage(MyMessage msg) { synchronized (mHolder) { Canvas mCanvas = null; System.out.println("==lockCanvas=="); mCanvas = mHolder.lockCanvas();// 锁定画布。之后就可以在此画布上画图了。 String content = (String) msg.obj; mCanvas.drawColor(Color.WHITE); mCanvas.drawText(content, 100, 400, mPaint); mHolder.unlockCanvasAndPost(mCanvas);// } } /** * 定时发送消息的线程 * * @author Administrator * */ class MyTimerRunnable implements Runnable { int index = 0; @Override public void run() { while (true) { MyMessage msg = new MyMessage(); msg.obj = "这是第" + index + "个"; index++; handler.sendMessage(msg); if (index >= 50) { break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 创建MyHandler的线程 * * @author Administrator * */ class MyHandlerCreateRunnable implements Runnable { @Override public void run() { MyLooper.prepare(); handler = new MyHandler(MySurfaceView.this); MyLooper.loop(); } } } }
好了。代码都贴完了。现在结合到一块来看。在MyHandlerCreateRunnable中。我们对调用了MyLooper.prepare()方法对当前线程进行了线程局部变量保存。再创建出MyHandler对象。最后让消息循环启动。这时。在此线程中如果没有消息到来。就会在MyLooper的loop()方法中。被阻塞队列的take()方法所阻塞。直到有消息到来。
然后我们在MyTimerRunnable中。对消息进行创建。并使用在MyHandlerCreateRunnable线程中创建的handler对象。对消息进行发送。为了方便。下面贴出局部代码继续分析
/** * 发送消息 * * @param msg */ public void sendMessage(MyMessage msg) { msg.target = this; try { mQueue.put(msg); } catch (InterruptedException e) { } }
在此handler的sendMessage方法处。将MyHandler自身作为msg对象的一个成员变量赋值。再将些消息存放入此消息队列中。放入之后。MyLooper.loop()方法中的take()方法就会获取到些msg对象并解除阻塞。继续运行。
/** * 这里启动消息循环 */ public static void loop() { while (true) { try { myLooper = myLooper(); BlockingQueue<MyMessage> mQueue = myLooper.mQueue; // take()方法是个阻塞方法。线程运行到此会阻塞住。以准备接收发过来的消息 MyMessage msg = mQueue.take(); msg.target.dispatchMessage(msg); } catch (InterruptedException e) { // 当线程关闭的时候会出现此异常。此时退出循环 return; } } }
/** * 派发消息 * * @param msg */ public void dispatchMessage(MyMessage msg) { callBack.handleMessage(msg); }
所以。搞了半天。真正用来对线程间进行通信的其实就是一个阻塞队列。。。相信这个结论够简洁明了。。。这是几乎完全仿照安卓原生的handler-looper逻辑来写的。所以。如果你理解了这篇博客。相信更进一步的看Handler-Looper源码会通畅不少。。。
下面提供demo下载。
点击下载demo