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

转载请注明出处: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;
			}
		}
	}


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


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

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

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

下面提供demo下载。

点击下载demo

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
好的,针对你的问题,我可以给出一些参考。在Android开发中,Handler是一个常用的类,用于在多线程环境下进行消息传递和处理。在创建一个Handler实例时,需要传入一个Looper对象,该对象负责管理消息队列并将消息分发给对应的Handler进行处理。 以下是一个示例代码,展示如何自定义Handler并传入Looper对象进行实例化: ```java public class MyHandler extends Handler { public MyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // 处理消息 } } ``` 在上述代码中,我们创建了一个名为`MyHandler`的自定义Handler类,并在其构造函数中传入了一个Looper对象。在`handleMessage`方法中,我们可以对收到的消息进行处理。 在实际使用时,我们可以在主线程中创建一个Handler实例,并将其关联到主线程的Looper对象上,以便在主线程中处理消息。示例代码如下: ```java public class MainActivity extends AppCompatActivity { private MyHandler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取主线程的Looper对象 Looper looper = Looper.getMainLooper(); // 创建Handler实例,并关联到主线程的Looper对象上 mHandler = new MyHandler(looper); } } ``` 在上述代码中,我们获取了主线程的Looper对象,并将其传入自定义Handler类中进行实例化。这样,我们就可以在主线程中使用该Handler实例来处理消息了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值