首页 > 试题广场 >

Android中Looper的实现原理,为什么调用Loope

[问答题]
Android中Looper的实现原理,为什么调用Looper.prepare()就在当前线程关联了一个Looper对象,它是如何实现的。
推荐
1、线程间通信机制
首先,looper、handler、messagequeue三者共同实现了android系统里线程间通信机制。如在A、B两个子线程之间需要传递消息,首先给每个子线程绑定一套handler、looper、messagequeue机制,然后这三个对象都与其所属线程对应。然后A线程通过调用B线程的Handler对象,发送消息。这个消息会被Handler发送到B线程的messagequeue中,而属于B线程的Looper对象一直在for循环里无限遍历MessageQueue, 一旦发现该消息队列里收到了新的消息,就会去对消息进行处理,处理过程中会回调自身Handler的heandleMessage方法。从而实现了不同线程间通信。
2、Looper实现原理
Looper类里包含一个消息队列对象和一个线程对象。当创建Looper时,会自动创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当开启Looper后(looper.loop()),会自动进入无限for循环中,不断去遍历消息队列,如果没有消息则阻塞,有消息则回调handler的handlemessage方法进行处理。
3、Looper.prepare()
首先,要使用Looper机制一般会在当前线程中创建Handler对象,里面会自动创建一个looper对象和消息队列,这里面的消息队列属于当前线程空间。但此时的looper还不会去遍历,也没有绑定到当前线程。其中,looper对象内部也包含一个空消息队列对象和空线程。通过Looper.prepare()方法,先让该消息队列指向当前线程的消息队列,让空线程也指向当前线程。从而实现了绑定。
编辑于 2015-09-14 12:18:14 回复(13)
Android提供的Looper、MessageQueue可以帮助我们实现流水线工作的线程
核心类就是Looper啦
题中问线程是如何关联上一个Looper对象的

首先Looper对象会保存一个与之关联的线程的引用
看代码:mThread = Thread. currentThread () ;
然后呢,再把自己变成线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
··· sThreadLocal.set(new Looper(quitAllowed));

这样它们就关联起来了

编辑于 2015-08-31 20:57:47 回复(0)

图片说明

首先看下prepare()的源码,注意到sThreadLocal,Looper正是通过ThreadLocal来获取到当前线程的looper对象的。下面讲下ThreadLocal类的特性:

ThreadLocal是线程内部的数据存储类,当使用ThreadLocal维护变量的时候,它会为每个使用该变量的线程提供一个独立的变量副本,这个变量副本是该线程独有,不受其他线程影响。

因此当Looper的prepare()中调用
sThreadLocal.get()时,就获取到了当前线程的一个独有的Looper对象,即在在当前线程关联了一个Looper对象,Looper的实现正是巧妙利用了Threadlocal的特性来实现的,避免了去使用哈希表来维护不同线程及其对应Looper的映射关系。

参考:

《Android开发艺术探索》p375
彻底理解ThreadLocal

发表于 2017-09-04 11:52:15 回复(0)
## 问答题 ##
 Android中Looper的实现原理,为什么调用Looper.prepare()就在当前线程关联了一个Looper对象,它是如何实现的。

首先该问题,有三个子问题,a Looper实现原理 ,b 调用Looper.prepare()就在当前线程关联了一个Looper对象 , c 它是如何实现的,这个它是谁?我认为是Looper.prepare() 关联Looper对象。
a.Looper实现原理
    - Looper可以理解为一个类似轮询器
    - Looper在创建的时候,会自动创建一个MessageQueue(消息队列),和一个线程。
    - 将内部线程对象指向自动创建的线程。
    - 然后当Looper开启的时候,去不断遍历“询问”消息队列,如果没有消息,队列为空,那么就继续轮询。如果有消息进入队列,则对消息进行处理,回调handler的handlemessage方法进行处理
    - 这样就完成了线程通信的一部分
b.c.Looper.prepare()方法关联一个Looper对象
    - 首先逗逼一下的说,问我为什么,肯定会说是系统设计或者方法设计的要求啊,2333,用了Looper.prepare就是会关联Looper对象,2333
    - 那么要考虑一个问题,Looper.prepare()是谁执行这个方法,是一个Looper对象,那么关联的Looper对象是谁?
    - 在相关源码里有注释如下:初始化当前线程作为一个looper,在looper开始之前,参考这个looper,去创建一个handlers。然后一堆不知所云,2333
    - public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread");  } sThreadLocal.set(new Looper(quitAllowed)); }
    - 看上面的方法,我们看到prepare针对不同的参数,又不一样的重构方法,但是通过一步步调用,我们还是看到了sThreadLocal.set(new Looper(quitAllowed));
    - prepare方法中会调用一个prepare(true)--->之后去set一个新new的Looper对象
    - 那么为什么呢?是因为源码里就这样用的?到底怎么梳理总结呢?
    - threadLocal是线程内部的数据储存类,储存了一个线程的相关信息。线程关联一个(set)looper对象之后,通过looper的loop方法,再低通threadLocal的get方法,可以将之前关联的对象提取出来,对该对象的messageQueue进行轮询。
    - 每一个线程都有一个独立的looper,去轮询相应的消息队列。
c.假如问的是这一堆是如何实现的
    - 在进行通信时,我们每一个线程都会有自己对应的handler,looper,messagequeue
    - 两个线程要通信,那么一线程调用二线程的Handler对象,发送消息。这个消息会被Handler发送到二线程的Messagequeue,同时被二线程的looper轮询到,然后处理,用二线程的handlenMessage方法
发表于 2016-08-07 19:06:53 回复(0)
想说的是Looper.prepare()?该方法调用Looper的构造,Looper()中新建MessageQueue和绑定当前的Thread。
源码地址
:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/os/Looper.java#Looper.prepare%28%29
发表于 2015-03-08 22:57:24 回复(0)
 /** 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() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread");  } sThreadLocal.set(new Looper(quitAllowed)); }
直接贴上源码咯,关联的话,主要是通过,sThreadLocal的set方法进行关联的,而threadLocal是线程内部的数据存储类,该类存储了线程的所有数据信息。
/**  * Sets the value of this variable for the current thread. If set to  * {@code null}, the value will be set to null and the underlying entry will  * still be present.  *  * @param value the new value of the variable for the caller thread.  */ public void set(T value) {
    Thread currentThread = Thread.currentThread();  Values values = values(currentThread);  if (values == null) {
        values = initializeValues(currentThread);  }
    values.put(this, value); }
存储了相关的信息后,通过looper的loop()方法,调用threadLocal的get()方法再把之前新创建的looper对象提取出来,对looper对象中messageQueue进行轮询信息,查询到有消息就调用handler的dispatchMessage()分发消息出去,这时候就会响应handlerMessage,实现跨线程通信了。

总的来说,handler调用sendMessage,就往handler的messageQueue里装消息,而messageQueue就是恰好就是装进在初始化handler的那个looper环境中的,一般在UI主线程中会默认创建了looper,但是在子线程,需要为handler独立创建messageQueue空间,这样,使得调用sendMessage时,能够有地方放message。
调用Looper.loop()会获取到相应的messageQueue,将会丢其信息进行轮询操作,一旦接收到message到来,就会响应message.target即目标handler的handlerMessage,消息到这里算是处理完毕。
最为关键是使用了ThreadLocal来存储不同的looper,这样,每个线程都有一个独立的looper,这样,对应的handler才能处理相关的信息。

发表于 2016-02-24 19:55:41 回复(0)
prepare方法源码
可以看出new了一个Looper的实例并用ThreadLocal进行存储。
至于当前线程是如何关联这个Looper对象,可以看ThreadLocal源码:
ThreadLocal实例调用set方法时首先判断成员变量map指针是否为空,非空则直接以该ThreadLocal对象为键,存储Looper实例。
指针为空则创建ThreadLocalMap实例并赋值给当前线程的threadLocals成员变量,这样当前线程通过关联该map间接地关联Looper实例。


发表于 2015-09-04 16:53:44 回复(0)
wb头像 wb
Looper.prepare()方法是将当前线程绑定一个looper实例,并存储在TreadLocal中,一个线程只有一个looper对象。
looper中,有一个messagequeue,即消息队列;
handler可以通过sendmessage 等方法向队列中发送消息。
一个线程中可以有多个handler,handler 绑定当前线程的looper对象,以及其中的消息队列;
looper.loop()方法即循环调用MQ中的message,并且调用方送它的handler来处理它。
发表于 2015-04-21 18:06:16 回复(0)
oow头像 oow
Looper内部有一个ThreadLocal数据结构存放Looper的集合,key是当前线程,value是Looper,当调用prepare()时,回去ThreadLocal里面查找是否存在当前线程的looper,如果存在则取出,不存在则创建新的Looper,并存入ThreadLocal;由于主线程Looper实在ActivityThread启动时的main方法中已经调用了,所以自动给UI线程关联上Looper
发表于 2018-03-20 16:14:16 回复(0)
推荐的答案有问题:创建handler会自动创建一个looper?搞笑。。看过源码么?自己在非UI线程去写代码试试,不报异常崩溃算我傻。。

Looper负责管理消息队列,主要方法是looper.prepare和looper.loop方法
Looper.prepare方法:
        首先得到当前线程的threadLocal对象,从当前ThreadLocal对象中获取looper对象,如果为空,则新建一个looper对象,之后存储在当前thread对象的threadLoacal对象中。
Looper.loop方法
          核心是一个for的死循环,在该循环中主要有两个地方值得注意:1. 一直调用messageQueue的next方法(一直在从消息队列取出消息)2. 对于取出的Message交给:msg.target.dispathMessage(Message message)去执行,实际上就是又交给了handler去处理了。
发表于 2017-08-24 20:26:25 回复(0)
1、线程间通信机制
首先,Looper、Handler、MessageQueue三者共同实现了Android系统里线程间通信机制。如在A、B两个子线程之间需要传递消息,首先给每个子线程绑定一套Looper、Handler、MessageQueue机制,然后这三个对象都与其所属线程对应。然后A线程通过调用B线程的Handler对象,发送消息。这个消息会被Handler发送到B线程的MessageQueue中,而属于B线程的Looper对象一直在for循环里无限遍历MessageQueue, 一旦发现该消息队列里收到了新的消息,就会去对消息进行处理,处理过程中会回调自身Handler的handleMessage方法,从而实现了不同线程间通信。
2、Looper实现原理
Looper类里包含一个消息队列对象和一个线程对象。当创建Looper时,会自动创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当开启Looper后(Looper.loop()),会自动进入无限for循环中,不断去遍历消息队列,如果没有消息则阻塞,有消息则回调HandlerhandleMessage方法进行处理。loop方法唯一跳出循环的方式是MessageQueue的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。
3、Looper.prepare()
首先,要使用Looper机制一般会在当前线程中创建Handler对象,里面会自动创建一个Looper对象和消息队列,这里面的消息队列属于当前线程空间。但此时的Looper还不会去遍历,也没有绑定到当前线程。其中,Looper对象内部也包含一个空消息队列对象和空线程。通过Looper.prepare()方法,先让该消息队列指向当前线程的消息队列,让空线程也指向当前线程,从而实现了绑定。
发表于 2017-03-31 15:48:01 回复(0)
答:android中的Looper是一个消息处理机制,一个handler创建必须有一个looper,handler里面有messageQueue和Looper.messageQueue负责存储消息队列,looper是负责去消息队列里面查看是否有新的消息需要处理,如果需要,则由looper处理交给handler中的dispatchmessage来处理。而在android中的主线程中默认生成了looper对象,因此可以创建handler.但是在子线程中创建handler之后必须主动的去创建looper对象,因此需要使用looper.prepare().接着开启looper对象就是looper.loop();
发表于 2016-04-19 07:47:08 回复(0)
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

发表于 2016-04-12 19:55:52 回复(0)
 public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

  private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

发表于 2016-04-01 23:02:04 回复(0)
2、Looper实现原理
Looper类里包含一个消息队列对象和一个线程对象。当创建Looper时,会自动创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当开启Looper后(looper.loop()),会自动进入无限for循环中,不断去遍历消息队列,如果没有消息则阻塞,有消息则回调handler的handlemessage方法进行处理。
3、Looper.prepare()
首先,要使用Looper机制一般会在当前线程中创建Handler对象,里面会自动创建一个looper对象和消息队列,这里面的消息队列属于当前线程空间。但此时的looper还不会去遍历,也没有绑定到当前线程。其中,looper对象内部也包含一个空消息队列对象和空线程。通过Looper.prepare()方法,先让该消息队列指向当前线程的消息队列,让空线程也指向当前线程。从而实现了绑定。
发表于 2016-04-01 21:08:07 回复(0)
线程创建时默认是不会自动创建一个Looper对象的,Ui线程除外,它会在创建时自动产生一个Looper对象。在子线程中,如果要创建一个Looper对象,使用Looper.prepare()就会调用Looper的构造函数,然后产生一个Looper对象,而且构造函数中会产生一个对应的MessageQueue,这样当主线程创建一个Handler对象时,关联到Looper对象后,便形成了一个内部消息循环系统。
发表于 2016-03-29 15:46:31 回复(0)
Android中线程间的通信机制是通过Looper、Handler、MessageQueue来实现的。
Looper的实现原理:Looper类中包含一个线程对象和一个消息队列对象。Looper的构造方法中创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当Looper.loop()方法开启后,会自动进入无限for循环中,不断的遍历异步消息队列,如果没有消息则阻塞,有消息的话则回调Handler的handleMessage方法来进行处理。

Looper的prepare方法中首先去检查Looper对象是否已经创建,如果已经创建则抛出异常,说明Looper.prepare方法只能被调用一次,同时也保证了一个线程只有一个Looper实例。
发表于 2016-03-25 10:07:13 回复(0)
1.Looper和当前线程的关联表现为Looper的mThread成员变量被赋值为当前线程 
    private Looper(boolean quitAllowed)
    {
        //新建消息队列
        mQueue = new MessageQueue(quitAllowed);
        //获得当前线程
        mThread = Thread.currentThread();
    }
2.当前线程和Looper的关联表现为Thread的localValues成员变量被赋值
这个变量用于保存键值对,对于Android消息机制来说,键就是ThreadLocal实例,值就是Looper实例。
使用ThreadLocal还保证了一个线程只能有一个Looper,
因为prepare()实现中先取当前线程取Looper实例,当没有取到时才新建Looper实例。 
    private static void prepare(boolean quitAllowed)
    {
        //如果当前线程已经有Looper则抛出异常
        if (sThreadLocal.get() != null)
        {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将Looper实例赋值给当前线程的localValues成员变量
        sThreadLocal.set(new Looper(quitAllowed));
    }

发表于 2015-12-18 08:45:37 回复(0)
looper类中有ThreadLocal<Looper>线程局部变量,Looper.prepare()方法中:new了一个Looper对象,并放入ThreadLocal<Looper>,从而线程变量中就有了Looper,looper就和线程关联起来了。
此外,Handler也是通过 ThreadLocal<Looper>来get的Looper对象,使他俩关联起来

发表于 2015-11-24 17:05:42 回复(0)
<div> if(sThreadLocal.get()!=null){ </div> <div> &nbsp; &nbsp; throw new RuntimeException("Only one looper can be created per thread");<br /> </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;<strong></strong>sThreadLocal.set(new Looper()); </div> <div> } </div>
发表于 2015-09-02 21:18:51 回复(0)
<div> 调用Looper的prepare()方法为当前线程创建了一个Looper实例,并创建配套的MessageQueue。 </div> <div> prepare()方法保证每个线程最多只有一个Looper对象,prepare()方法的源代码如下: </div> <div> public static final void prepare() </div> <div> { </div> <div> &nbsp; &nbsp; if(sThreadLocal.get()!=null)<br /> </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;{ </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; throw new RuntimeException("Only one Looper may be created per thread");<br /> </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;} </div> <div> &nbsp; &nbsp; sThreadLocal.set(new Looper());<br /> </div> <div> } </div> <div> <br /> </div> <div> Looper提供的构造器源代码如下: </div> <div> private Looper() </div> <div> { </div> <div> &nbsp; &nbsp; mQueue=new MessageQueue();<br /> </div> <div> &nbsp; &nbsp; mRun=true;<br /> </div> <div> &nbsp; &nbsp; mThread=Thread.currentThread();<br /> </div> <div> } </div>
发表于 2015-08-28 20:42:30 回复(0)