我是安卓系统的新手。我想知道Looper类做什么,以及如何使用它。我已经阅读了Android Looper类文档,但我无法完全理解它。 我在很多地方见过它,但无法理解它的目的。谁能帮我定义一下Looper的目的,如果可能的话,也给我一个简单的例子?
当前回答
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);
}
}
}
现在我们可以在一些其他线程(比如ui线程)中使用处理程序将任务发布到Looper上执行。
handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
}
});
在UI线程上,我们有一个隐式的循环器,允许我们处理UI线程上的消息。
其他回答
什么是环形使者?
Looper是一个用于执行队列中的消息(可运行对象)的类。普通线程没有这样的队列,例如简单线程没有任何队列。它执行一次,方法执行完成后,线程将不再运行另一个Message(Runnable)。
我们可以在哪里使用Looper类?
如果有人想要执行多个消息(Runnables),那么他应该使用负责在线程中创建队列的Looper类。 例如,在编写从internet下载文件的应用程序时,我们可以使用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();
}
}
理解循环线程
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传递给循环线程,它将应用过滤器效果,并将其存储在临时位置,然后广播图像的临时路径。
这个答案与问题无关,但looper的使用以及人们在这里的所有答案中创建处理程序和looper的方式都是明显的坏习惯(尽管有些解释是正确的),我不得不发布这个:
HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);
以及一个完整的实现
在Service中处理多个下拉或上传项是一个更好的例子。
Handler和AsnycTask通常用于在UI(线程)和工作线程之间传播事件/消息或延迟操作。所以它们与UI更相关。
循环程序在后台处理线程相关队列中的任务(Runnables, Futures) -即使没有用户交互或显示UI(应用程序在调用期间在后台下载文件)。
我尝试在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
}
推荐文章
- 改变开关的“开”色
- 以编程方式将EditText的输入类型从PASSWORD更改为NORMAL,反之亦然
- 如何在隐藏和查看密码之间切换
- 在Android上调整一个大的位图文件到缩放输出文件
- 如何更改Android版本和代码版本号?
- Android Studio突然无法解析符号
- 应用程序重新启动而不是恢复
- 跨线程操作无效:控件“textBox1”从创建它的线程以外的线程访问
- 如何设置整个应用程序在纵向模式?
- Android中文本的阴影效果?
- 以编程方式设置TextView的布局权重
- Android -如何覆盖“后退”按钮,所以它不完成()我的活动?
- 如何从通知点击发送参数到一个活动?
- 导航目标xxx对于这个NavController是未知的
- 使用ConstraintLayout均匀间距的视图