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


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

    Handler、Looper。面试的时候被问到的机率很高的一个问题。

    当然。我说的是像我们这种0基础。

    一般解说Handler、Looper机制的都是通过源代码去解说。这里我来通过自己定义Handler、Looper。让各位看官能有个更直观的了解。相信有了这篇博文的基础。

    再看Handler、Looper的源代码。理解起来就更easy了。


    为了与安卓原生的相接轨。这里自己定义的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的測试project,因为内部有堵塞队列。而安卓的机制是不同意此类的堵塞行为在主线程中出现。
     * 所以此处用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; } } }


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


    /**
    	 * 派发消息
    	 * 
    	 * @param msg
    	 */
    	public void dispatchMessage(MyMessage msg) {
    		callBack.handleMessage(msg);
    	}

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

    所以。

    搞了半天。真正用来对线程间进行通信的事实上就是一个堵塞队列。

    。。相信这个结论够简洁明了。。。这是差点儿全然仿照安卓原生的handler-looper逻辑来写的。所以。假设你理解了这篇博客。相信更进一步的看Handler-Looper源代码会通畅不少。。

    以下提供demo下载。

    点击下载demo

  • 相关阅读:
    Spring bean相关
    Springboot消除switch-case方法
    Springcloud中的region和zone的使用
    SpringCloud-Eureka-服务注册是如何发起的
    SpringBoot-SpringCloud-版本对应关系
    SpringCloud-Eureka-Provider&Consumer
    激活IDEA
    搞懂spring事务
    部署spring Boot项目到tomcat
    springBoot项目打war包部署到tomcat上
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6797345.html
Copyright © 2020-2023  润新知