Android札记技术干货程序员

Android线程与消息机制

2014-07-29  本文已影响2126人  kkmoving

OUTLINE

§UI线程

§Looper

§消息机制

§线程交互

§AsyncTask

§Activity/Service与主线程

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操作。

【茶工坊】

上一篇下一篇

猜你喜欢

热点阅读