Android主线程的消息系统(Handler\Looper)
前言:
之前的文章写的都是关于Bitmap和内存的优化技术,这一篇文章给大家谈谈Handler。
Handler是Android系统中比较重要的一个知识,在Android多线程面试经常会被问到,在实际项目中的确也经常用到。当然也比较复杂,知识比较多,牵扯到的类有Thread、Looper、Message、MessageQueue。
Android是支持多线程的,通常应用程序中与用户相关的UI事件都是运行在主线程中,比如点击屏幕、按钮等,为了保持主线程顺畅相应用户事件不被阻塞就需要把耗时的操作(主要是联网、操作大文件等)放到子线程中,这个时候你可能会想到Handler(当然还你可以用其他的比如:异步任务,,这个以后再讲),但是Handler又是怎么和Thread联系起来的呢?这个咱们来看一下Android主线程是怎么创建的。
ActivityThread:
在ActivityThread.java中有一个main()函数,这个函数就是在一个应用启动的入口,调用关系是:ActivityManagerService.java中的startProcessLocked函数调用如下代码:
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags,
app.info.targetSdkVersion, null);
Process.start又做了如下的操作,只看方法注释就行,现在不需要知道具体做了什么:
/**
* Start a new process.
*
* <p>If processes are enabled, a new process is created and the
* static main() function of a <var>processClass</var> is executed there.
* The process will continue running after this function returns.
*
* <p>If processes are not enabled, a new thread in the caller's
* process is created and main() of <var>processClass</var> called there.
*
* <p>The niceName parameter, if not an empty string, is a custom name to
* give to the process instead of using processClass. This allows you to
* make easily identifyable processes even if you are using the same base
* <var>processClass</var> to start them.
*
* @param processClass The class to use as the process's main entry
* point.
* @param niceName A more readable name to use for the process.
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int targetSdkVersion,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, targetSdkVersion, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
通过注释也能看到上面的函数会找到ActivityThread的main函数并且执行。main函数中创建了Looper,Looper的作用就是利用线程创建一个消息处理队列,并且维护这个消息队列:
public static void main(String[] args) {
Looper.prepareMainLooper();//创建Looper
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);//应用所有的逻辑都在这个方法中
Looper.loop();//开启一个消息循环,不断的读取MessageQueue中的Message。
}
Looper:
Looper.prepareMainLooper()的代码如下:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
上面的方法注释已经说的很明白,创建了主线程的Looper,这段代码是系统调用的。先看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 void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();//获取当前线程
}
创建主线程的Looper,每一个Looper对应一个Thread、一个MessageQueue,创建Looper的时候会创建一个MessageQueue。到目前位置创建了应用的主线程(Thread)、Looper、MessageQueue,调用Looper.loop(),开始不断的从MessageQueue中读取Message并处理,如果没有消息则等待。现在有了消息循环,有了管理消息循环的Looper就差发送消息和处理消息的Handler了。
Handler:
这个时候你在你的应用中创建一个Handler,一般都是下面的代码:
private static final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
..........
}
}
};
这个Handler是在主线程中创建的,Handler的构造函数如下:
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
mLooper = Looper.myLooper();//获取上面在主线程创建的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//获取Looper的MessageQueue
mCallback = null;//默认为null在后面处理msg时会就行检查
}
创建完Handler你就可以用了,比如你发一个消息:
mHandler.sendEmptyMessage(MSG_WHAT);
在系统中会走最终走到Handler.java下面的方法:
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;//注意这行代码后面会用,把Handler赋值给Msg的target对象
sent = queue.enqueueMessage(msg, uptimeMillis);//把msg放到MsgQueue中
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
上面的方法第二个是延时毫秒数,queue.enqueueMessage把消息发送到MessageQueue后剩下的就是等待消息被处理,前面不是说了Looper.loop()方法开始轮询消息队列吗,你发送的消息就是在loop方法中读取到的,读取到后谁去处理呢?在loop()方法中有一句代码:
msg.target.dispatchMessage(msg);
msg就是你发送到MessageQueue的消息,被读取后调用target.dispatchMessage(),这个target就是上面Handler发送消息是赋值的,就是发送消息的Handler本身,然后Handler调用自己的下面方法就行消息处理:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//在这会调用到上面重写的handleMessage方法。
}
}
因为在new Message的时候callback为空,并且Handler的mCallback = null,所以会调用到你上面new Handler时重写的handleMessage方法。
总结:
每一个线程中都对应一个Looper,每一个Looper都对应一个MessageQueue,这个Looper是用来管理消息队列的,主要是读取消息队列和把消息发送给Message的target去处理。到这你应该清除Thread、Handler、Message、MessageQueue和Looper他们之间的关系了吧。
大家如果对编程感兴趣,想了解更多的编程知识,解决编程问题,想要系统学习某一种开发知识,我们这里有java高手,C++/C高手,windows/Linux高手,android/ios高手,请大家关注我的微信公众号:程序员互动联盟or coder_online,大牛在线为您提供服务。
Android主线程的消息系统(Handler\Looper)的更多相关文章
- Android之消息机制Handler,Looper,Message解析
PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...
- Android:子线程向UI主线程发送消息
在Android里,UI线程是不同意被堵塞的.因此我们要将耗时的工作放到子线程中去处理. 那么子线程耗时处理后要如何通知UI线程呢? 我们能够在UI主线程中创建一个handler对象,然后通过重写其h ...
- Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别?
一个帖子的整理: Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别如果你不带参数的实例化:Handler ...
- android 主线程和子线程之间的消息传递
从主线程发送消息到子线程(准确地说应该是非UI线程) package com.zhuozhuo; import android.app.Activity; import android.os.Bun ...
- 主线程中有多个handler的情况
工作中遇到了这么一种情况,有两个视图,都需要开启异步任务从服务器获取数据,每个view中创建一个Handler,注册到异步任务中去,当异步任务从服务器获取数据出错,或者出现io异常或者http协议异常 ...
- android 消息系统Handler、MessageQueue、Looper源代码学习
android消息系统 总体框架如图所看到的 在安卓的消息系统中,每一个线程有一个Looper,Looper中有一个MessageQueue,Handler向这个队列中投递Message,Looper ...
- Android 主线程和线程之间相互发送消息
通过分析Activity源码,我们知道每个Activity都有一个Looper,所以主线程在接收Message是不需要调用Looper.prepare()和Looper.loop(),但是线程是不带L ...
- Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息
先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...
- Android源码分析-消息队列和Looper
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17361775 前言 上周对Android中的事件派发机制进行了分析,这次博主 ...
随机推荐
- idea启动springboot+jsp项目出现404
场景:用IntelliJ IDEA 启动 springBoot项目访问出现404,很皮,因为我用eclipse开发时都是正常的,找了很久,什么加注释掉<scope>provided< ...
- 20175316盛茂淞 2018-2019-2 《Java程序设计》第9周学习总结
20175316盛茂淞 2018-2019-2 <Java程序设计>第9周学习总结 教材学习内容总结 下载安装MySQL数据库管理系统. 学习<Java程序设计>第十一章MyS ...
- 移动端比较好用的滑动条 vue-slider-component
安装: npm install vue-slider-component <template> <div> <vue-slider v-model="value ...
- 第六周助教工作总结——NWNU李泓毅
本周应批改作业23份,实际批改作业23份. 本周作业要求:https://www.cnblogs.com/nwnu-daizh/p/10569690.html 本周存在的问题: 一.github迭代过 ...
- 常见的js dom操作
1.查找 document.getElementById('id属性值'); 返回拥有指定id的第一个对象的引用 document/element.getElementsByClassName( ...
- mybatis-plus 3.X 配置
官网配置参数说明地址:https://mp.baomidou.com/config/#logicdeletevalue 本地配置:yml mybatis-plus: mapper-locations: ...
- Python_day7
sys模块 import sys def _add(a, b): return a + b def _sub(a, b): return a - b def _mul(a, b): return a ...
- ffmpeg源码编译安装(Compile ffmpeg with source) Part 2 : 扩展安装
在Ubuntu,Debian,Mint上编译ffmpeg 本文主要为在Ubuntu,Debian和Mint上编译安装ffmpeg和库文件以及一些扩展的编解码器.当然这与从源中安装无关. 请首先看一下通 ...
- Codeforces831D Office Keys
D. Office Keys time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...
- python xss相关的编码解码小脚本
1.功能分析: 实际工作中经常会遇到alert()之类的函数被防火墙过滤,而把alert()转化为ascii码放到String.fromCharCode()中就可以绕过,之前会一个一个查ascii表, ...