我是安卓系统的新手。我想知道Looper类做什么,以及如何使用它。我已经阅读了Android Looper类文档,但我无法完全理解它。 我在很多地方见过它,但无法理解它的目的。谁能帮我定义一下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.
其他回答
Looper & Handler的最简单定义:
Looper是一个将线程转换为管道线程的类,Handler为您提供了一种机制,可以将任务从任何其他线程推入该管道。
一般措词的详细说明:
因此,流水线线程是一个可以通过处理程序从其他线程接受更多任务的线程。
Looper之所以这样命名,是因为它实现了循环——接受下一个任务,执行它,然后再接受下一个任务,以此类推。Handler之所以被称为Handler,是因为它用于处理或接受每次来自任何其他线程的下一个任务,并传递给Looper(线程或管道线程)。
例子:
循环程序和处理程序或管道线程的非常完美的例子是下载多个图像或在单个线程中逐个上传到服务器(Http),而不是在后台为每个网络调用启动一个新线程。
在这里阅读更多关于Looper和Handler以及Pipeline Thread的定义:
Android内脏:环形器和处理程序的介绍
这个答案与问题无关,但looper的使用以及人们在这里的所有答案中创建处理程序和looper的方式都是明显的坏习惯(尽管有些解释是正确的),我不得不发布这个:
HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);
以及一个完整的实现
循环器有一个同步的messagqueuue,用于处理放置在队列上的消息。
它实现了线程特定的存储模式。
每个线程只有一个Looper。关键方法包括prepare()、loop()和quit()。
prepare()将当前线程初始化为循环程序。prepare()是使用ThreadLocal类的静态方法,如下所示。
public static void prepare(){
...
sThreadLocal.set
(new Looper());
}
在运行事件循环之前,必须显式地调用Prepare()。 loop()运行事件循环,等待消息到达特定线程的消息队列。一旦接收到下一个Message, loop()方法将该Message分派给它的目标处理程序 Quit()关闭事件循环。它不会终止循环,而是将一条特殊消息编入队列
Looper可以通过几个步骤在线程中编程
扩展的线程 调用loop .prepare()将Thread初始化为Looper 创建一个或多个Handler来处理传入消息 调用loop .loop()来处理消息,直到循环被告知quit()。
我尝试在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
}
java线程的生命周期在run()方法完成后结束。同一线程不能再次启动。
Looper将普通线程转换为消息循环。Looper的主要方法有:
void prepare ()
将当前线程初始化为循环程序。这使您有机会创建处理程序,然后在实际开始循环之前引用此循环程序。请确保在调用此方法后调用loop(),并通过调用quit()结束它。
void loop ()
在此线程中运行消息队列。确保调用quit()来结束循环。
void quit()
退出循环器。 导致loop()方法终止,而不处理消息队列中的任何其他消息。
Janishar的这篇mindorks文章很好地解释了核心概念。
循环程序与线程相关联。如果你在UI线程上需要Looper, loop . getmainlooper()将返回相关的线程。
您需要将Looper与Handler相关联。
Looper, Handler和HandlerThread是Android解决异步编程问题的方法。
有了Handler之后,就可以调用下面的api了。
post (Runnable r)
导致Runnable r被添加到消息队列。可运行对象将在附加此处理程序的线程上运行。
boolean sendMessage (Message msg)
在当前时间之前的所有挂起消息之后,将消息压入消息队列的末尾。它将在附加到此处理程序的线程中的handleMessage(Message)中接收。
HandlerThread是一个方便的类,用于启动具有循环程序的新线程。循环程序可以用来创建处理程序类
在某些情况下,你不能在UI线程上运行可运行任务。 例如,网络操作:在套接字上发送消息,打开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:在线程中吐司
推荐文章
- 如何隐藏动作栏之前的活动被创建,然后再显示它?
- 是否有一种方法以编程方式滚动滚动视图到特定的编辑文本?
- 在Android中将字符串转换为Uri
- 如何在NestedScrollView内使用RecyclerView ?
- 移动到另一个EditText时,软键盘下一步点击Android
- Android应用中的GridView VS GridLayout
- Activity和FragmentActivity的区别
- 右对齐文本在android TextView
- 权限拒绝:start前台需要android.permission.FOREGROUND_SERVICE
- 如何更改android操作栏的标题和图标
- Android Split字符串
- 让一个链接在安卓浏览器启动我的应用程序?
- 如何在Android工作室的外部库中添加一个jar ?
- GridLayout(不是GridView)如何均匀地拉伸所有子元素
- 如何让一个片段删除自己,即它的等效完成()?