浅析Handler、Looper机制。通过自定义Handler、Looper,让你有更直观的了观!
来源:程序员人生 发布时间:2014-11-21 08:26:19 阅读次数:2536次
转载请注明出处:http://blog.csdn.net/liu470368500/article/details/40625333
Handler、Looper。http://www.wfuyu.com/cxyms/的时候被问到的机率非常高的1个问题。固然。我说的是像我们这样的低级。。。1般讲授Handler、Looper机制的都是通过源码去讲授。这里我来通过自定义Handler、Looper。让各位看官能有个更直观的了解。相信有了这篇博文的基础。再看Handler、Looper的源码。理解起来就更容易了。
为了与安卓原生的相接轨。这里自定义的Handler-Looper使用的逻辑基本与系统原生的相1致。
先贴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;
/** 1个线程对应1个阻塞队列。 */
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实例创建出来加入进去。这样便能保存1个线程只有1个Looper。相应的也只有1个阻塞队列。
接下来看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();
}
}
}
}
好了。代码都贴完了。现在结合到1块来看。在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对象的1个成员变量赋值。再将些消息寄存入此消息队列中。放入以后。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。
所以。弄了半天。真正用来对线程间进行通讯的其实就是1个阻塞队列。。。相信这个结论够简洁明了。。。这是几近完全仿照安卓原生的handler-looper逻辑来写的。所以。如果你理解了这篇博客。相信更进1步的看Handler-Looper源码会通畅很多。。。
下面提供demo下载。
点击下载demo
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠