我是安卓系统的新手。我想知道Looper类做什么,以及如何使用它。我已经阅读了Android Looper类文档,但我无法完全理解它。 我在很多地方见过它,但无法理解它的目的。谁能帮我定义一下Looper的目的,如果可能的话,也给我一个简单的例子?


当前回答

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

其他回答

理解循环线程

java线程是一个执行单元,它被设计为在run()方法中执行任务,并在此之后终止:

但在Android中,有许多用例,我们需要保持线程活跃,并等待用户输入/事件等。UI线程又名主线程。

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

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

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

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

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

循环器的目的是保持一个线程活着,并等待下一个周期 input消息对象来执行计算,否则将得到 在第一个循环执行后销毁。

如果你想深入挖掘Looperclass如何管理Message对象队列,那么你可以看看Looperclass的源代码:

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

下面是如何创建一个循环线程并使用LocalBroadcast与Activity类通信的示例

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

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

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

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

用于双向套接字通信,服务器继续监听客户端套接字并写回确认 位图处理在后台。将图像url传递给循环线程,它将应用过滤器效果,并将其存储在临时位置,然后广播图像的临时路径。

Android Looper是一个包装器,用于将messagqueuue附加到线程,并管理队列处理。它在Android文档中看起来非常神秘,很多时候我们可能会面临与Looper相关的UI访问问题。如果我们不了解基础知识,就很难处理。

这是一篇文章,解释了Looper的生命周期,如何使用它,以及在Handler中使用Looper

循环器=线程+消息队列

您可以在GUI框架的上下文中更好地理解什么是Looper。《环形使者》有两项任务。

Looper将一个正常的线程转换为持续运行的线程,直到Android应用程序运行为止。 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();
    }
}

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.

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

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

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

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.

我尝试在Kotlin中给出一个例子。下面是代码示例。

首先,我们需要实例化handler(提供的循环程序而不是默认的循环程序)中的变量处理程序,它要求主线程(loop . 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
    }

Looper & Handler的最简单定义:

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

一般措词的详细说明:

因此,流水线线程是一个可以通过处理程序从其他线程接受更多任务的线程。

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

例子:

循环程序和处理程序或管道线程的非常完美的例子是下载多个图像或在单个线程中逐个上传到服务器(Http),而不是在后台为每个网络调用启动一个新线程。

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

Android内脏:环形器和处理程序的介绍