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;
   
}

原创粉丝点击