我是Android的新手,想了解Looper
类是做什么的,以及如何使用它。我已经阅读了Android Looper类文档,但我仍然无法完全理解它。
我在很多地方都看到了它,但不理解它的用途。是否有人能够通过定义Looper
的目的,并在可能的情况下给出一个简单的示例来帮助我?
我是Android的新手,想了解Looper
类是做什么的,以及如何使用它。我已经阅读了Android Looper类文档,但我仍然无法完全理解它。
我在很多地方都看到了它,但不理解它的用途。是否有人能够通过定义Looper
的目的,并在可能的情况下给出一个简单的示例来帮助我?
Looper是什么?
Looper是一个类,用于在队列中执行消息(Runnables)。普通线程没有这样的队列,例如普通线程没有任何队列。它只会执行一次,并且在方法执行完成后,该线程将不会再运行其他消息(Runnable)。
Loops可以在哪里使用?
如果有人想要执行多个消息(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();
}
}
在GUI框架的上下文中,你可以更好地理解Looper是什么。Looper的作用有两个:
将普通线程转换为一直运行直到Android应用程序结束。
提供一个队列,将需要执行的任务加入其中。
当一个应用程序启动时,系统会为该应用程序创建一个执行线程,称为“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();
}
}
现在,让我们将这个简单的原则应用到 Android 应用上。如果一个 Android 应用程序在普通线程上运行会发生什么?一个名为“main”或“UI”的线程启动你的应用程序并绘制所有 UI。因此,第一个屏幕显示给用户。现在呢?主线程终止了吗?不应该。它应该等待用户做些什么,对吧?但是我们如何实现这种行为呢?嗯,我们可以尝试使用 Object.wait()
或 Thread.sleep()
。例如,主线程完成了显示第一个屏幕的初始工作,并休眠。当获取新的任务时,它会被唤醒,也就是被中断了。到目前为止都还好,但现在我们需要一个类似队列的数据结构来按先进先出的方式保存多个作业。想想当用户连续触摸屏幕时,任务需要更长时间才能完成的情况。因此,我们需要一个数据结构来保存要以先进先出的方式完成的作业。此外,你可能会想象,使用中断来实现永久运行和处理作业的线程并不容易,并且会导致复杂且难以维护的代码。我们最好为这种目的创建一个新机制,这就是 Looper 的全部内容。Loops 类的官方文档说,“线程默认情况下不具有与其关联的消息循环”,而 Looper 是一个“用于运行线程的消息循环”的类。现在你可以理解这意味着什么了。为了让事情更清楚,让我们来看一下将主线程转换的代码。这些都发生在 ActivityThread 类中。在它的 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框架中找到完全相同的机制。
Looper允许在单个线程上按顺序执行任务。而handler定义了需要执行的任务。这是我试图在这个例子中说明的典型情况:
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);
}
}
}
handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
}
});
在UI线程中,我们有一个隐式的Looper,可以让我们处理UI线程上的消息。
Android中的Looper
是一个包装器,用于将MessageQueue
附加到Thread
并管理队列处理。在Android文档中,它看起来非常神秘,而且我们经常会遇到与Looper
相关的UI访问问题。如果我们不了解基础知识,处理起来就会变得非常困难。
这里有一篇article,解释了Looper
的生命周期,如何使用它以及在Handler
中使用Looper
的用法。
Looper = 线程 + 消息队列
Looper 和 Handler 的最简定义:
Looper 是把一个线程变成 管道线程 的类,Handler 提供了一种机制,可以从任何其他线程向这个管道推送任务。
通俗易懂的详细解释:
因此管道线程是一条线程,它可以通过 Handler 从其他线程接受更多的任务。
Looper 之所以被称为 Looper,是因为它实现了一个循环 - 获取下一个任务,执行它,然后获取下一个任务,如此循环进行。Handler 被称为处理程序,因为它用于每次从任何其他线程接受和传递下一个任务给 Looper(线程或管道线程)。
示例:
Looper 和 Handler 或管道线程的非常完美的示例是在单个线程中下载多个图像或将它们上传到服务器(Http)中,而不是为后台的每个网络调用启动一个新的线程。
了解有关 Looper 和 Handler 以及管道线程定义的更多信息,请点击以下链接:
Java Thread是执行任务并在其run()方法完成后终止的执行单元:
但在Android中,有许多用例需要保持线程活动并等待用户输入/事件,例如UI线程,也称为主线程
。
在Android中,主线程是由JVM在应用程序启动时首先启动的Java线程,一直运行直到用户选择关闭它或遇到未处理的异常。
现在需要注意的是,尽管主线程是Java线程,但它仍然会持续监听用户事件并在屏幕上绘制60fps的帧,并且在每个周期结束后不会死亡。为什么会这样呢?当应用程序启动时,系统为应用程序创建一个执行线程,称为“main”。这个线程非常重要,因为它负责将事件分派到适当的用户界面小部件,包括绘制事件。
以下是如何创建一个“Looper线程”,并使用“LocalBroadcast”与“Activity”类通信的示例:https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/os/Looper.java
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任务或Intent服务吗?
Async任务旨在在后台执行短时间操作并在UI线程上提供进度和结果。 Async任务有限制,例如您不能创建超过128个Async任务且ThreadPoolExecutor
只允许最多5个Async任务。
IntentServices
也旨在执行稍长时间的后台任务,您可以使用LocalBroadcast
与Activity
通信。但是服务在任务执行后被销毁。如果要让它长时间运行,则需要进行诸如while(true){...}
之类的检查。
Looper Thread的其他有意义的用例:
用于双向套接字通信,其中服务器继续侦听客户端套接字并写回确认
在后台处理位图。将图像网址传递给Looper线程,它将应用滤镜效果并将其存储在临时位置,然后广播图像的临时路径。
在完成run()
方法后,Java 线程的生命周期就结束了。同一个线程不能再次启动。
Looper可以将普通的Thread
转换为消息循环。 Looper
的关键方法包括:
void prepare ()
将当前线程初始化为一个循环器。这样您就有机会在实际启动循环之前创建引用此循环器的处理程序。在调用此方法后一定要调用loop(),并通过调用quit()来结束它。
void loop ()
void quit()
退出循环。
导致loop()方法终止,不再处理消息队列中的任何消息。
Janishar撰写的mindorks文章以简明易懂的方式解释了核心概念。
Looper
与线程相关联。如果您需要在UI线程上使用Looper
,则Looper.getMainLooper()
将返回相关联的线程。
您需要将Looper
与Handler关联。
Looper
、Handler
和HandlerThread
是Android解决异步编程问题的方式。
一旦您拥有Handler
,就可以调用以下API。
post (Runnable r)
boolean sendMessage (Message msg)
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;
}
Looper有一个同步的MessageQueue
,用于处理放置在队列中的消息。
它实现了线程特定存储模式。
每个Thread
只有一个Looper
。关键方法包括prepare()
、loop()
和quit()
。
prepare()
将当前Thread
初始化为一个Looper
。prepare()
是一个静态方法,使用ThreadLocal
类如下所示。
public static void prepare(){
...
sThreadLocal.set
(new Looper());
}
prepare()
。loop()
运行一个事件循环,等待消息到达特定线程的消息队列。一旦接收到下一条消息,loop()
方法将消息分派给其目标处理程序。quit()
关闭事件循环。它不终止循环,而是将一个特殊消息加入队列。Looper
可以通过几个步骤在 Thread
中进行编程
扩展 Thread
调用 Looper.prepare()
将线程初始化为 Looper
创建一个或多个 Handler
来处理传入的消息
Looper.loop()
处理消息,直到循环被告知退出 quit()
。这个答案与问题无关,但是在所有的答案中使用looper和创建handler和looper的方式都是很糟糕的做法(尽管有些解释是正确的),我必须发表以下观点:
HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);
and for a full implementation
什么是Looper?
来自文档
Looper
类用于为一个线程
运行消息循环。默认情况下,线程不带有与它们关联的消息循环;要创建一个消息循环,请在准备运行循环的线程中调用prepare()
,然后调用loop()
以使其处理消息,直到循环停止。
Looper
是一个消息处理循环:MessageQueue
。Looper的一个重要特点是它与创建Looper的线程相关联。Looper
之所以被命名为Looper,是因为它实现了循环 - 获取下一个任务,执行它,然后获取下一个任务,以此类推。Handler被称为处理程序,是因为没有人能想出更好的名称。Looper
是Android用户界面中的一个Java类,与Handler类一起处理UI事件,例如按钮单击、屏幕重绘和方向切换。它是如何工作的?
创建Looper
线程在运行后通过调用Looper.prepare()
来获取一个Looper
和MessageQueue
。 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();
}
}
更多信息请查看以下帖子
class TextHandlerTask
中的while(Thread.currentThread().isInterrupted()){
这一行应该被反转为while(!Thread.currentThread().isInterrupted()){}
才能使其循环,这意味着它将重新尝试读取直到线程被中断,我的理解正确吗? - Delark