Android线程与消息机制

作者: kkmoving | 来源:发表于2014-07-29 22:43 被阅读2126次

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

    【茶工坊】

    相关文章

      网友评论

        本文标题:Android线程与消息机制

        本文链接:https://www.haomeiwen.com/subject/clnstttx.html