[置顶] Android消息机制小探索
2016-03-18 21:12
423 查看
Android消息机制主要指handler的运行机制(线程之间的切换),线程在默认的情况下是没有支持消息循环的,即Thread类的run方法执行完内容便推出循环,当然主线程那是后文啦!因此,Handler的运行需要底层的MessageQueue和Looper的支持。MessageQueue主要采用单链表实现(便于插入和删除),MessageQueue只是一个消息存储的单元不能去处理消息,Looper填补了这个空白(无线循环的去查寻消息),Handler在创建的时候需要采用当前线程的Looper来构建内部消息系统,先来看一段典型例子
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process messages here
}
};
Looper.loop();
}
} 那问题来了:正如前文提到的,Handler是在创建的时候调用当前线程的Looper对象构建消息内部消息系统,那Handler内部是如何获取当前线程的Looper对象呢?这就要使用到ThreadLocal(这是何方神圣),ThreadLocal可以在不同的线程中互不干扰的存储以及提供数据,因此Handler通过TheadLocal在不同线程中很容易获取当前线程的Looper对象。
当然,这么说可能我们还是不会明白ThreadLocal到底是怎么做到呢?在Thread中有一个专门的成员用于存储线程的ThreadLocal数据(values),在values内部有一个table数组,当不同线程访问同一个get方法时候,ThreadLocal内部会从各自线程中取出这个数组,然后再根据当前线程的ThreadLocal索引获取values值。这样说是不是好多了。
解决上面小疑问那马上来看看如何在飞主线程中创建消息循环系统。 直接调用Looper类的prepare方法创,实质上,在主线中也是调用prepare方法创建的~!~。
**
* Initialize the current thread as a looper. This gives you a chance to
* create handlers that then reference this looper, before actually starting
* the loop. Be sure to call {@link #loop()} after calling this method, and
* end it by calling {@link #quit()}.
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException(
"Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
} 这段代码上面已经做了详尽的解析,注意最后一行:prepare方法调用Looper类的私有构造方法创建Looper对象,在构造方法中创建消息队列,然后将当前线程对象保存起来。还是前面提到的消息队列只不过是一个单链表~~简单的数据结构,单链表效率正是在于插入以及删除操作,这些也是MessageQueue的操作enqueueMessage(插入)和next(取出并从消息队列中删除这条消息)
private Looper() {
// 私有构造方法,在prepare()方法里面调用
// 创建消息队列
mQueue = new MessageQueue();
mRun = true;
// 当前线程
mThread = Thread.currentThread();
} 谈到Looper类,不得不说loop方法,调用这个方法消息才真正进入循环。Looer.loop()方法是个死循环,在循环中不断的调用Queue的next方法读取消息,如果过没有消息next方法处以阻塞状态;唯一可以结束当前循环的方法是next返回null(looper的quit方法会被调用),当队列中有消息时,Looper的loop方法便会调用Handler中的diapatchMessage方法处理消息。
/**
* Run the message queue in this thread. Be sure to call {@link #quit()} to
* end the loop.
*/
public static final void loop() {
// 进入当前线程的消息循环
Looper me = myLooper();
MessageQueue queue = me.mQueue;
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;
}
// other codes...
// 分发消息
msg.target.dispatchMessage(msg);
// 消息的target是Handler类型的对象
// other codes...
// 释放清理
msg.recycleUnchecked();
}
}
} Handler的post和send方法本质上是调用sendMessage方法,sendMessage方法的工作就是将消息入队。looper当一个消息道来,在loop方法中调用Handler的dispatchMessage方法处理消息
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
// 首先,处理Message自己的callback,调用其run方法
if (msg.callback != null) {
handleCallback(msg);
}
else {
// 其次,调用Handler自留的接口对象
// 这个成员变量声明时的注释如下:
/**
* Callback interface you can use when instantiating a Handler to
* avoid having to implement your own subclass of Handler.
*/
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法
handleMessage(msg);
}
} dispatchMessage方法有调用了handleMessage方法,handleMessage方法实际上是一个空方法,我们重写该方法就可以实现消息处理。
谈完了非主线程消息循环系统,最后来小探下UI线程又是什么情况呢?Android程序的运行入口点可以认为是android.app.ActivityThread类的main()方法。
public static final void main(String[] args) {
// other codes...
// 创建主线程循环
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
// other codes...
// 进入当前线程(此时是主线程)消息循环
Looper.loop();
// other codes...
thread.detach();
// other codes...
}
这个main方法创建了主线程循环,通过prepareMainLooper()创建主线程的Looper以及MessageQueue,并通过loop方法开启主线程消息循环。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process messages here
}
};
Looper.loop();
}
} 那问题来了:正如前文提到的,Handler是在创建的时候调用当前线程的Looper对象构建消息内部消息系统,那Handler内部是如何获取当前线程的Looper对象呢?这就要使用到ThreadLocal(这是何方神圣),ThreadLocal可以在不同的线程中互不干扰的存储以及提供数据,因此Handler通过TheadLocal在不同线程中很容易获取当前线程的Looper对象。
当然,这么说可能我们还是不会明白ThreadLocal到底是怎么做到呢?在Thread中有一个专门的成员用于存储线程的ThreadLocal数据(values),在values内部有一个table数组,当不同线程访问同一个get方法时候,ThreadLocal内部会从各自线程中取出这个数组,然后再根据当前线程的ThreadLocal索引获取values值。这样说是不是好多了。
解决上面小疑问那马上来看看如何在飞主线程中创建消息循环系统。 直接调用Looper类的prepare方法创,实质上,在主线中也是调用prepare方法创建的~!~。
**
* Initialize the current thread as a looper. This gives you a chance to
* create handlers that then reference this looper, before actually starting
* the loop. Be sure to call {@link #loop()} after calling this method, and
* end it by calling {@link #quit()}.
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException(
"Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
} 这段代码上面已经做了详尽的解析,注意最后一行:prepare方法调用Looper类的私有构造方法创建Looper对象,在构造方法中创建消息队列,然后将当前线程对象保存起来。还是前面提到的消息队列只不过是一个单链表~~简单的数据结构,单链表效率正是在于插入以及删除操作,这些也是MessageQueue的操作enqueueMessage(插入)和next(取出并从消息队列中删除这条消息)
private Looper() {
// 私有构造方法,在prepare()方法里面调用
// 创建消息队列
mQueue = new MessageQueue();
mRun = true;
// 当前线程
mThread = Thread.currentThread();
} 谈到Looper类,不得不说loop方法,调用这个方法消息才真正进入循环。Looer.loop()方法是个死循环,在循环中不断的调用Queue的next方法读取消息,如果过没有消息next方法处以阻塞状态;唯一可以结束当前循环的方法是next返回null(looper的quit方法会被调用),当队列中有消息时,Looper的loop方法便会调用Handler中的diapatchMessage方法处理消息。
/**
* Run the message queue in this thread. Be sure to call {@link #quit()} to
* end the loop.
*/
public static final void loop() {
// 进入当前线程的消息循环
Looper me = myLooper();
MessageQueue queue = me.mQueue;
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;
}
// other codes...
// 分发消息
msg.target.dispatchMessage(msg);
// 消息的target是Handler类型的对象
// other codes...
// 释放清理
msg.recycleUnchecked();
}
}
} Handler的post和send方法本质上是调用sendMessage方法,sendMessage方法的工作就是将消息入队。looper当一个消息道来,在loop方法中调用Handler的dispatchMessage方法处理消息
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
// 首先,处理Message自己的callback,调用其run方法
if (msg.callback != null) {
handleCallback(msg);
}
else {
// 其次,调用Handler自留的接口对象
// 这个成员变量声明时的注释如下:
/**
* Callback interface you can use when instantiating a Handler to
* avoid having to implement your own subclass of Handler.
*/
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法
handleMessage(msg);
}
} dispatchMessage方法有调用了handleMessage方法,handleMessage方法实际上是一个空方法,我们重写该方法就可以实现消息处理。
谈完了非主线程消息循环系统,最后来小探下UI线程又是什么情况呢?Android程序的运行入口点可以认为是android.app.ActivityThread类的main()方法。
public static final void main(String[] args) {
// other codes...
// 创建主线程循环
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
// other codes...
// 进入当前线程(此时是主线程)消息循环
Looper.loop();
// other codes...
thread.detach();
// other codes...
}
这个main方法创建了主线程循环,通过prepareMainLooper()创建主线程的Looper以及MessageQueue,并通过loop方法开启主线程消息循环。
相关文章推荐
- 简单理解Android的Fragement组件
- 【Android 学习】 Android反编译
- Android侧滑控件DrawerLayout实战案例
- Android Service完全解析,关于服务你所需知道的一切(下)
- Android与Servlet之间的通信
- android PreferenceFragment
- Android工程分析
- Android_Fragment遇到BaiduMap
- android 修改 SwitchPreferenceCompat 高度,内边距,字体大小
- Android Service完全解析,关于服务你所需知道的一切(上)
- Android System Properties
- 【Android 学习笔记】之 Material design
- Android Studio下JNI开发
- [置顶] android开发之java代码中如何获取到当前时间。详情代码带注释。
- Android 最新视频教程
- 安卓 jni 开发 —— Android Studio 打包 so 的坑
- Android课程---Activity 的生命周期
- android dispatchTouchEvent方法查找包含点击坐标的view;
- Android屏幕适配全攻略(最权威的官方适配指导)
- 【Settings】设置何时启动屏保