1.Android消息处理机制(★★★★必会)
、Message、Handler的关系
当我们的Android应用程序的进程一创建的时候,系统就给这个进程提供了一个Looper,Looper是一个死循环,它内部维护这个一个消息队列。Looper不停地从消息队列中取消息(Message),取到消息就发送给了Handler,最后Handler根据接收到的消息去修改UI。Handler的sendMessage方法就是将消息添加到消息队列中。
Activity中提供了一个runOnUiThread方法,用于进行消息处理。此方法是通过线程合并——join来实现消息处理的。
线程合并:主线程将子线程的任务拿到自己这里来执行并终止子线程。
实例代码如下:
/**
* Runs the specified action on the UI thread. If the current thread is
* the UI thread, then the action is executed immediately. If the
* current thread is not the UI thread, the action is posted to the
* event queue of the UI thread.
* 上面的意思为:在UI线程中运行我们的任务,如果当前线程是UI线程,则立即执行,如果不是则该任务发送到UI线程的事件队列。
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
//自定义我们的业务代码
}
});
d
该方法是Handler对象提供的,Handler给消息队列发送一个消息,发送成功则返回true,否则返回false,如果返回false一般是由于looper进程不存在导致的。该方法主要用于定时任务。如果返回true也不一定代表着我们的定时任务就执行了,因为很可能在定时任务的时间未到之前我们的looper进程退出了,那么消息也就丢失了。
执行该任务的线程用的就是Handler对象所在的线程。
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses. The runnable will be run
* on the thread to which this handler is attached. Parameters: r The
* Runnable that will be executed. delayMillis The delay (in
* milliseconds) until the Runnable will be executed. Returns: Returns
* true if the Runnable 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 Runnable will be processed -- if the looper is quit before the
* delivery time of the message occurs then the message will be dropped.
* 上面代码翻译如下:
* 该方法将一个Runnable对象r添加到消息队列,在指定的时间后会被执行。
* 这个Runnable对象会运行在当前handler所在的线程中。
* 第一个参数:Runnable 要执行的任务
* 第二个参数:delayMillis(单位:毫秒) runnable 任务被执行前的延迟时间
* 返回值:boolean ,如果该Runnable被成功添加到消息队列则返回true,否则返回false
* 不过,通常返回false是因为looper进程处理消息队列退出。
* 注意:返回true不代表着Runnable被执行,如果looper在延时任务还没被执行前退出了,那么消息就丢失掉了。
*/
boolean flag = handler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 2000);
该方法也属于Handler对象,唯一不同的是该方法设置的定时任务是一个绝对时间,指的是Android系统的开机时间,如果想设置从当前时间算起2秒后执行该任务则可以将时间这样写:SystemClock.uptimeMillis()+2000,其中SystemClock.uptimeMillis()是系统运行时间。
/**
* Causes the Runnable r to be added to the message queue, to be run at
* a specific time given by uptimeMillis. The time-base is
* android.os.SystemClock.uptimeMillis. The runnable will be run on the
* thread to which this handler is attached. Parameters: r The Runnable
* that will be executed. uptimeMillis The absolute time at which the
* callback should run, using the android.os.SystemClock.uptimeMillis
* time-base. Returns: Returns true if the Runnable 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 Runnable will be processed -- if
* the looper is quit before the delivery time of the message occurs
* then the message will be dropped.
* 意译:给消息队列发出一个消息,让指定的任务在指定的时间执行。这里的时间是绝对时间,是相对于android.os.SystemClock.uptimeMillis的时间
* 如果我们想在当前时间的2秒后执行该任务则将时间设置为:SystemClock.uptimeMillis()+2000即可。
*/
boolean postAtTime = handler.postAtTime(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}, SystemClock.uptimeMillis()+2000);
}
1.5.ANR异常
1.5.1. 简介
Application No Response:应用程序无响应。在主线程中,是不允许执行耗时的操作的,如果主线程阻塞的时间大于6秒,就很有可能出现anr异常。
主线程,要完成界面的更新,事件的处理,窗体显示的回调,所以如果主线程阻塞时间较长,就不能很好的处理以上比较重要的事情,那么Android有一个机制,就是如果他发现消息队列中有很多消息,主线程没办法响应的话,他就会抛出anr异常。所以,比较耗时的操作都必须要交给子线程。
1.5.2. 解决办法
可以通过Handler来解决这个问题,将比较耗时的操作交给子线程,然后子线程通过Handler,发送消息给主线程,让主线程去更新界面。
什么样的操作时比较耗时的?
1、访问网络,2、大文件的拷贝,3、阻塞式的请求,socket
1.6.结合工作和面试
1. 面试中
Ø ANR异常 (常问)
为什么为出现ANR异常,如何解决?
Ø Handler机制 (这个非常重要) 这个后期在讲项目的时候应该还会涉及到。Looper、Message、MessageQueue、Hanler如何运作的?
2. 工作中
这个章节非常重要,工作中用的很多。工作中如果碰到ANR异常,根据异常日志,需要会解决。需要会使用handler进行线程间通讯。