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

Kha*_*aza 441 multithreading android android-looper

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

Dha*_*dra 386

什么是Looper?

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

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

如果有人想要执行多个消息(Runnables),那么他应该使用负责在线程中创建队列的Looper类.例如,在编写从Internet下载文件的应用程序时,我们可以使用Looper类将要下载的文件放入队列中.

这个怎么运作?

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

这是您可以准备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();
      }
  }
Run Code Online (Sandbox Code Playgroud)

  • AsyncTask更适用于此目的,并且因为它封装了所有线程管理而不那么复杂. (17认同)
  • 我认为最好使用[HandlerThread](http://developer.android.com/reference/android/os/HandlerThread.html),这对于带有looper的线程来说是一个方便的类. (6认同)
  • 文档表明您必须调用looper.quit.在上面的代码中,Looper.loop将无限期地阻止. (5认同)
  • 在run()和handleMessage()方法之前应该有@Override注释 (4认同)
  • 这并不能解释为什么要使用这个类,而只能解释如何使用。 (3认同)
  • 如何退出循环.我的意思是在上面的代码示例中包含Looper.quit()的位置? (2认同)
  • 太棒了...有人知道如何向 LooperThread 发送消息吗?这是完全没用的例子。每个人都只是从文档中复制了愚蠢的示例。 (2认同)

김준호*_*김준호 266

您可以更好地了解Looper在GUI框架中的作用.Looper是做两件事的.

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

2)Looper 提供了一个队列,其中要完成的作业被排队,这在GUI框架中也是必需的.

您可能知道,当启动应用程序时,系统会为应用程序创建一个执行线程,称为"main",而Android应用程序通常完全在单个线程上运行,默认情况下为"主线程".但主线程不是一些秘密,特殊线程.它只是一个类似于您使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们将这个简单的原则应用于Android应用程序.如果Android应用程序在正常线程上运行会发生什么?一个名为"main"或"UI"的线程或任何启动应用程序的线程,并绘制所有UI.因此,第一个屏幕显示给用户.所以现在怎么办?主线程终止了吗?不,它不应该.它应该等到用户做某事,对吧?但是我们怎样才能实现这种行为呢?好吧,我们可以尝试Object.wait()Thread.sleep().例如,主线程完成其初始作业以显示第一个屏幕,然后休眠.当需要执行新工作时,它会唤醒,这意味着中断.到目前为止一切顺利,但此时我们需要一个类似队列的数据结构来保存多个作业.想想用户连续触摸屏幕的情况,任务需要更长的时间才能完成.因此,我们需要一个数据结构来保持以先进先出的方式完成的工作.此外,您可以想象,使用中断实现永远运行和进程的作业到达线程并不容易,并导致复杂且通常无法维护的代码.我们宁愿为此目的创建一个新机制,这就是Looper的意义所在.Looper类官方文档说,"默认情况下,线程没有与它们相关的消息循环",而Looper是一个"用于为线程运行消息循环"的类.现在你可以理解它的含义了.

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

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

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);
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,基本上Looper是一个用于解决GUI框架中出现的问题的类.但是这种需求也可能在其他情况下发生.实际上它是一个非常着名的多线程应用程序模式,你可以 Doug Lea的" Concurrent Programming in Java "中了解它(特别是第4.1.4节"工作线程"会很有帮助).此外,您可以想象这种机制在Android框架中并不是唯一的,但是所有GUI框架可能都需要与此类似.您可以在Java Swing框架中找到几乎相同的机制.

  • 这是唯一能够解释为何使用Looper类的原因的答案.我不确定为什么它不是最佳答案,三个评级较高的答案什么都不解释. (25认同)
  • @AK.这就是为什么我添加这个答案,即使它似乎为时已晚.我很高兴我的回答对你有帮助!:) (4认同)

小智 74

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);
  } 
}
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以在其他一些线程(比如ui线程)中使用处理程序在Looper上发布任务来执行.

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

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

  • 感谢您提供有关如何将"作业"发布到队列的示例 (3认同)
  • 这并不能解释为什么要使用这个类,而只能解释如何使用。 (2认同)

小智 31

Android Looper是附加MessageQueueThread它的包装器,它管理队列处理.它在Android文档中看起来非常神秘,很多时候我们可能会遇到Looper相关的UI访问问题.如果我们不了解基础知识就很难处理.

这里是一个文章这也解释了Looper生命周期,如何使用它和使用LooperHandler

在此输入图像描述

Looper = Thread + MessageQueue

  • 这并不能解释为什么人们会使用这个类,只能使用这个类. (3认同)

ahm*_*och 13

Looper和Handler的定义:

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

细节:

因此,PipeLine Thread是一个可以通过Handler从其他线程接受更多任务的线程.

活套被命名是因为它实现了循环-需要下一个任务,执行它,然后采取下一个等.Handler被称为处理程序,因为它用于每次从任何其他线程处理或接受下一个任务并传递给Looper(Thread或PipeLine Thread).

例:

Looper和Handler或PipeLine Thread的非常完美的例子是下载多个图像或者在一个线程中逐个上传到服务器(Http),而不是在后台为每个网络调用启动一个新的Thread.

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

Android Guts:Loopers和Handlers简介


Hit*_*ahu 11

了解 Looper 线程

Java 线程是一个执行单元,旨在在其 run() 方法中执行任务并在此之后终止: 在此处输入图片说明

但是在 Android 中有很多用例,我们需要保持线程处于活动状态并等待用户输入/事件,例如。UI线程又名Main Thread

Android 中的主线程是一个 Java 线程,它在应用程序启动时首先由 JVM 启动,并一直运行直到用户选择关闭它或遇到未处理的异常。

当应用程序启动时,系统会为该应用程序创建一个执行线程,称为“main”。这个线程非常重要,因为它负责将事件分派到适当的用户界面小部件,包括绘图事件。

在此处输入图片说明

现在需要注意的是,虽然主线程是 Java 线程,但它一直在监听用户事件并在屏幕上绘制 60 fps 帧,并且在每个循环后它仍然不会死。怎么会这样?

答案是 Looper 类:Looper 是一个类,用于保持线程处于活动状态并管理消息队列以在该线程上执行任务。

默认情况下,线程没有关联的消息循环,但您可以通过在 run 方法中调用 Looper.prepare() 分配一个消息循环,然后调用 Looper.loop()。

Looper 的目的是让线程保持活动状态并等待输入Message对象的下一个周期执行计算,否则将在第一个执行周期后被销毁。

如果您想深入了解 Looper 如何管理Message对象队列,那么您可以查看以下源代码Looperclass

https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/os/Looper.java

以下是如何使用创建类Looper Thread并与Activity类进行通信的示例LocalBroadcast

class LooperThread : Thread() {

    // sendMessage success result on UI
    private fun sendServerResult(result: String) {
        val resultIntent = Intent(ServerService.ACTION)
        resultIntent.putExtra(ServerService.RESULT_CODE, Activity.RESULT_OK)
        resultIntent.putExtra(ServerService.RESULT_VALUE, result)
        LocalBroadcastManager.getInstance(AppController.getAppController()).sendBroadcast(resultIntent)
    }

    override fun run() {
        val looperIsNotPreparedInCurrentThread = Looper.myLooper() == null

        // Prepare Looper if not already prepared
        if (looperIsNotPreparedInCurrentThread) {
            Looper.prepare()
        }

        // Create a handler to handle messaged from Activity
        handler = Handler(Handler.Callback { message ->
            // Messages sent to Looper thread will be visible here
            Log.e(TAG, "Received Message" + message.data.toString())

            //message from Activity
            val result = message.data.getString(MainActivity.BUNDLE_KEY)

            // Send Result Back to activity
            sendServerResult(result)
            true
        })

        // Keep on looping till new messages arrive
        if (looperIsNotPreparedInCurrentThread) {
            Looper.loop()
        }
    }

    //Create and send a new  message to looper
    fun sendMessage(messageToSend: String) {
        //Create and post a new message to handler
        handler!!.sendMessage(createMessage(messageToSend))
    }


    // Bundle Data in message object
    private fun createMessage(messageToSend: String): Message {
        val message = Message()
        val bundle = Bundle()
        bundle.putString(MainActivity.BUNDLE_KEY, messageToSend)
        message.data = bundle
        return message
    }

    companion object {
        var handler: Handler? = null // in Android Handler should be static or leaks might occur
        private val TAG = javaClass.simpleName

    }
}
Run Code Online (Sandbox Code Playgroud)

用法

 class MainActivity : AppCompatActivity() {

    private var looperThread: LooperThread? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start looper thread
        startLooperThread()

        // Send messages to Looper Thread
        sendMessage.setOnClickListener {

            // send random messages to looper thread
            val messageToSend = "" + Math.random()

            // post message
            looperThread!!.sendMessage(messageToSend)

        }   
    }

    override fun onResume() {
        super.onResume()

        //Register to Server Service callback
        val filterServer = IntentFilter(ServerService.ACTION)
        LocalBroadcastManager.getInstance(this).registerReceiver(serverReceiver, filterServer)

    }

    override fun onPause() {
        super.onPause()

        //Stop Server service callbacks
     LocalBroadcastManager.getInstance(this).unregisterReceiver(serverReceiver)
    }


    // Define the callback for what to do when data is received
    private val serverReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val resultCode = intent.getIntExtra(ServerService.RESULT_CODE, Activity.RESULT_CANCELED)
            if (resultCode == Activity.RESULT_OK) {
                val resultValue = intent.getStringExtra(ServerService.RESULT_VALUE)
                Log.e(MainActivity.TAG, "Server result : $resultValue")

                serverOutput.text =
                        (serverOutput.text.toString()
                                + "\n"
                                + "Received : " + resultValue)

                serverScrollView.post( { serverScrollView.fullScroll(View.FOCUS_DOWN) })
            }
        }
    }

    private fun startLooperThread() {

        // create and start a new LooperThread
        looperThread = LooperThread()
        looperThread!!.name = "Main Looper Thread"
        looperThread!!.start()

    }

    companion object {
        val BUNDLE_KEY = "handlerMsgBundle"
        private val TAG = javaClass.simpleName
    }
}
Run Code Online (Sandbox Code Playgroud)

我们可以改用异步任务或意图服务吗?

  • 异步任务旨在在后台执行一个简短的操作,并在 UI 线程上提供进度和结果。异步任务有限制,例如您不能创建超过128 个异步任务,并且ThreadPoolExecutor最多只允许5 个异步任务

  • IntentServices还设计用于执行更长持续时间的后台任务,您可以LocalBroadcast用来与Activity. 但是服务在任务执行后被销毁。如果你想让它运行很长时间,那么你需要做一些像 while(true){...}.

Looper Thread 的其他有意义的用例:

  • 用于 2 路套接字通信,其中服务器继续侦听客户端套接字并回写确认

  • 后台位图处理。将图像 url 传递给 Looper 线程,它将应用滤镜效果并将其存储在临时位置,然后广播图像的临时路径。


The*_*heo 7

活套具有synchronized MessageQueue这些会用来处理放置在队列中的消息.

它实现了Thread特定的存储模式.

只有一个LooperThread.主要的方法包括prepare(),loop()quit().

prepare()将当前初始化Thread为a Looper.prepare()static使用ThreadLocal如下所示类的方法.

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
Run Code Online (Sandbox Code Playgroud)
  1. prepare() 必须在运行事件循环之前显式调用.
  2. loop()运行事件循环,等待消息到达特定线程的消息队列.收到下一条消息后,该loop()方法将Message分派给其目标处理程序
  3. quit()关闭事件循环.它不会终止循环,而是将特殊消息排入队列

Looper可以Thread通过几个步骤编程

  1. 延伸 Thread

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

  3. 创建一个或多个Handler(s)以处理传入的消息

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


Rav*_*abu 5

完成方法后,Java Thread的生命周期结束run().相同的线程无法再次启动.

Looper将normal Thread转换为消息循环.关键方法Looper是:

void prepare ()
Run Code Online (Sandbox Code Playgroud)

将当前线程初始化为looper.这使您有机会创建处理程序,然后在实际启动循环之前引用此循环器.确保在调用此方法后调用loop(),并通过调用quit()结束它.

void loop ()
Run Code Online (Sandbox Code Playgroud)

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

void quit()
Run Code Online (Sandbox Code Playgroud)

退出弯针.

导致loop()方法终止,而不处理消息队列中的任何其他消息.

mindorks文章通过Janishar解释了不错的方式的核心概念.

在此输入图像描述

Looper与线程相关联.如果你需要LooperUI线程,Looper.getMainLooper()将返回关联的线程.

您需要Looper处理程序关联.

Looper,HandlerHandlerThreadAndroid是解决异步编程问题的方法.

完成后Handler,您可以调用以下API.

post (Runnable r)
Run Code Online (Sandbox Code Playgroud)

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

boolean sendMessage (Message msg)
Run Code Online (Sandbox Code Playgroud)

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

HandlerThread是用于启动具有looper的新线程的便捷类.然后可以使用looper来创建处理程序类

在某些情况下,您无法Runnable在UI线程上运行任务.例如网络操作:在套接字上发送消息,打开URL并通过阅读获取内容InputStream

在这些情况下,HandlerThread是有用的.你可以Looper从对象HandlerThread和创建HandlerHandlerThread,而不是主线程.

HandlerThread代码将是这样的:

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

请参阅下面的帖子,例如代码:

Android:在线程中敬酒


Tac*_*0sS 5

这个答案与问题无关,但是循环器的使用以及人们在此处的所有答案中创建处理程序和循环器的方式都是明显的不好的做法(尽管有些解释是正确的),我必须发布此内容:

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);
Run Code Online (Sandbox Code Playgroud)

全面实施