转载请注明出处:http://blog.csdn.net/liu470368500/article/details/40625333
Handler、Looper。面试的时候被问到的机率非常高的一个问题。当然。我说的是像我们这样的初级。。。一般讲解Handler、Looper机制的都是通过源码去讲解。这里我来通过自定义Handler、Looper。让各位看官能有个更直观的了解。相信有了这篇博文的基础。再看Handler、Looper的源码。理解起来就更容易了。
为了与安卓原生的相接轨。这里自定义的Handler-Looper使用的逻辑基本与系统原生的相一致。
先贴MyHanlder代码:
-
-
-
-
-
-
- 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;
- }
-
-
-
-
-
-
-
- public interface CallBack {
-
-
-
-
-
- void handleMessage(MyMessage msg);
- }
-
-
-
-
-
-
- public void sendMessage(MyMessage msg) {
- msg.target = this;
- try {
- mQueue.put(msg);
- } catch (InterruptedException e) {
- }
- }
-
-
-
-
-
-
- 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>();
- }
-
-
-
-
- public static void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException(
- "Only one MyLooper may be created per thread");
- }
- sThreadLocal.set(new MyLooper());
- }
-
-
-
-
-
-
- public static MyLooper myLooper() {
- return sThreadLocal.get();
- }
-
-
-
-
- public static void loop() {
- while (true) {
- try {
- myLooper = myLooper();
- BlockingQueue<MyMessage> mQueue = myLooper.mQueue;
-
- 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在子线程中画图来测试是否可进行线程间通信。
-
-
-
-
-
-
-
- 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);
-
- 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==");
-
- 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);
- }
- }
-
-
-
-
-
-
-
- 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();
- }
-
- }
- }
-
- }
-
-
-
-
-
-
-
- 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对象。对消息进行发送。为了方便。下面贴出局部代码继续分析
-
-
-
-
-
- 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;
-
- MyMessage msg = mQueue.take();
- msg.target.dispatchMessage(msg);
- } catch (InterruptedException e) {
-
- return;
- }
- }
- }
运行之后可以看到。通过调用msg.target.dispatchMessage(msg)方法将此message发送给之前我们用来发送消息的MyHandler对象。
-
-
-
-
-
- public void dispatchMessage(MyMessage msg) {
- callBack.handleMessage(msg);
- }
接着立马就将此消息对象发送给了自己定义的回调方法中。也就是我们handler用来处理消息的回调方法。handleMessage。
所以。搞了半天。真正用来对线程间进行通信的其实就是一个阻塞队列。。。相信这个结论够简洁明了。。。这是几乎完全仿照安卓原生的handler-looper逻辑来写的。所以。如果你理解了这篇博客。相信更进一步的看Handler-Looper源码会通畅不少。。。
下面提供demo下载。
点击下载demo