Android Handler 机制

在android中,android的消息机制对于每一个开发者来说这都是必须要掌握的,因为在日常开发中我们总是避免不了的,最常见的莫过于用来子线程与主线程间的通信,更新UI,在RxAndroid中,我们AndroidSchedulers.mainThread()方法返回的也是一个包裹在Scheduler 中的Handler对象。从代码的角度来说,Handler是对android消息队列机制的高层次封装,我们使用的时候只要对Handler进行操作即可。

主线程Handler的创建和使用
public class MainActivity extends AppCompatActivity {

    private Handler myHandler = new MyHandler(this);//创建Handler对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //发送handler消息
        Message m = myHandler.obtainMessage();
        m.obj = "Message content.";
        myHandler.sendMessage(m);
    }

    /**
     * 创建Handler的子类,不推荐直接new一个Handler的匿名子类,因为有可能会产生内存泄露
     */
    static class MyHandler extends Handler {

        private WeakReference<Activity> act;

        public MyHandler(Activity act) {
            this.act = new WeakReference<Activity>(act);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Toast.makeText(act.get(), msg.obj.toString(), Toast.LENGTH_SHORT).show();
        }
    }

}

上面就是Handler的使用过程,创建出Handler实例,在handleMessage 方法中处理消息,创建Message 消息对象,然后通过Handler来发送消息。一切都很简单,这主要归功于Handler的高层次封装。下面我们就来看看里边的原理

Hanlder 的创建过程

Handler有很多个构造方法,我们挑一个作为入口就可以了,那么从Handler的默认构造方法进去

public Handler() {
	this(null, false);
}	
public Handler(Callback callback, boolean async) {
	 //......
	 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 = callback;
	//......
}

我们会发现它在构造方法中主要做了两件事,调用Looper.myLoop()方法获得一个Looper实例,通过Looper实例获取到一个MessageQueue对象。那么什么是Looper,什么是MessageQueue呢。这就引出了Android真正的消息机制的实现。在Android中,消息循环就是通过Looper、Message和MessageQueue来实现的。

Message : 主要用于承载我们发送的消息内容的对象,我们的数据信息都是放在这个实例里面

MessageQueue : 该类内部维护一个Message单向链表结构,我们发送出Message对象之后,Message对象会被存储在MessageQueue实例内部,等待被处理

Looper: 该类主要的作用是循环的从MessageQueue中取出Message进行处理

接着往下走,先看 Looper.myLooper() 方法做了什么事情

public static @Nullable Looper myLooper() {
	return sThreadLocal.get();
}

在该方法中我们发现它调用了sThreadLocal.get()来取得Looper实例。sThreadLocal 是维护在Looper 类中的一个静态ThreadLocal 的实例。ThreadLocal 是用来存储一个线程本地变量的类。到了这里或许会有一个疑问,Looper实例是什么时候放进ThreadLocal 中的呢?让我们先保留这个问题接着往下走,在Handler构造方法调用Looper.myLooper()方法之后会对获得的实例进行空安全检查,当实例为空的时候会抛出一个RuntimeExcption,这个异常信息的描述也告诉了我们,在创建Handler对象之前要调用Looper.prepare()方法,那我们现在来看一下Looper.prepare()方法都做了些什么。

private static void prepare(boolean quitAllowed) {
	if (sThreadLocal.get() != null) {
		throw new RuntimeException("Only one Looper may be created per thread");
	}
	sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

我们会发现在Looper.prepare()方法中创建了Looper实例,同时将它放进ThreadLocal 实例中。在Looper 的构造方法中则是创建了MessageQueue 实例,我们走到MessageQueue的构造方法中

MessageQueue(boolean quitAllowed) {
	mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

我们会发现这边调用了一个nativeInit的方法,并返回了一个Long类型的值,并将其赋给mPtr,我们接着往下走,看看nativeInit 的方法里面都做了什么,mPtr 是一个什么东西,该方法是一个JNI函数,我们可以在android_os_MessageQueue 中找到

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

NativeMessageQueue::NativeMessageQueue() :
    mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

从这里可以看出来,在这边同步的创建了一个native的消息队列NativeMessageQueue 以及在NativeMessageQueue 的构造方法里创建了对应于当前线程的一个Native层面的Looper对象,最后在android_os_MessageQueue_nativeInit方法里返回出了这个NativeMessageQueue的指针,现在我们就可以知道,nativeInit方法同步的为我们创建一个Native层面的消息队列,mPtr 是指向该队列的指针。到这里就完成了一个Handler的创建过程。

遍观整个过程,我们其实一个疑问,为什么在主线程中,**我们可以直接创建Handler对象而不需要调用Looper.prepare()方法呢?**这个可以在ActivityThread类中找到答案,android进程的main()方法就在该类中。

public static void main(String[] args) {
		//......
        Looper.prepareMainLooper();
		//......
        Looper.loop();
    }

main方法里面我们发现系统在这里调用了Looper.prepareMainLooper()方法,进入该方法

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

Looper.prepareMainLooper()方法中我们发现它调用了Looper.prepare()方法。所以我们不需要在主线程中调用Looper.prepare()方法

####Handler 发送消息消息的过程

使用Handler发送消息有很多的重载方法,我们就从上文中的myHandler.sendMessage(m)方法进入,逐层跟进sendMessage ⇒ \Rightarrow sendMessageDelayed ⇒ \Rightarrow sendMessageAtTime最后一直到enqueueMessage的方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler.enqueueMessage方法中,给当前的我们发送的Message实例绑定了target属性,该属性的值就是当前的Handler 实例,然后执行MessageQueue.enqueueMessage方法

boolean enqueueMessage(Message msg, long when) {
	synchronized (this) {
		msg.markInUse();
		msg.when = when;
		Message p = mMessages;
		boolean needWake;
		if (p == null || when == 0 || when < p.when) {
			// New head, wake up the event queue if blocked.
			msg.next = p;
			mMessages = msg;
			needWake = mBlocked;
		} else {
			// Inserted within the middle of the queue.  Usually we don't have to wake
			// up the event queue unless there is a barrier at the head of the queue
			// and the message is the earliest asynchronous message in the queue.
			needWake = mBlocked && p.target == null && msg.isAsynchronous();
			Message prev;
			for (;;) {
				prev = p;
				p = p.next;
				if (p == null || when < p.when) {
					break;
				}
				if (needWake && p.isAsynchronous()) {
					needWake = false;
				}
			}
			msg.next = p; // invariant: p == prev.next
			prev.next = msg;
		}

		// We can assume mPtr != 0 because mQuitting is false.
		if (needWake) {
			nativeWake(mPtr);
		}
	}
	return true;
}

MessageQueue.enqueueMessage方法,我们发送的Message在这里被添加到了消息队列中,在这个方法中,有一个值得注意的方法nativeWake(mPtr); ,这个方法的作用我们放到后面去进行说明,现在先保留在这里。走到这里,我们发送消息的一个过程就已经走完。

发送了一个消息,那总得有一个处理消息的地方,我们现在接着看Android里边是怎么处理Handler发送的消息的。

####Handler处理消息的过程

关于处理Message的一个过程,我们还得回过头,从Looper.loop()的方法开始

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    for (; ; ) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        try {
            msg.target.dispatchMessage(msg);
        }finally{}
        msg.recycleUnchecked();
    }
}

该方法去掉一些不重要的代码之后大致就是这样,首先是调用myLooper()方法取得当前线程的HandlerMessageQueue实例,之后直接进入一个死循环中,然后不停的读MessageQueue里面的Message,并调用Message对象中的target对象的dispatchMessage()方法,Message实例的target对象又是什么呢?它就是我们用来发送Message 信息的那个Handler实例,关于Handler.dispatchMessage()的实现我们回头再看。我们接着往下走,Handler.dispatchMessage()方法执行完成之后就调用Message.recycleUnchecked()清空回收该Message实例,接着就进入下一轮循环。

在这里可能也会有一个疑问,一直不停的循环取出MessageQueue实例中的Message,总有一次会将MessageQueue实例中的Message取完,然后MessageQueue.next()方法返回null然后结束循环,那这个消息机制不就停止运行了吗?

关于这个问题,我们就要来看一下MessageQueue.next()方法的具体实现了。

Message next() {
    for (;;) {
	
		nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                do {
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                    return msg;
            }else {
            	// No more messages.
            	nextPollTimeoutMillis = -1;
            }

			if (mQuitting) {
                    dispose();
                    return null;
            }
        }
    }
}

除掉一些逻辑处理的代码最后会发现MessageQueue.next()方法里面也是一个死循环,当没有消息时,线程会一直阻塞在MessageQueue.next()方法里面,一直到有新的Message进入队列,并且只有调用MessageQueue.quit()方法之后才会返回出null。那没有Message的时候,是一直在MessageQueue.next()方法里面做循环吗,答案是否定的。当没有消息的时候,在调用nativePollOnce方法是阻塞住,该方法最终会调用到Linux的epoll_wait函数,并且传入的超时时间为-1,这将会一直阻塞住等待被唤醒。要唤醒它就要提到我们之前说过的nativeWake的方法了,该方法会执行一个Native的写入操作来唤醒线程,继而开始下一轮的消轮询。

在有消息的情况下,会调用Handler.dispatchMessage()方法执行我们在发送消息添加的回调方法

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

在这个方法中我们发现它会先判断当前Message实例的callback 属性是否有值,如果有值,则调用Message.callback.run()方法,该回掉是一个Runable的实例,Handler.post(Runable callback) 方法就是这样实现的,Message实例的callback 属性无值的时候才会去调用Handler.handleMessage()方法,该方法也就是我们一般处理逻辑的地方

##最后附上一图##
消息机制实例关系图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值