android之线程间通信
来源:互联网 发布:java 时间校验 编辑:程序博客网 时间:2024/05/01 21:50
以前经常使用handler,用handler实现了好多功能,但具体原理不是很清楚,有点混乱,今天看了一下源码,研究了一下豁然开朗。
先看涉及到的几个对象
Looper:
public class Looper {
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
}
public synchronized static Looper getMainLooper() {
return mMainLooper;
}
public static Looper myLooper() {
return sThreadLocal.get();
}
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
public void quit() {
Message msg = Message.obtain();
// NOTE: By enqueueing directly into the message queue, the
// message is left with a null target. This is how we know it is
// a quit message.
mQueue.enqueueMessage(msg, 0);
}
public Thread getThread() {
return mThread;
}
}
每个线程对应一个Looper对象,当程序运行时,主线程会自动创建一个Looper对象,这时我们通过Looper.getMainLooper或者Looper.myLooper()会得到属于主线程的Looper对象;但如果我们开启一个新线程,在新线程里面用Looper.myLooper()去获取属于当前线程的Looper对象,但是返回结果为null。起初很不理解,Looper对象里有个静态变量ThreadLocal,大家都知道静态变量存放于常量池里面,只有一份,按道理应该是能获取的到。后来查了一下这个TheadLocal对象,看到一篇文章说,当开启多个线程去访问数据库时,这时候共享数据库链接是不安全的,ThreadLocal保证了每个线程都有自己的session(数据库链接),首次访问为null,会创建自己的链接。Looper对象也一样,ThreadLocal保证了每个线程都有自己的Looper对象。在android中主线程启动会创建属于自己的looper对象,这时候如果我们在去调用prepare就会抛Only one Looper may be created per thread;但子线程并不会自己创建looper对象,需要我们手动调用prepare创建属于该线程的looper对象。在android示例中经常见到HandlerThread对象,这是一个线程对象,我们可以创建一个handler=new Handler(HandlerThread.myLooper());这时候用handler发送消息,会发送到属于HandlerThread的消息队列上。问题:在子线程中Toast会报错Can't create handler inside thread that has not called Looper.prepare(),原因是当常见toast对象时,会创建默认的handler对象,但这时子线程并没有looper对象所以会报错。
Handler:
final MessageQueue mQueue;
final Looper mLooper;
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
当我们创建无参的handler对象时,Looper会调用myLooper方法获得属于当前线程的looper对象,然后looper对象会调用其mQueue获得MessageQueue对象,MessageQueue对象在Looper初始化时会被创建。这时我们通过handler发送消息,系统会调用mQueue.enqueueMessage把消息存入MessageQueue里。Looper对象的looper方法会不停地监控MessageQueue,当发现有消息时,会调用handler的handleMessage方法把消息传送过去。
当然我们也可以创建带参的handler对象
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
- android之线程间通信
- Android之线程间通信
- android 之 线程间的通信
- 【Android】Handler之线程间通信
- Android线程间通信之Handler
- android线程间通信之handler
- android线程通信之Asynctask
- android线程间通信
- Android线程间通信
- Android线程间通信
- android 线程间通信
- Android线程间通信
- Android线程间通信
- android线程间通信
- Android 线程间通信
- android开发之线程间通信(消息的使用)
- Android线程间通信机制之Handler Looper
- android学习之handler实现线程间通信
- java面试题收集整理(2)
- AT命令集详解
- 硬盘的文件系统结构
- << 的思考
- apk重新签名
- android之线程间通信
- xcode object-c 笔记
- vim 参考手册
- AIR-文件操作:使用文件对象操作文件和目录
- error:WINDOWS.H already included错误解释
- uboot执行流程
- UVA 253 立方体着色
- 不少美国人认为云计算跟天气有关 约会装B必备
- 在KVM 上访问 Linux 虚拟机终端