我是Android新手。我想知道Looper
该类的作用以及如何使用它。我已经阅读了Android Looper类文档,但无法完全理解。我在很多地方都看到过它,但无法理解它的目的。任何人都可以通过定义目标Looper
以及通过给出一个简单示例来帮助我吗?
我是Android新手。我想知道Looper
该类的作用以及如何使用它。我已经阅读了Android Looper类文档,但无法完全理解。我在很多地方都看到过它,但无法理解它的目的。任何人都可以通过定义目标Looper
以及通过给出一个简单示例来帮助我吗?
Answers:
什么是Looper?
Looper是用于在队列中执行Messages(Runnables)的类。普通线程没有这样的队列,例如简单线程没有任何队列。它执行一次,方法执行完成后,该线程将不会运行另一个Message(Runnable)。
在哪里可以使用Looper类?
如果有人想执行多个消息(Runnables),那么他应该使用Looper类,该类负责在线程中创建队列。例如,在编写从Internet下载文件的应用程序时,我们可以使用Looper类将要下载的文件放在队列中。
怎么运行的?
有prepare()
准备弯针的方法。然后,您可以使用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可以做2件事。
1)Looper 将普通线程(在其run()方法返回时终止)转换为连续运行的状态,直到Android应用程序运行为止,这在GUI框架中是必需的(从技术上讲,它仍然在run()方法返回时终止。但是让我在下面阐明我的意思)。
2)Looper 提供了一个队列,将要完成的工作排入队列,这在GUI框架中也需要。
如您所知,启动应用程序时,系统会为该应用程序创建一个执行线程,称为“主线程”,而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类中。在其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允许任务在单个线程上顺序执行。处理程序定义了我们需要执行的那些任务。在此示例中,我试图说明一个典型的场景:
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和Handler的定义:
Looper是一个将线程转换为管道线程和处理程序的类为您提供了一种机制,可以将任务从任何其他线程推入该线程。
细节:
因此,PipeLine线程是可以通过处理程序从其他线程接受更多任务的线程。
该活套被命名是因为它实现了循环-需要下一个任务,执行它,然后采取下一个等。该处理程序之所以称为处理程序,是因为它用于每次从任何其他线程处理或接受该下一个任务并传递给Looper(线程或管道线程)。
例:
Looper和Handler或PipeLine线程的一个非常完美的示例是在一个线程中一个一个地下载多个图像或将它们一张一张地上传到服务器(Http),而不是在后台为每个网络调用启动一个新线程。
在此处阅读有关Looper和Handler以及Pipeline Thread的定义的更多信息:
一个活套有synchronized
MessageQueue
这些会用来对放置在该队列过程的消息。
它实现了 Thread
特定的存储模式。
Looper
每只一个Thread
。主要的方法包括prepare()
,loop()
和quit()
。
prepare()
将电流初始化Thread
为Looper
。prepare()
是static
使用ThreadLocal
如下所示的类的方法。
public static void prepare(){
...
sThreadLocal.set
(new Looper());
}
prepare()
必须在运行事件循环之前显式调用它。 loop()
运行事件循环,以等待消息到达特定线程的消息队列。收到下一条消息后,该loop()
方法将消息调度到其目标处理程序quit()
关闭事件循环。它不会终止循环,而是会加入一条特殊消息Looper
可以Thread
通过几个步骤进行编程
延伸 Thread
调用Looper.prepare()
以将Thread初始化为Looper
创建一个或多个Handler
来处理传入的消息
Looper.loop()
以处理消息,直到告知循环为止quit()
。方法完成后,Java Thread的生命周期结束run()
。相同的线程无法再次启动。
Looper将法线Thread
转换为消息循环。的关键方法Looper
是:
void prepare ()
将当前线程初始化为循环程序。这使您有机会创建在实际开始循环之前引用该循环程序的处理程序。确保在调用此方法后调用loop(),并通过调用quit()结束它。
void loop ()
在此线程中运行消息队列。确保调用quit()结束循环。
void quit()
退出弯针。
使loop()方法终止,而不处理消息队列中的更多消息。
这mindorks文章通过Janishar解释了不错的方式的核心概念。
Looper
与线程关联。如果需要Looper
在UI线程上,Looper.getMainLooper()
将返回关联的线程。
您需要Looper
与Handler关联。
Looper
,Handler
和HandlerThread
是Android解决异步编程问题的方法。
拥有之后Handler
,您可以调用以下API。
post (Runnable r)
使Runnable r添加到消息队列。可运行对象将在此处理程序所连接的线程上运行。
boolean sendMessage (Message msg)
在当前时间之前所有未决消息之后将消息推送到消息队列的末尾。它将在该处理程序附带的线程中的handleMessage(Message)中接收。
处理器线程是方便的类,用于启动具有程序的新线程。然后可以使用循环程序创建处理程序类
在某些情况下,您无法Runnable
在UI线程上运行任务。例如,网络操作:在套接字上发送消息,打开URL并通过阅读获取内容InputStream
在这些情况下,HandlerThread
很有用。您可以从中获取Looper
对象HandlerThread
并创建Handler
on HandlerThread
而不是主线程。
该HandlerThread代码将是这样的:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
请参阅以下帖子以获取示例代码:
一个Java线程,一个旨在在其run()方法中执行任务并在此之后终止的执行单元:
但是在Android中,有很多用例需要保持Thread存活并等待用户输入/事件,例如。UI线程又名Main Thread
。
Android中的主线程是一个Java线程,它首先由JVM在应用程序启动时启动,并一直运行直到用户选择关闭它或遇到未处理的异常。
启动应用程序时,系统会为该应用程序创建一个执行线程,称为“主线程”。该线程非常重要,因为它负责将事件调度到适当的用户界面小部件,包括绘图事件。
现在需要注意的是,尽管主线程是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
}
}
用法:
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
还设计了更长的时间做后台任务,您可以用于LocalBroadcast
与进行通信Activity
。但是服务在任务执行后被破坏。如果您想让它长时间运行,则需要做类似的工作 while(true){...}
。
Looper线程的其他有意义的用例:
用于2路套接字通信,其中服务器继续侦听客户端套接字并写回确认
位图在后台处理。将图片网址传递到Looper线程,它将应用滤镜效果并将其存储在临时模板位置,然后广播图片的临时路径。
什么是Looper?
从DOCS
Looper
用于运行的消息循环的类thread
。默认情况下,线程没有与之关联的消息循环。若要创建一个,请prepare()
在要运行循环的线程中进行调用,然后loop()
让其处理消息,直到循环停止为止。
Looper
是消息处理循环:MessageQueue
,其中包含列表消息。Looper的一个重要特征是,它与创建Looper的线程相关联。Looper
被命名为如此,因为它实现了循环-需要下一个任务,执行它,然后采取下一个等。的Handler
被称为处理程序,因为有人不能创造一个更好的名字Looper
是Android用户界面中的Java类,它与Handler类一起用于处理UI事件,例如按钮单击,屏幕重绘和方向切换。怎么运行的?
创建Looper
线程在运行后通过调用获得Looper
和。识别调用线程,创建一个Looper和对象并关联该线程MessageQueue
Looper.prepare()
Looper.prepare()
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();
}
}
有关更多信息,请在下面的帖子中查看