Android线程与消息机制

# 标签: 读博客

UI线程

先从一个经典错误开始:

android.view.ViewRootImpl$CalledFromWrongThreadException:

Only the original thread that created a view hierarchy can touch its views

为什么会出现这个错误?

UI的呈现必须在同一个线程里面完成。

试想,如果多个线程可以绘制UI,那么肯定乱套,呈现结果不可预期。

因此界面程序必然有一个UI线程,android,java,windows等都是如此。

Android UI 丈量、排布、绘制最终都是在ViewRootImpl里面完成的。

ViewRootImpl在执行UI操作之前,会进行线程检查。如果当前线程不是UI线程,就会抛出上述异常。

每个应用都对应一个进程,进程创建是伴随一个主线程创建。这个主线程就是UI线程。

Android的主线线程的入口在ActivityThread。ActivityThread和普通的java入口类一样,有一个静态main函数,作为主线程的入口。

ActivityThread与Android应用生命周期密切相关,后续会讲到。

Looper

Thread是一个线性执行,界面程序需要持续存在,因此需要一个循环,Looper就是Android里面线程循环的封装。

这样说还是比较抽象,那么Looper到底是什么?

消息机制

任务的循环执行,需要一个队列,可进可出,Android使用消息队列MessageQueue来实现。

一个Looper绑定一个Thread,在这个线程中循环;同时绑定一个MessageQueue,在这个消息队列中存取消息;然后,通过Handler向外接口。通过Handler把消息加入。

MessageQueue, Looper调用loop进行循环,循环地从消息队列中获取消息,处理消息。

Message

§消息出队:MessageQueue::next

§消息入队<- 生成消息:绑定Handler

Handler::obtainMessage

Message::obtainMessage(Handler)

消息队列中的消息是供Looper来消耗的,Looper通过MessageQueue的next方法取出消息。这个过程是在Looper内部完成,我们不需要太过关心。

不过next方法也是比较讲究的,这个方法最终会调用native的方法,可能会等待睡眠,直到IO事件或者消息入队把它唤醒。

Android的Message必须绑定到一个Handler,由这个Handler来发送和处理消息。消息入队的时候会检查消息是否绑定了Handler,如果没有绑定,会直接抛出异常。

因此我们通常是Handler::obtainMessage,这个方法获得的Message直接绑定到了Handler;另外,也会Message::obtainMessage,当前必须传递Handler。

Looper的创建

§Looper.prepare生成Looper实例

§ThreadLocal映射,将Looper和Thread绑定

Looper.prepare();

Looper looper = Looper.myLooper();

...

Looper.loop();

在线程中调用Looper.prepare完成Looper的创建。

通过ThreadLocal映射,Looper与线程绑定。

Looper创建时生成了MessageQueue。

Thread和ThreadLocal都是java的东西,Looper是Android的。Thread和Looper之前的绑定使用了ThreadLocal。ThreadLocal就是线程的存储器,它是一个通用设计,它为每个线程存储数据,从每个线程进来看到对应的线程的数据。因此Android的Looper很好地利用了这一点,使用ThreadLocal,从每个线程进来看到的Looper都是绑定的Looper。

MainLooper的创建

§ActivityThread main入口

Looper. prepareMainLooper();

...

Looper.loop();

主线程入口处(ActivityThread的main入口),调用Looper.prepareMainLooper,完成MainLooper的创建。再调用loop让主线程循环起来。

MainLooper作为静态变量保存在Looper中,可通过getMainLooper获取。

Handler的创建

§Handler——Looper的对外接口

§Handler创建 <- Looper实例

?主线程Handler:直接获取MainLooper来创建。

?其他线程Handler:必须在调用Looper.prepare之后创建。

eg:

HandlerThread::onLooperPrepared

?不指定Looper,使用当前线程Looper。

Handler创建需要指定Looper,因此Handler的创建需要在调用Looper.prepare之后(HandlerThread.onLooperPrepared),否则会报异常。

如果没有Looper,默认使用当前线程绑定的Looper。

因此,通常在主线程可以任意创建Handler,因为MainLooper在主线程启动时已经prepare。

而在其他线程创建Handler时需要先调用Looper.prepare。

非主线线程Handler典型创建方法是通过HandlerThread。HandlerThread是Android联结Handler和线程的封装,它用onLooperPrepared回调提供给外界创建Handler。

线程的创建

§线性执行:直接或间接new Thread

§循环:

1. 基于Looper的Thread ->HandlerThread

2. 自己为线程实现循环机制

线程交互

线程之间的交互通过消息机制完成。具体来说,A线程需要发送消息到B线程,需要通过持有B线程Looper的Handler发送消息。

eg:  A线程需要操作B线程

A发送消息给B

?A发送:A需要Handler实例

?给B:Handler实例必须持有B线程的Looper。

实现:

?B线程生成Handler

?Handler定义消息和消息处理

主线程与非主线程交互

§主线程执行非主线程操作

?获取非主线程Handler发送消息。     针对持久存在的非主线程处理

?通过AsyncTask::doInBackground执行。 针对临时存在的后台线程处理

-View.post(Runnable)。     在View中执行。

-Activity.runOnUiThread。     在Activity中执行。

§非主线程执行主线程操作

?获取MainLooper生成主线程Handler。  针对持久的主线程处理

eg:ViewRootHander,Activity的Handler

?通过AsyncTask::onProgressUpdate或onPostExecute。   针对临时主线程处理

?通过临时new Handler(传递MainLooper)来post执行。

View的post函数将Runnable加入到ViewRootImpl的执行队列中,在下次ViewRootImpl执行tranversal时,将队列任务加入一个主线程的Handler的消息队列。

Activity的runOnUiThread后文再详细讲解。

AsyncTask是Android对主线程和后台线程处理的一个封装,它是线性执行的,不作循环。

AsyncTask

§主线程执行——持有MainLooper的Handler静态成员sHandler,在主线程入口处初始化。

§后台执行——新建线程,设置为优先级:

THREAD_PRIORITY_BACKGROUND

onProgressUpdate

onPostExecute  主线程

doInBackground     后台线程

onPreExecute      取决于调用线程

AsyncTask持有一个静态Handler,由主线程入口处初始化创建,因此它持有MainLooper。onProgressUpdate和onPostExecute是通过该Handler执行的,因此都是主线程操作。

类的静态成员和静态块,是在类加载的时候执行的。在主线程入口的地方,没有必须创建一个AsyncTask实例,但是需要为它初始Handler,因此调用AsyncTask一个无用的静态方法init,仅仅是为了初始化Handler。

AsyncTask的后台执行是通过创建一个新的线程,非设置线程优先级为Background,因此它执行后台操作。

onProgressUpdate和onPostExecute是通过该Handler执行的,因此都是主线程操作。doInBackground是新建线程(THREAD_PRIORITY_BACKGROUND优先级),因此是后台线程操作。onPreExecute在execute中执行,所在线程取决于调用的线程。

线程优先级

§设置线程优先级

?android.os.Process.setThreadPriority:[-20, 19]。越小优先级越高

?java.lang.Thread.setPriority:[1, 10]。越大优先级越高。

§THREAD_PRIORITY_BACKGROUND(10):标准的后台优先级

§默认线程优先级:THREAD_PRIORITY_DEFAULT(0),中等

设置线程优先级有两种方式:

android.os.Process.setThreadPriority:[-20,19]。越小优先级越高

java.lang.Thread.setPriority:[1, 10]。越大优先级越高。

优先级调度是底层实现的,没有具体深入。优先级的设置和执行结果是没有办法准确预期的,但是可以肯定的是Process.setThreadPriority的效果更符合预期。

Activity/Service与主线程

§Activity和Service运行在主线程(ActivityThread$H)

onCreate,onResume,…

§Activity Handler

与AsyncTask类似,Activity内部有一个持有MainLooper的Handler。

Acitivty的启动过程是通过ActivityThread.H这样一个Handler来调用的,这是一个主线程的Handler,因此主线程的启动都在主线程。

另外,与AsyncTask类似,Activity内部有一个持有MainLooper的Handler。因此Activity提供了一个runOnUiThread的方法,方便直接执行UI操作。

by kkmoving



介绍的很全面。

时间: 2025-01-02 01:31:51

Android线程与消息机制的相关文章

Android 线程与消息 机制 15问15答

1.handler,looper,messagequeue三者之间的关系以及各自的角色? 答:MessageQueue就是存储消息的载体,Looper就是无限循环查找这个载体里是否还有消息.Handler就是创建的时候 会使用looper来构建这个消息循环. handler的主要功能就是 将一个任务切换到某个指定的线程中去执行. 2.为何android 无法在子线程更新ui? 答:都知道 更新ui 实际工作都是在viewrootimpl这个类里面去做的.他的源码里有下面这样一个函数: 1 voi

深入解析Android中Handler消息机制

Android提供了Handler 和 Looper 来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(MessageExchange).Handler消息机制可以说是Android系统中最重要部分之一,所以,本篇博客我们就来深入解析Android中Handler消息机制. Handler的简单使用 为什么系统不允许子线程更新UI 因为的UI控件不是线程安全的. 如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么不对UI控件的访

Android中的消息机制

在分析Android消息机制之前.我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListener { private TextView stateText; private Button btn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); s

浅析Android中的消息机制(转)

原博客地址:http://blog.csdn.net/liuhe688/article/details/6407225 在分析Android消息机制之前,我们先来看一段代码: 1 public class MainActivity extends Activity implements View.OnClickListener { 2 private TextView stateText; 3 private Button btn; 4 5 @Override 6 public void onC

浅析Android中的消息机制-解决:Only the original thread that created a view hierarchy can touch its views.

在分析Android消息机制之前,我们先来看一段代码: [html] view plaincopyprint? public class MainActivity extends Activity implements View.OnClickListener { private TextView stateText; private Button btn; @Override public void onCreate(Bundle savedInstanceState) { super.onC

浅析Android中的消息机制(转)

在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListener { private TextView stateText; private Button btn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); s

Android Framework 分析---消息机制Native层

在Android的消息机制中,不仅提供了供Application 开发使用的java的消息循环.其实java的机制最终还是靠native来实现的.在native不仅提供一套消息传递和处理的机制,还提供了自定义文件描述符的I/O时间的监听机制.下面我们从具体代码中分析一下. Native层的关键类: Looper.cpp.该类中提供了pollOnce 和wake的休眠和唤醒机制.同时在构造函数中也创建 管道 并加入epoll的机制中,来监听其状态变化. Looper::Looper(bool al

Android Handler AsyncTask 消息机制

一.Android消息机制一 Android 有一种叫消息队列的说法,这里我们可以这样理解:假如一个隧道就是一个消息队列,那么里面的每一部汽车就是一个一个消息,这里我们先忽略掉超车等种种因素,只那么先进隧道的车将会先出,这个机制跟我们android 的消息机制是一样的. 角色描述 1. Looper:(相当于隧道) 一个线程可以产生一个Looper 对象,由它来管理此线程里的Message Queue( 车队,消息隧道) . 2. Handler: 你可以构造Handler 对象来与Looper

Android开发之消息机制

转:http://stackvoid.com/introduction-to-Message-Handler-in-Android/ http://blog.dreamtobe.cn/2016/03/11/android_handler_looper/ Android应用程序主线程用来跟新UI,所以不能让主线程做费时操作,否则会出现ANR(App Not Response), 一般来说耗时操作都新开启一个线程,新线程执行结束,发消息给主线程来更新UI,常用方法有: Activity.runOnU