消息机制模型
- Message:需要传递的消息,可以传递数据;
- MessageQueue:消息队列,并不是队列,实际上是一个单链表的数据结构来 管理和维护
Message
,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);因为有延时消息
的存在。所以没设计成队列,因为并不能完全保证先进先出,而是需要根据时间来判断,所以Android中采用了链表的形式来实现这个队列,也方便了数据的插入 - Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
- Looper:不断循环执行(Looper.loop),
负责关联线程以及消息的分发
,从MessageQueue
中获取Message
分发给 Handler。
Handler 的面目
Handler handler = new Handler() {
@Overridse
public void handleMessage(Message msg) {
}
};
Message msg = new Message();
handler.sendMessage(msg);
- 创建
Handler
时会 获取当前线程的 Looper(Looper.myLooper();)(一个Handler仅一个),Looper 内部创建 MessageQueue; - sendMessage(msg)或post(Runnable) 会将本Handler的引用打包到
Message.target
并发送给 Looper 的消息队列 MessageQueue 中(通过单向链表的数据结构来存储消息); - Looper 的
loop
方法中拿到当前的消息队列,创建一个死循环不断取消息queue.next();
,拿到就分发给 Handler 执行msg.target.dispatchMessage(msg);
,执行完之后会将用完的消息回收掉msg.recycleUnchecked();
; -
MessageQueue
中next()
有for(;;)取消息,当前时间没有消息就处于阻塞状态nativePollOnce(ptr, nextPollTimeoutMillis);
,取到了消息即退出循环。
//之前必须调用 prepare ;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
ActivityThread 和Handler的关系
- 通过在
main
方法中运行一个死循环(Looper.loop())让 main 一直执行。 - 本质上Android就是事件驱动的程序,界面刷新也好,交互也好,本质上都是事件。这些事件最后通通被作为了Message发送到了MessageQueue中。
- 主线程做耗时操作本质上不是阻塞了主线程,而是阻塞了Looper的loop方法。导致loop方法无法处理其他事件,导致出现了
ANR
。
public static void main(String[] args) {
AndroidOs.install();
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);//建立Binder通道 (创建新线程)
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主线程Looer会不会消耗cpu资源
- 这就涉及到
Linux pipe/epoll
机制,简单说就是在主线程的MessageQueue
没有消息时,便阻塞在loop的queue.next()
中的nativePollOnce()
方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道
写端写入数据来唤醒主线程工作。 - 这里采用的
epoll
机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
MessageQueue没有消息时候会怎样?阻塞之后怎么唤醒呢?说说pipe/epoll机制?
- 当消息不可用或者没有消息的时候就会阻塞在
next
方法,而阻塞的办法是通过pipe/epoll
机制。 - epoll机制是一种IO多路复用的机制,具体逻辑就是一个进程可以监视多个描述符,当某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作,这个读写操作是阻塞的。在Android中,会创建一个Linux管道(Pipe)来处理阻塞和唤醒。
- 当消息队列为空,管道的读端等待管道中有新内容可读,就会通过
epoll
机制进入阻塞状态。 - 当有消息要处理,就会通过管道的写端写入内容,唤醒主线程。
同步屏障
2.自定义Handler时如何避免内存泄漏
一般非静态内部类
持有外部类的引用的情况下,造成外部类在使用完成后不能被系统回收内存,从而造成内存泄漏。为了避免这个问题,我们可以自定义的Handler
声明为静态内部类
形式,然后通过弱引用
的方式,让Handler持有外部类的引用,从而可避免内存泄漏问题。
以下是代码实现
private WeakReference<MainActivity> activityWeakReference;
private MyHandler myHandler;
static class MyHandler extends Handler {
private MainActivity activity;
MyHandler(WeakReference<MainActivity> ref) {
this.activity = ref.get();
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
//需要做判空操作
if (activity != null) {
activity.mTextView.setText("new Value");
}
break;
default:
Log.i(TAG, "handleMessage: default ");
break;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在onCreate中初始化
activityWeakReference = new WeakReference<MainActivity>(this);
myHandler = new MyHandler(activityWeakReference);
myHandler.sendEmptyMessage(1);
mTextView = (TextView) findViewById(R.id.tv_test);
}
网友评论