Looper的目的是什么?如何使用它?

我是Android新手。 我想知道Looper类做什么以及如何使用它。 我已阅读Android Looper类文档,但我无法完全理解它。 我曾在很多地方看过,但无法理解它的目的。 任何人都可以通过定义Looper的目的来帮助我,也可以通过给出一个简单的例子,如果可能的话?

什么是Looper?

Looper是一个用来执行队列中的消息(Runnables)的类。 普通线程没有这样的队列,例如简单线程没有任何队列。 它执行一次,在方法执行完成后,线程将不会运行另一个消息(Runnable)。

我们可以在哪里使用Looper类?

如果有人想执行多个消息(Runnables),那么他应该使用负责在线程中创build一个队列的Looper类。 例如,在编写从互联网下载文件的应用程序时,我们可以使用Looper类将文件下载到队列中。

怎么运行的?

prepare()方法来准备Looper。 然后你可以使用loop()方法在当前线程中创build一个消息循环,现在你的Looper已经准备好执行队列中的请求,直到你退出循环。

这里是你可以准备活套的代码。

 class LooperThread extends Thread { public Handler mHandler; @Override public void run() { Looper.prepare(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } } 

您可以更好地理解Looper在GUI框架中的作用。 活套是做2件事情。

1)Looper 将一个正常的线程转换成一个正常的线程直到Android的应用程序正在运行 ,这是在GUI框架中所需要的(技术上,它仍然在run()方法返回时终止。在下面澄清我的意思)。

2)Looper 提供了一个排队的工作,在GUI框架中也是需要的。

如您所知,启动应用程序时,系统会为应用程序创build一个称为“main”的执行线程,Android应用程序通常完全在单个线程上运行,默认为“主线程”。 但主线不是一些秘密,特殊的线程 。 这只是一个正常的线程,类似于你用new Thread()代码创build的new Thread() ,这意味着它的run()方法返回时会终止! 想想下面的例子。

 public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } } 

现在,让我们将这个简单的原则应用到Android应用程序。 如果一个Android应用程序在普通线程上运行会发生什么? 一个称为“主”或“用户界面”或任何启动您的应用程序,并绘制所有用户界面的线程。 所以,第一个屏幕显示给用户。 所以现在怎么办? 主线程终止? 不,不应该。 它应该等到用户做了什么,对吧? 但是我们怎样才能做到这一点呢? 那么,我们可以尝试使用Object.wait()Thread.sleep() 。 例如,主线程完成其初始工作以显示第一个屏幕,然后睡觉。 它醒来,意味着中断,当一个新的工作要做。 到目前为止这么好,但是现在我们需要一个类似队列的数据结构来保存多个作业。 考虑一个用户连续触摸屏幕的情况,任务需要较长的时间才能完成。 所以,我们需要有一个数据结构,以先进先出的方式进行工作。 另外,你可能会想象,使用中断来实现永不间断运行的线程并不容易,而且会导致代码复杂且不可维护。 我们宁愿为此目的创build一个新的机制, 这就是Looper的全部内容 。 Looper类的官方文档说:“线程默认没有与它们相关的消息循环”,Looper是一个“用于为线程运行消息循环”的类。 现在你可以明白它的意思了。

为了使事情更清楚,让我们检查一下主线程转换的代码。 这一切都发生在ActivityThread类 。 在它的main()方法中,你可以find下面的代码,它将一个正常的主线程变成我们需要的东西。

 public final class ActivityThread { ... public static void main(String[] args) { ... Looper.prepareMainLooper(); Looper.loop(); ... } } 

Looper.loop()方法无限循环,并且一次处理一个消息并处理一个消息:

 public static void loop() { ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... msg.target.dispatchMessage(msg); ... } } 

所以,基本上Looper是一个解决GUI框架中出现的问题的类。 但是这种需求也可能在其他情况下发生。 实际上它是一个非常着名的multithreading应用程序模式,您可以 Doug Lea的“ Java中的并行编程 ”中了解更多关于它的信息(尤其是4.1.4章节“工作线程”将会有所帮助)。 另外,你可以想象这种机制在Android框架中并不是唯一的,但是所有的GUI框架可能都需要类似于这个。 您可以在Java Swing框架中find几乎相同的机制。

Looper允许在单个线程上顺序执行任务。 处理程序定义了我们需要执行的任务。 这是一个典型的场景,我试图在这个例子中说明:

 class SampleLooper extends Thread { @Override public void run() { try { // preparing a looper on current thread // the current thread is being detected implicitly Looper.prepare(); // now, the handler will automatically bind to the // Looper that is attached to the current thread // You don't need to specify the Looper explicitly handler = new Handler(); // After the following line the thread will start // running the message loop and will not normally // exit the loop unless a problem happens or you // quit() the looper (see below) Looper.loop(); } catch (Throwable t) { Log.e(TAG, "halted due to an error", t); } } } 

现在我们可以在一些其他的线程(比如说ui线程)中使用这个处理器来把这个任务放在Looper上来执行。

 handler.post(new Runnable() { public void run() { //This will be executed on thread using Looper. } }); 

在UI线程上,我们有一个隐式的Looper,它允许我们处理UI线程上的消息。

Android Looper是一个将MessageQueue附加到Thread的包装,它pipe理Queue处理。 它在Android文档中看起来非常神秘,很多时候我们可能面临与Looper相关的UI访问问题。 如果我们不了解基本知识,那么处理起来就非常困难。

这里有一篇文章解释Looper生命周期,如何使用它以及在Handler使用Looper

在这里输入图像描述

Looper = Thread + MessageQueue

Looper是一个将线程转换为Pipeline线程的类, Handler为您提供了一种从任何其他线程将任务推送到其中的机制。

所以PipeLine Thread是一个线程,可以通过Handler接受来自其他线程的更多任务。

Looper被命名,因为它实现了循环 – 接下来的任务,执行它,然后取下一个,等等。 处理程序被称为处理程序,因为它用于处理或接受来自任何其他线程的下一个任务,并传递给Looper(线程或PipeLine线程)。

一个Looper和Handler或PipeLine Thread就是一个非常完美的例子,它可以下载一个以上的图像,或者在一个线程中将其上传到一个服务器(Http),而不是在后台为每个networking调用启动一个新的Thread。

在这里阅读更多关于Looper和Handler以及Pipeline Thread的定义:

Android Guts:介绍活页夹和处理程序

Looper具有一个synchronized MessageQueue ,用于处理放置在队列中的消息。

它实现了一个Thread特定的存储模式。

每个Thread只有一个Looper 。 关键的方法包括prepare()loop()quit()

prepare()将当前的Thread初始化为一个Looperprepare()是使用ThreadLocal类的static方法,如下所示。

  public static void prepare(){ ... sThreadLocal.set (new Looper()); } 
  1. 在运行事件循环之前,必须明确调用prepare()
  2. loop()运行事件循环,等待消息到达特定线程的消息队列。 一旦接收到下一个消息, loop()方法就会将消息分派给它的目标处理程序
  3. quit()closures事件循环。 它不会终止循环,而是排队一个特殊的消息

Looper可以通过多个步骤在一个Thread进行编程

  1. 扩展Thread

  2. 调用Looper.prepare()将Thread初始化为一个Looper

  3. 创build一个或多个Handler程序来处理传入的消息

  4. 调用Looper.loop()来处理消息,直到循环被告知quit()

Java的生命周期完成run()方法后, Thread已经结束。 同一个线程不能再次启动。

Looper将普通的Thread转换成消息循环。 Looper关键方法是:

 void prepare () 

将当前线程初始化为循环。 这给了你一个创build处理程序的机会,然后在实际启动循环之前引用这个循环。 确保在调用此方法后调用loop(),并通过调用quit()来结束它。

 void loop () 

在此线程中运行消息队列。 一定要调用quit()来结束循环。

 void quit() 

退出活套。

导致loop()方法终止,而不处理消息队列中的更多消息。

Janishar的这篇mindorks文章很好地解释了核心概念。

在这里输入图像描述

Looper与一个线程相关联。 如果你需要UI线程的LooperLooper.getMainLooper()将返回相关的线程。

你需要Looper与一个Handler相关联。

LooperHandlerHandlerThread是Android解决asynchronous编程问题的方法。

一旦你有Handler ,你可以调用下面的API。

 post (Runnable r) 

导致Runnable r被添加到消息队列中。 runnable将在该处理程序所连接的线程上运行。

 boolean sendMessage (Message msg) 

在当前时间之前的所有待处理消息之后,将消息推送到消息队列的末尾。 它将在handleMessage(Message)中被接收到,在附加到该处理程序的线程中。

HandlerThread是一个方便的类,用于启动一个具有活套的新线程。 然后可以使用循环来创build处理程序类

在某些情况下,您不能在UI线程上运行Runnable任务。 例如networking操作:在套接字上发送消息,通过读取InputStream打开一个URL并获取内容

在这些情况下, HandlerThread是有用的。 您可以从HandlerThread获取Looper对象,并在HandlerThread而不是主线程上创buildHandler

HandlerThread代码将如下所示:

 @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } 

示例代码参考下面的post:

Android:在一个线程中吐司

在服务中处理多个下载或上传项目是一个更好的例子。

HandlerAsnycTask通常用于在UI(线程)和工作线程之间传播Events / Messages或延迟动作。 所以他们更关系到UI。

即使没有用户交互或显示的UI(应用程序在通话期间在后台下载文件), Looper在后台的线程相关队列中处理任务( Runnables,Futures )。