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

发布于 2024-12-07 14:49:23 字数 228 浏览 2 评论 0 原文

我是安卓新手。我想知道 Looper 类的作用以及如何使用它。我已阅读 Android Looper 类文档 但我无法完全理解它。 我在很多地方见过它,但无法理解它的用途。任何人都可以帮助我定义 Looper 的用途,并在可能的情况下给出一个简单的示例吗?

I am new to Android. I want to know what the Looper class does and also how to use it. I have read the Android Looper class documentation but I am unable to completely understand it.
I have seen it in a lot of places but unable to understand its purpose. Can anyone help me by defining the purpose of Looper and also by giving a simple example if possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

简美 2024-12-14 14:49:23

什么是Looper?

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

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

如果有人想要执行多个消息(Runnables)那么他应该使用Looper类,它负责在线程中创建一个队列。
例如,在编写一个从互联网下载文件的应用程序时,我们可以使用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();
      }
  }

What is Looper?

Looper is a class which is used to execute the Messages(Runnables) in a queue. Normal threads have no such queue, e.g. simple thread does not have any queue. It executes once and after method execution finishes, the thread will not run another Message(Runnable).

Where we can use Looper class?

If someone wants to execute multiple messages(Runnables) then he should use the Looper class which is responsible for creating a queue in the thread.
For example, while writing an application that downloads files from the internet, we can use Looper class to put files to be downloaded in the queue.

How it works?

There is prepare() method to prepare the Looper. Then you can use loop() method to create a message loop in the current thread and now your Looper is ready to execute the requests in the queue until you quit the loop.

Here is the code by which you can prepare the 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();
      }
  }
恰似旧人归 2024-12-14 14:49:23

在GUI框架的背景下你可以更好的理解Looper是什么。 Looper 可以做两件事。

  1. Looper 将普通线程转换为连续运行的线程,直到 Android 应用程序运行。

  2. Looper 提供了一个队列,将要完成的作业排入队列。

如您所知,当应用程序启动时,系统会为应用程序创建一个执行线程,称为“主线程”,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();
    }
}

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

为了让事情更清楚,让我们检查一下主线程被转换的代码。这一切都发生在 ActivityThread 类< /a>.在它的 main() 方法中,您可以找到下面的代码,它将普通线程变成我们需要的东西。

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 框架中出现的问题的类。但这种需求也可能发生在其他情况下。实际上,它是多线程应用程序的一个非常著名的模式,您可以在 Doug Lea 的“Java 中的并发编程”中了解更多信息(特别是第 4.1.4 章“工作线程”)有帮助)。另外,你可以想象这种机制在Android框架中并不是唯一的,但所有GUI框架可能都需要有些相似。您也可以在 Java Swing 框架中找到几乎相同的机制。

You can better understand what Looper is in the context of the GUI framework. Looper is made to do 2 things.

  1. Looper transforms a normal thread into something run continuously until Android app is running.

  2. Looper provides a queue where jobs to be done are enqueued.

As you may know, when an application is launched, the system creates a thread of execution for the application, called “main”, and Android applications normally run entirely on a single thread by default the “main thread”. But main thread is not a secret, special thread. It's just a normal thread similar to one you can create with new Thread(), which means it terminates when its run() method returns! Think of below example.

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();
    }
}

Now, let's apply this simple principle to Android apps. What would happen if an Android app runs on a normal thread? A thread called "main" or "UI" or whatever starts your application and draws all UI. So the first screen is displayed to users. What now? The main thread terminates? No, it shouldn’t. It should wait until users do something, right? But how can we achieve this behavior? Well, we can try with Object.wait() or Thread.sleep(). For example, main thread finishes its initial job to display first screen, and sleeps. It awakes, which means interrupted, when a new job to do is fetched. So far so good, but at this moment we need a queue-like data structure to hold multiple jobs. Think about a case when a user touches screen serially, and a task takes longer time to finish. So, we need to have a data structure to hold jobs to be done in first-in-first-out manner. Also, you may imagine, implementing ever-running-and-process-job-when-arrived thread using interrupt is not easy, and leads to complex and often unmaintainable code. We'd rather create a new mechanism for such purpose, and that is what Looper is all about. The official document of Looper class says, "Threads by default do not have a message loop associated with them", and Looper is a class "used to run a message loop for a thread". Now you can understand what it means.

To make things more clear, let's check the code where the main thread is transformed. It all happens in ActivityThread class. In its main() method, you can find below code, which turns a normal thread into something that we need.

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

and Looper.loop() method loop infinitely and dequeue a message and process one at a time:

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);
        ...
    }
}

So, basically, Looper is a class that is made to address a problem that occurs in the GUI framework. But this kind of need can also happen in other situations as well. Actually, it is a pretty famous pattern for multi-thread applications, and you can learn more about it in "Concurrent Programming in Java" by Doug Lea(Especially, chapter 4.1.4 "Worker Threads" would be helpful). Also, you can imagine this kind of mechanism is not unique in Android framework, but all GUI framework may need somewhat similar. You can find almost same mechanism in Java Swing framework too.

清旖 2024-12-14 14:49:23

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 线程上的消息。

Looper allows tasks to be executed sequentially on a single thread. And handler defines those tasks that we need to be executed. It is a typical scenario that I am trying to illustrate in this example:

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);
  } 
}
}

Now we can use the handler in some other threads(say ui thread) to post the task on Looper to execute.

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

On UI thread we have an implicit Looper that allow us to handle the messages on ui thread.

一身骄傲 2024-12-14 14:49:23

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

这是一篇文章,解释了< code>Looper生命周期、如何使用以及HandlerLooper的使用方法

在此处输入图像描述

Looper = Thread + MessageQueue

Android Looper is a wrapper to attach MessageQueue to Thread and it manages Queue processing. It looks very cryptic in Android documentation and many times we may face Looper related UI access issues. If we don't understand the basics it becomes very tough to handle.

Here is an article which explains Looper life cycle, how to use it and usage of Looper in Handler

enter image description here

Looper = Thread + MessageQueue

初熏 2024-12-14 14:49:23

Looper 和 Looper 的最简单定义Handler:

Looper是一个将线程转变为管道线程的类,Handler为您提供了一种将任务推送到此线程的机制来自任何其他线程的管道。

一般措辞细节:

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

Looper 之所以如此命名,是因为它实现了循环 - 接受下一个任务,执行它,然后接受下一个任务,依此类推。 Handler 之所以称为处理程序,是因为它用于每次处理或接受来自任何其他线程的下一个任务,并传递给 Looper(Thread 或 PipeLine Thread)。

示例:

Looper 和 Handler 或 PipeLine Thread 的非常完美的示例是在单个线程中下载多个图像或将它们一张一张地上传到服务器(Http),而不是为每个图像启动一个新线程在后台进行网络调用。

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

Android Guts:循环器和处理程序简介

Simplest Definition of Looper & Handler:

Looper is a class that turns a thread into a Pipeline Thread and Handler gives you a mechanism to push tasks into this pipe from any other threads.

Details in general wording:

So a PipeLine Thread is a thread which can accept more tasks from other threads through a Handler.

The Looper is named so because it implements the loop – takes the next task, executes it, then takes the next one and so on. The Handler is called a handler because it is used to handle or accept that next task each time from any other thread and pass to Looper (Thread or PipeLine Thread).

Example:

A Looper and Handler or PipeLine Thread's very perfect example is to download more than one images or upload them to a server (Http) one by one in a single thread instead of starting a new Thread for each network call in the background.

Read more here about Looper and Handler and the definition of Pipeline Thread:

Android Guts: Intro to Loopers and Handlers

魔法少女 2024-12-14 14:49:23

了解 Looper 线程

java 线程是一个执行单元,旨在在其 run() 方法和方法中执行任务。之后终止:

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

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 线程 并与之通信的示例使用 LocalBroadcastActivity

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

    }
}

用法

 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
    }
}

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

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

  • IntentServices 还设计用于执行较长时间的后台任务,您可以使用 LocalBroadcastActivity 进行通信。但任务执行后服务就会被销毁。如果你想让它保持运行很长一段时间,那么你需要像 while(true){...} 这样的事情。

Looper 线程的其他有意义的用例:

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

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

Understanding Looper Threads

A java Thread a unit of execution which was designed to perform a task in its run() method & terminate after that:
enter image description here

But in Android there are many use cases where we need to keep a Thread alive and wait for user inputs/events for eg. UI thread aka Main Thread.

Main thread in Android is a Java thread which is first started by JVM at the launch of an app and keeps on running till the user choose to close it or encounters unhandled exception.

When an application is launched, the system creates a thread of
execution for the application, called "main." This thread is very
important because it is in charge of dispatching events to the
appropriate user interface widgets, including drawing events.

enter image description here

Now point to note here is although main thread is Java thread yet it keeps on listening to user events and draw 60 fps frames on screen and still it wont die after each cycle. how is it so?

The answer is Looper Class: Looper is a class which is used to keep a thread alive and manage a message queue to execute tasks on
that thread.

Threads by default do not have a message loop associated with them but you can assign one by calling Looper.prepare() in the run method and then call the Looper.loop().

Purpose of Looper is to keep a Thread alive and wait for next cycle of
input Message object to perform computation which otherwise will get
destroyed after first cycle of execution.

If you want to dig deeper how Looper manage Message object queue then you can have a look at source code of Looperclass:

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

Below is an example of how you can create a Looper Thread and communicate with Activity class using 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

    }
}

Usage:

 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
    }
}

Can we use Async task or Intent Services instead?

  • Async tasks are designed to perform a short operation in background and give progres & results on UI thread. Async tasks have limits like you cant create more than 128 Async tasks and ThreadPoolExecutor will allow only upto 5 Async tasks.

  • IntentServices are also designed to do background task for a little longer duration and you can use LocalBroadcast to communicate with Activity. But services get destroyed after task execution. If you want to keep it running for a long time than you need to do hecks like while(true){...}.

Other meaningful use cases for Looper Thread:

  • Used for 2 way socket communication where server keep on listening to Client socket and write back acknowledgment

  • Bitmap processing in background. Pass the image url to Looper thread and it will apply filter effects and store it in tempe rory location and then broadcast temp path of image.

巷雨优美回忆 2024-12-14 14:49:23

java Thread 的生命周期完成后就结束了run() 方法。同一线程无法再次启动。

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

void prepare ()

将当前线程初始化为循环程序。这使您有机会创建处理程序,然后在实际启动循环之前引用此循环程序。调用该方法后一定要调用loop(),并调用quit()结束循环。

void loop ()

在此线程中运行消息队列。请务必调用 quit() 来结束循环。

void quit()

退出循环程序。

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

Janishar 的这篇mindorks 文章解释了 Nice 中的核心概念方式。

输入图像描述这里

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

您需要将 LooperHandler

LooperHandlerHandlerThread是Android解决异步编程问题的方式。

一旦您有了Handler,您就可以调用以下API。

post (Runnable r)

导致 Runnable r 添加到消息队列中。该可运行对象将在该处理程序所附加的线程上运行。

boolean sendMessage (Message msg)

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

HandlerThread 是一个方便的类,用于启动具有循环器的新线程。然后,循环器可用于创建处理程序类。

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

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

HandlerThread 代码将如下所示:

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

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

Android:线程中的 Toast

Life span of java Thread is over after completion of run() method. Same thread can't be started again.

Looper transforms normal Thread into a message loop. Key methods of Looper are :

void prepare ()

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 loop() after calling this method, and end it by calling quit().

void loop ()

Run the message queue in this thread. Be sure to call quit() to end the loop.

void quit()

Quits the looper.

Causes the loop() method to terminate without processing any more messages in the message queue.

This mindorks article by Janishar explains the core concepts in nice way.

enter image description here

Looper is associated with a Thread. If you need Looper on UI thread, Looper.getMainLooper() will return associated thread.

You need Looper to be associated with a Handler.

Looper, Handler, and HandlerThread are the Android’s way of solving the problems of asynchronous programming.

Once you have Handler, you can call below APIs.

post (Runnable r)

Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

boolean sendMessage (Message msg)

Pushes a message onto the end of the message queue after all pending messages before the current time. It will be received in handleMessage(Message), in the thread attached to this handler.

HandlerThread is handy class for starting a new thread that has a looper. The looper can then be used to create handler classes

In some scenarios, you can't run Runnable tasks on UI Thread.
e.g. Network operations : Send message on a socket, open an URL and get content by reading InputStream

In these cases, HandlerThread is useful. You can get Looper object from HandlerThread and create a Handler on HandlerThread instead of main thread.

The HandlerThread code will be like this:

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

Refer to below post for example code:

Android: Toast in a thread

知足的幸福 2024-12-14 14:49:23

Looper 有一个同步 MessageQueue 用于处理放置在队列中的消息。

它实现了线程特定存储模式。

每个线程只有一个Looper。主要方法包括 prepare()loop()quit()

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

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

Looper 可以通过几个步骤在 Thread 中进行编程

  1. Extend Thread >

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

  3. 创建一个或多个Handler来处理传入消息

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

A Looper has a synchronized MessageQueue that's used to process Messages placed on the queue.

It implements a Thread Specific Storage Pattern.

Only one Looper per Thread. Key methods include prepare(),loop() and quit().

prepare() initializes the current Thread as a Looper. prepare() is static method that uses the ThreadLocal class as shown below.

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
  1. prepare() must be called explicitly before running the event loop.
  2. loop() runs the event loop which waits for Messages to arrive on a specific Thread's messagequeue. Once the next Message is received,the loop() method dispatches the Message to its target handler
  3. quit() shuts down the event loop. It doesn't terminate the loop,but instead it enqueues a special message

Looper can be programmed in a Thread via several steps

  1. Extend Thread

  2. Call Looper.prepare() to initialize Thread as a Looper

  3. Create one or more Handler(s) to process the incoming messages

  4. Call Looper.loop() to process messages until the loop is told to quit().
孤蝉 2024-12-14 14:49:23

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

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);

这个答案与问题 全面实施

This answer has nothing to do with the question, but the use of looper and the way people created the handler and looper in ALL the answers here are plain bad practice (some explanations are correct though), I have to post this:

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);

and for a full implementation

↘紸啶 2024-12-14 14:49:23

什么是循环器?

来自文档

Looper

Looper 用于为线程运行消息循环的类。默认情况下,线程没有与其关联的消息循环;要创建一个循环,请在要运行循环的线程中调用 prepare(),然后调用 loop() 以使其处理消息,直到循环停止。

  • Looper 是一个消息处理循环:
  • Looper 的一个重要特征是它与创建 Looper 的线程相关联。
  • Looper 类维护一个 MessageQueue,其中包含消息列表。 Looper 的一个重要特征是它与创建 Looper 的线程相关联。
  • Looper 之所以如此命名,是因为它实现了循环——接受下一个任务,执行它,然后接受下一个任务,依此类推。 Handler 之所以被称为处理程序,是因为有人找不到更好的名字
  • Android Looper 是 Android 用户界面中的一个 Java 类,它与 Handler 类一起处理 UI 事件,例如单击按钮、屏幕重绘和方向切换。

它是如何工作的?

输入图片此处描述

创建循环

线程获取一个LooperMessageQueue< /code> 通过在运行后调用 Looper.prepare() 来实现。 Looper.prepare() 标识调用线程,创建一个 Looper 和 MessageQueue 对象并关联线程

示例代码

class MyLooperThread extends Thread {

      public Handler mHandler; 

      public void run() { 

          // preparing a looper on current thread  
          Looper.prepare();

          mHandler = new Handler() { 
              public void handleMessage(Message msg) { 
                 // process incoming messages here
                 // this will run in non-ui/background thread
              } 
          }; 

          Looper.loop();
      } 
  }

有关更多信息,请查看下面的帖子

What is Looper?

FROM DOCS

Looper

Looper Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

  • A Looper is a message handling loop:
  • An important character of Looper is that it's associated with the thread within which the Looper is created
  • The Looper class maintains a MessageQueue, which contains a list messages. An important character of Looper is that it's associated with the thread within which the Looper is created.
  • The Looper is named so because it implements the loop – takes the next task, executes it, then takes the next one and so on. The Handler is called a handler because someone could not invent a better name
  • Android Looper is a Java class within the Android user interface that together with the Handler class to process UI events such as button clicks, screen redraws and orientation switches.

How it works?

enter image description here

Creating Looper

A thread gets a Looper and MessageQueue by calling Looper.prepare() after its running. Looper.prepare() identifies the calling thread, creates a Looper and MessageQueue object and associate the thread

SAMPLE CODE

class MyLooperThread extends Thread {

      public Handler mHandler; 

      public void run() { 

          // preparing a looper on current thread  
          Looper.prepare();

          mHandler = new Handler() { 
              public void handleMessage(Message msg) { 
                 // process incoming messages here
                 // this will run in non-ui/background thread
              } 
          }; 

          Looper.loop();
      } 
  }

For more information check below post

许一世地老天荒 2024-12-14 14:49:23

Service 中处理多个下载或上传项目是一个更好的示例。

HandlerAsnycTask 通常用于在 UI(线程)和工作线程之间传播事件/消息或来延迟行动。所以它们与UI更相关。

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

Handling multiple down or upload items in a Service is a better example.

Handler and AsnycTask are often used to propagate Events/Messages between the UI (thread) and a worker thread or to delay actions. So they are more related to UI.

A Looper handles tasks (Runnables, Futures) in a thread related queue in the background - even with no user interaction or a displayed UI (app downloads a file in the background during a call).

烟雨凡馨 2024-12-14 14:49:23

我将尝试尽可能简单地解释 Looper 类的用途。
对于普通的 Java 线程,当 run 方法完成执行时,我们说该线程已完成其工作,此后线程不再存在。
如果我们想使用不再存在的同一个线程在整个程序中执行更多任务怎么办?
哦,现在有问题吧?是的,因为我们想要执行更多任务,但线程不再活动。这就是 Looper 来拯救我们的地方。
Looper 顾名思义就是循环。 Looper只不过是线程内的无限循环。因此,它使线程无限期地保持活动状态,直到我们显式调用 quit() 方法。在无限活动的线程上调用 quit() 方法将使线程内的无限循环中的条件为假,从而退出无限循环。因此,线程将死亡或不再存活。在循环程序所附加的线程上调用 quit() 方法至关重要,否则它们将像僵尸一样出现在您的系统中。
因此,例如,如果我们想创建一个后台线程来执行一些多项任务。我们将创建一个简单的Java线程,并使用Looper类来准备一个循环器,并将准备好的循环器附加到该线程上,这样我们的线程就可以按照我们想要的时间生存,因为我们可以随时调用quit()来终止我们的线程。因此,我们的循环器将使我们的线程保持活动状态,因此我们将能够使用同一线程执行多个任务,当我们完成时,我们将调用 quit() 来终止线程。
如果我们希望主线程或 UI 线程在某些 UI 元素上显示后台线程或非 UI 线程计算的结果怎么办?
为此,出现了处理程序的概念;
通过处理程序,我们可以进行进程间通信,或者说通过处理程序,两个线程可以相互通信。
因此,主线程将有一个关联的处理程序,后台线程将通过该处理程序与主线程通信,以完成在主线程上的某些 UI 元素上显示其计算结果的任务。
我知道我在这里只是解释理论,但尝试理解这个概念,因为深入理解这个概念非常重要。我在下面发布了一个链接,它将带您观看有关 Looper、Handler 和 HandlerThread 的小视频系列,我强烈建议观看它,所有这些概念都会通过示例得到澄清。

https://www.youtube.com/watch?v=rfLMwbOKLRk&list=PL6nth5sRD25hVezlyqlBO9dafKMc5fAU2&index=1

I will try to explain the purpose of looper class as simple as possible.
With a normal thread of Java when the run method completes the execution we say the thread has done it's job and thread lives no longer after that.
what if we want to execute more tasks throughout our program with that same thread which is not living anymore?
Oh there is a problem now right? Yes because we want to execute more tasks but the thread in not alive anymore. It is where the Looper comes in to rescue us.
Looper as the name suggests loops. Looper is nothing more than an infinite loop inside your thread. So, it keeps the thread alive for an infinite time until we explicitly calls quit() method. Calling quit() method on the infinitely alive thread will make the condition false in the infinite loop inside the thread thus, infinite loop will exit. so, the thread will die or will no longer be alive. And it's critical to call the quit() method on our Thread to which looper is attached otherwise they will be there in your system just like Zombies.
So, for example if we want to create a background thread to do some multiple tasks over it. we will create a simple Java's thread and will use Looper class to prepare a looper and attach the prepared looper with that thread so that our thread can live as longer as we want them because we can always call quit() anytime whenever we want to terminate our thread. So our the looper will keep our thread alive thus we will be able to execute multiple tasks with the same thread and when we are done we will call quit() to terminate the thread.
What if we want our Main thread or UI thread to display the results computed by the background thread or non-UI thread on some UI elements?
for that purpose there comes in the concept of Handlers;
via handlers we can do inter-process communication or say via handlers two threads can communicate with each other.
So, the main thread will have an associated Handler and Background thread will communicate with Main Thread via that handler to get the task done of displaying the results computed by it on some UI elements on Main thread.
I know I am explaining only theory here but try to understand the concept because understanding the concept in depth is very important. And I am posting a link below which will take you to a small video series about Looper, Handler and HandlerThread and I will highly recommend watching it and all these concepts will get cleared with examples there.

https://www.youtube.com/watch?v=rfLMwbOKLRk&list=PL6nth5sRD25hVezlyqlBO9dafKMc5fAU2&index=1

浊酒尽余欢 2024-12-14 14:49:23

我尝试用 Kotlin 举一个例子。下面是代码示例。

首先,我们需要从 Handler 实例化变量处理程序(提供的循环程序而不是默认的循环程序),它要求主线程(Looper.getMainLooper())。

函数 getAllCourses() 需要返回 LiveData,因此我们使用 handler.postDelayed() 将其添加到消息队列中,并在常量 SERVICE_LATENCY_IN_MILLIS 中指定的 x 毫秒后运行。

请随意为我的解释详细阐述更多措辞,以使其更加清晰。

class RemoteDataSource private constructor(private val jsonHelper: JsonHelper) {

    private val handler = Handler(Looper.getMainLooper())

    companion object {
        private const val SERVICE_LATENCY_IN_MILLIS: Long = 2000

        @Volatile
        private var instance: RemoteDataSource? = null

        fun getInstance(helper: JsonHelper): RemoteDataSource =
                instance ?: synchronized(this) {
                    RemoteDataSource(helper).apply { instance = this }
                }
    }

    fun getAllCourses(): LiveData<ApiResponse<List<CourseResponse>>> {
        EspressoIdlingResource.increment()
        val resultCourse = MutableLiveData<ApiResponse<List<CourseResponse>>>()
        handler.postDelayed({
            resultCourse.value = ApiResponse.success(jsonHelper.loadCourses())
            EspressoIdlingResource.decrement()
        }, SERVICE_LATENCY_IN_MILLIS)
        return resultCourse
    }

I try to give an example in Kotlin. Here is below the code example.

Firstly we need to instantiate variable handler from the Handler(the provided looper instead of the default one) which ask for mainthread (Looper.getMainLooper()).

The function getAllCourses() need to return LiveData so we use handler.postDelayed() to be added to the message queque and run after x amount of millisecond specified in constant SERVICE_LATENCY_IN_MILLIS.

Please feel free to elaborate more wording to my explanation to give more clarity.

class RemoteDataSource private constructor(private val jsonHelper: JsonHelper) {

    private val handler = Handler(Looper.getMainLooper())

    companion object {
        private const val SERVICE_LATENCY_IN_MILLIS: Long = 2000

        @Volatile
        private var instance: RemoteDataSource? = null

        fun getInstance(helper: JsonHelper): RemoteDataSource =
                instance ?: synchronized(this) {
                    RemoteDataSource(helper).apply { instance = this }
                }
    }

    fun getAllCourses(): LiveData<ApiResponse<List<CourseResponse>>> {
        EspressoIdlingResource.increment()
        val resultCourse = MutableLiveData<ApiResponse<List<CourseResponse>>>()
        handler.postDelayed({
            resultCourse.value = ApiResponse.success(jsonHelper.loadCourses())
            EspressoIdlingResource.decrement()
        }, SERVICE_LATENCY_IN_MILLIS)
        return resultCourse
    }
不即不离 2024-12-14 14:49:23

使用 Looper 的官方方法在文档中,并且不使用不推荐使用的没有参数的 Handler CTOR :

https://developer.android.com/reference/android/os/Looper

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler(Looper.myLooper()) {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

这是我在 Kotlin 中制作的改进版本:

open class LooperThread : Thread() {
    @Volatile
    var handler: Handler? = null
        private set

    @WorkerThread
    override fun run() {
        Looper.prepare()
        handler = object : Handler(Looper.myLooper()!!) {
            fun handleMessage(msg: Message?) {
                [email protected](msg!!)
            }

        }
        Looper.loop()
    }

    @WorkerThread
    open fun handleMessage(msg: Message) {
    }

    /**stops handling messages for the looper, so it can be released
     * @param quitSafely whether to use Looper.quitSafely (wait for normal messages) or Looper.quit (ditch all pending messages):
     * https://developer.android.com/reference/android/os/Looper#quitSafely()
     * https://developer.android.com/reference/android/os/Looper#quit()*/
    fun quit(quitSafely: Boolean) {
        handler?.let { handler ->
            if (quitSafely)
                handler.looper.quitSafely()
            else handler.looper.quit()
            [email protected] = null
        }
    }
}

The official way to use the Looper is in the documentation and doesn't use the deprecated Handler CTOR that has no parameters :

https://developer.android.com/reference/android/os/Looper

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler(Looper.myLooper()) {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

Here's an improved version of it in Kotlin that I've made:

open class LooperThread : Thread() {
    @Volatile
    var handler: Handler? = null
        private set

    @WorkerThread
    override fun run() {
        Looper.prepare()
        handler = object : Handler(Looper.myLooper()!!) {
            fun handleMessage(msg: Message?) {
                [email protected](msg!!)
            }

        }
        Looper.loop()
    }

    @WorkerThread
    open fun handleMessage(msg: Message) {
    }

    /**stops handling messages for the looper, so it can be released
     * @param quitSafely whether to use Looper.quitSafely (wait for normal messages) or Looper.quit (ditch all pending messages):
     * https://developer.android.com/reference/android/os/Looper#quitSafely()
     * https://developer.android.com/reference/android/os/Looper#quit()*/
    fun quit(quitSafely: Boolean) {
        handler?.let { handler ->
            if (quitSafely)
                handler.looper.quitSafely()
            else handler.looper.quit()
            [email protected] = null
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文