1.简介
尝试Android开发的每个人都发现并发的重要性。 创建响应式应用程序的唯一方法是让UI线程尽可能自由,让所有艰苦的工作由后台线程异步完成。
由于Android的设计,仅使用java.lang.thread
和java.util.concurrent
包管理线程可能真的很困难。 在Android上使用低级线程程序包意味着您必须担心很多棘手的同步操作,以避免出现竞争状况。 幸运的是,Google的人们辛勤工作,并构建了一些出色的工具来IntentService
我们的工作: AsyncTask
, IntentService
, Loader
, AsyncQueryHandler
和CursorLoader
都很有用,以及HaMeR类Handler
, Message
和Runnable
。 有很多很棒的选择供您选择,每种选择都有其优缺点。
但是, AsyncTask
不应是工具带上的唯一工具。
对于长时间运行的操作,复杂的并发问题或在某些情况下要提高效率,您应该选择另一种解决方案。 如果您需要比AsyncTask
更高的灵活性或效率,则可以使用HaMeR( Handler
, Message
和Runnable
)框架。 在本教程中,我们将探索HaMeR框架,它是Android上最强大的并发模型之一,并且将学习何时以及如何使用它。 在后续教程中,我将向您展示如何编写应用程序代码以尝试HaMeR的一些可能性。
下一节将介绍背景线程对Android系统的重要性。 如果您熟悉此概念,请随时跳过它,直接进入第3节中有关HaMeR框架的讨论。
2.通过后台线程进行响应
启动Android应用程序时,其进程产生的第一个线程是主线程,也称为UI线程,它负责处理所有用户界面逻辑。 这是应用程序中最重要的线程。 它负责处理所有用户交互,还负责将应用程序的移动部分“绑在一起”。 Android非常重视这一点,如果您的UI线程在某项任务上停留超过几秒钟,则该应用程序将崩溃。
[UI线程]非常重要,因为它负责将事件调度到适当的用户界面小部件,包括绘图事件。 这也是您的应用程序与Android UI工具包中的组件(android.widget
和android.view
包中的组件)进行交互的线程。 这样,主线程有时也称为UI线程。 — 进程和线程 ,Android开发人员指南
问题在于,默认情况下,Android应用程序中的几乎所有代码都将在UI线程上执行。 由于线程上的任务是顺序执行的,因此这意味着您的用户界面可能会“冻结”,在处理其他工作时会变得无响应。
在UI上调用的长时间运行的任务可能会对您的应用程序致命,并且将出现ANR(应用程序无响应)对话框。 即使是很小的任务也会损害用户体验,因此正确的方法是使用后台线程从UI线程中删除尽可能多的工作。 如前所述,有很多方法可以解决此问题,我们将探索HaMeR框架,它是Android为解决这种情况而提供的核心解决方案之一。
3. HaMeR框架
HaMeR框架允许后台线程通过处理程序将消息或可运行对象发送到UI线程和任何其他线程的MessageQueue
。 HaMeR引用Handler
, Message
和Runnable
。 还有其他一些与HaMeR一起使用的重要类: Looper
和MessageQueue
。 这些对象共同负责促进Android上的线程管理,进行同步,并为后台线程与UI和其他线程通信提供简单方法。
这是HaMeR框架中的类如何组合在一起的方式。
-
Looper
使用MessageQueue
在线程上运行消息循环。 -
MessageQueue
包含Looper
调度的消息列表。 -
Handler
允许向MessageQueue
发送和处理Message
和Runnable
。 它可用于在线程之间发送和处理消息。 -
Message
包含描述和可以发送到处理程序的数据。 -
Runnable
表示要执行的任务。
使用HaMeR框架,线程可以向自身或UI线程发送消息或发布可运行的对象。 HaMeR还通过Handler
促进后台线程交互。
处理程序类
Handler
是HaMeR的主力军。 它负责发送Message
(数据消息)并将Runnable
(任务消息)对象发布到与Thread
关联的MessageQueue
中。 将任务传递到队列后,处理程序从Looper
接收对象,并在适当的时间使用与其关联的Handler
消息。
Handler
程序可用于在线程之间发送或发布Message
和Runnable
对象,只要这些线程共享相同的进程即可。 否则,有必要创建进程间通信(IPC) ,这种方法超出了本教程的范围。
实例化处理程序
Handler
必须始终与Looper
关联,并且此连接需要在其实例化期间进行。 如果不向Handler
提供Looper
,它将绑定到当前Thread
的Looper
。
// Handler uses current Thread's Looper
Handler handler = new Handler();
// Handler uses the Looper provides
Handler handler = new Handler(Looper);
请记住, Handler
程序始终与Looper
关联,并且此连接是永久的,一旦建立就无法更改。 但是, Looper
的线程可以与多个Handler
关联。 同样重要的是要注意,在将Looper
与Handler
关联之前,必须将其激活。
Looper和MessageQueue
Java线程中Looper
和MessageQueue
之间的协作工作创建了一个按顺序处理的任务循环。 这样的循环将在等待接收更多任务时使线程保持活动状态。 一个线程只能有一个Looper
和一个与之关联的MessageQueue
。 但是,每个线程可以有多个处理程序。 处理程序负责处理队列中的任务,每个任务都知道哪个处理程序负责其处理。
为HaMeR准备线程
UI或主线程是唯一一种默认情况下已经具有Handler
, Looper
和MessageQueue
的线程。 其他线程必须与这些对象一起准备,然后才能与HaMeR框架一起使用。 首先,我们需要创建一个已经包含MessageQueue
的Looper
并将其附加到线程。 您可以使用Thread
的子类来执行此操作,如下所示。
// Preparing a Thread for HaMeR
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
// adding and preparing the Looper
Looper.prepare();
// the Handler instance will be associated with Thread’s Looper
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
// Starting the message queue loop using the Looper
Looper.loop();
}
}
但是,使用名为HandlerThread
的帮助程序类更为简单,该类具有在Java Thread
内置的Looper
和MessageQueue
并准备接收Handler。
// The HandlerThread class includes a working Looper
public class HamerThread extends HandlerThread {
// you just need to add the Handler
private Handler handler;
public HamerThread(String name) {
super(name);
}
}
4.过帐可运行资产
Runnable
是具有许多用途的Java接口。 可以理解为要在Thread
上执行的单个任务。 它必须执行一个方法Runnable.run()
来执行任务。
// Declaring a Runnable
Runnable r = new Runnable() {
@Override
public void run() {
// the task goes here
}
};
有多个选项可以在Handler
上发布Runnable
。
-
Handler.post(Runnable r)
:将Runnable
添加到MessageQueue
。 -
Handler.postAtFrontOfQueue(Runnable r)
:将Runnable
添加到MessageQueue
的前面。 -
Handler. postAtTime(Runnable r, long timeMillis)
Handler. postAtTime(Runnable r, long timeMillis)
:将Runnable
添加到要在特定时间调用的MessageQueue
上。 -
Handler. postDelayed(Runnable r, long delay)
Handler. postDelayed(Runnable r, long delay)
:在经过特定时间后,将Runnable
添加到要调用的队列中。
// posting a Runnable on a Handler
Handler handler = new Handler();
handler.post(
new Runnable() {
@Override
public void run() {
// task goes here
}
});
也可以使用默认的UI处理程序来发布Runnable
调用Activity.runOnUiThread()
。
// posting Runnable using the UI Handler
Activity.runOnUiThread(
new Runnable() {
@Override
public void run(){
// task to perform
}
});
重要的是要记住有关Runnable
的一些事情。 与Message
不同, Runnable
不能被回收-一旦完成工作,它就死了。 由于Runnable
是标准Java包的一部分,因此它不依赖于Handler
,可以使用Runnable.run()
方法在标准Thread
上调用它。 但是,这种方法与HaMeR框架没有任何关系,也不会共享其任何优势。
5.发送信息
Message
对象定义一条消息,该消息包含描述和一些可以通过Handler
发送和处理的任意数据。 Message
由在Message.what()
上定义的int
标识。 Message
可以包含其他两个int
参数和一个用于存储不同类型数据的Object
。
-
Message.what
:标识Message
int
-
Message.arg1
:int
任意参数 -
Message.arg2
:int
任意参数 -
Message.obj
:用于存储各种数据的Object
当您需要发送一条消息时,而不是从头开始创建一条消息,建议的方法是使用Message.obtain()
或Handler.obtainMessage()
命令直接从全局池中检索回收的Handler.obtainMessage()
。 这些方法有一些不同的版本,可让您根据需要获取Message
。
Handler.obtainMessage()
常见用法是需要将消息发送到后台线程时。 您将使用与该线程的Looper
关联的Handler
来获取Message
并将其发送到后台线程,如以下示例所示。
int what = 0;
String hello = "Hello!";
// Obtaining Message associated with background Thread
Message msg = handlerBGThread.obtainMessage(what, hello);
// Sending the Message to background Thread
handlerBGThread.sendMessage(msg);
Message
类上有很多很酷的方法,我建议您仔细阅读文档 。
5.1。 sendMessage()
选项
与我们发布Runnable
的方式类似,有多个选项可以发送Message
:
-
Handler.sendMessage( Message msg )
:向MessageQueue
添加一条Message
。 -
Handler.sendMessageAtFrontOfQueue( Message msg )
:在MessageQueue
的前面添加一条Message
。
-
Handler.sendMessageAtTime( Message msg, long timeInMillis )
:在特定时间将Message
添加到队列。
-
Handler.sendMessageDelayed ( Message msg, long timeInMillis )
:在经过特定时间后,将Message
添加到队列。
使用处理程序处理消息
处理程序使用Handler.handleMessage
方法处理Looper
分派的Message
对象。 您要做的就是扩展Handler
类并重写此方法以处理消息。
public class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// handle 'Hello' msg
case 0:{
String hello = (String) msg.obj;
System.out.println(hello);
break;
}
}
}
}
6.结论
HaMeR框架可以帮助改善您的Android应用程序的并发代码。 与AsyncTask
的简单性相比,乍一看似乎令人困惑,但是如果正确使用HaMeR的开放性将是一个优势。
记得:
- 当发件人知道要执行哪些操作时,将使用
Handler.post()
方法。
- 接收者知道要执行的操作时,将使用
Handler.sendMessage ()
方法。
要了解有关Android线程的更多信息,您可能会对Anders Goransson的《 高效的Android线程:Android应用程序的异步处理技术 》一书感兴趣。
下一步是什么?
在下一个教程中,我们将通过构建一个演示使用此Android并发框架的不同方法的应用程序,继续通过动手方法探索HaMeR框架。 我们将从头开始创建此应用,尝试各种可能性,例如Threads
之间的通信,与UI线程交谈以及发送消息和延迟发布Runnable
。
再见!
翻译自: https://code.tutsplus.com/tutorials/concurrency-on-android-using-hamer-framework--cms-27129