Advertisement
  1. Code
  2. Android SDK
Code

在Android中使用HaMeR实现并发

by
Difficulty:IntermediateLength:LongLanguages:

Chinese (Simplified) (中文(简体)) translation by Zhang Xiang Liang (you can also view the original English article)

在文章 Understanding Concurrency on Android Using HaMeR 中,我们谈到了HaMeR(HandlerMessage和  Runnable)框架。 我们讲解了如何选择以及何时如何使用它。 

今天,我们将创建一个简单的应用程序来探索所学的知识。 通过实际操作,我们将了解如何用HaMeR管理Android平台上的并发。

1.示例应用程序

让我们开始工作吧, 在示例应用程序中发布  Runnable 并发送Message对象。 为了尽可能简单,我们只探索最有趣的部分。这里将忽略所有资源文件和标准activity调用。 因此,我强烈建议您查看示例应用程序的源代码。  因此,我强烈建议您查看示例应用程序的源代码。 

HaMeR sample application

该应用程序将包括:

  • 两个activities(安卓四大组件之一),一个为Runnable另一个为Message调用
  • 两个  HandlerThread 对象:
    • WorkerThread 从UI接收和处理调用
    • CounterThread 接收来自WorkerThreadMessage调用
  • 一些实体类(在配置更改期间为布局保留对象)

2.发布和接收Runnables

我们开始尝试使用Handler.post(Runnable) 方法及其变体,该方法及其变体将runnable添加到与线程关联的MessageQueue。 我们将创建RunnableActivity,它与称为WorkerThread的后台线程进行通信。

RunnableActivity组件实例化WorkerThread后台线程,参数需要HandlerWorkerThread.Callback。 该activity可以调用  WorkerThread 来异步下载位图,并在特定时间显示toast。 WorkerThread完成任务后的结果传递给RunnableActivity,该结果由Handler发送给WorkerThread的runnables传递过去。

2.1 为RunnableActivity准备Handler

RunnableActivity中, 我们将创建一个Handler用来传递给WorkerThread。 该Handler将与UI线程的Looper相关联,因为它从该线程被调用。

2.2 声明WorkerThread及其回调接口

WorkerThread是一个后台线程,在该线程我们将开始不同类型的任务。 它使用responseHandler 和实例化期间接收到的回调接口与用户界面进行通信。 从activities中获得的引用是  WeakReference<> 类型,因为activity可能被破坏并丢失引用。

该类提供了可由UI实现的界面。 它还继承了HandlerThread,是建立在Thread之上的帮助类,而且该Thread 已经包含了一个LooperMessageQueue。 因此,它可以使用HaMeR框架。

2.3 初始化 WorkerThread

我们需要给WorkerThread添加一个供activities调用的方法,该方法用来准备待使用的线程postHandler。 该方法只有在线程启动后才需要被调用。

RunnableActivity中,我们必须实现WorkerThread.Callback方法和初始化线程,这样就可以使用它。

2.4  在WorkerThread中使用Handler.post()

WorkerThread.downloadWithRunnable() 方法 用来下载位图并将其发送到RunnableActivity的ImageView中显示。 它展示了Handler.post(Runnable run)方法的两个基本用法  :

  • 允许线程将Runnable对象发布到MessageQueue, 当与Thread's Looper相关联的Handler中调用.post()时 。
  • 允许与其他线程进行通信,当与其他Thread's Looper相关联的Handler中调用.post()时 。
  1. WorkerThread.downloadWithRunnable()  方法的发布一个RunnableWorkerThreadMessageQueue,通过使用postHandler,这是与WorkThreadLooper相关联的的Handler
  2. 当runnable被处理时,就会在WorkerThread中下载Bitmap
  3. 在下载位图后,  responseHandler(这是与UI线程相关联的handler) 用于在包含位图的RunnableActivity中 发布runnable。
  4. runnable被处理,  WorkerThread.Callback.loadImage 用于在ImageView中显示下载的图像。

2.5使用Handler.postAtTime()和Activity.runOnUiThread()

WorkerThread.toastAtTime()  计划在特定时间执行的任务,比如显示Toast。 该方法使用了Handler.postAtTime() 和Activity.runOnUiThread()。

  • Handler.postAtTime(Runnable run, long uptimeMillis) 在给定的时间发布runnable 。
  • Activity.runOnUiThread(Runnable run) 使用默认UIhandler将runnable发布到主线程。

3.使用MessageActivityWorkerThread发送消息

接下来,我们来探讨使用 MessageActivity  来发送和处理  Message对象的一些不同方法 。   MessageActivity 实例化  WorkerThread,传递  Handler 作为参数。 WorkerThread 有一些公共方法由activity调用来下载一个位图,下载一个随机的位图或延时显示Toast。 所有这些操作的结果都将使用 responseHandler发送的Message对象回传给MessageActivity

3.1准备响MessageActivity的Handler

正如在RunnableActivity里面,在MessageActivity 里面,我们必须实例化和初始化WorkerThread来发送Handler,该Handler用来接收后台线程传来的数据。 但是,这一次我们不会实现WorkerThread.Callback; 相反,我们将从Message对象的WorkerThread中收到响应。

由于MessageActivityRunnableActivity的大部分代码基本相同,因此我们仅关注uiHandler的准备工作,并将其发送给WorkerThread来接收消息。

首先,我们提供int值作为Message对象的标识符。

MessageHandler的实现里面,我们必须继承Handler和实现handleMessage(Message)方法,在该方法里面处理所有的消息。 请注意,我们正在提取Message.what以识别消息,我们也从Message.obj中获取不同类型的数据。 在分析代码前,让我们快速查看最重要的Message属性。

  • Message.what:   int类型,用来 识别 Message
  • Message.arg1:   int类型 任意参数
  • Message.arg2: int类型 任意参数
  • Message.obj: Object 存储不同类型的数据

3.2使用WorkerThread发送Messages

现在让我们回到WorkerThread类。  我们将添加一些代码来下载特定的位图,还可以下载一个随机的代码。 为了完成这些任务,我们将把Message对象发送WorkerThread给它自己,并将结果回传给MessageActivity,使用之前RunnableActivity里面完全相同的逻辑。

首先,我们继承Handler 处理下载的消息。

downloadImageMSG(String url) 方法基本上与downloadImage(String url) 方法相同。 唯一的区别是,第一次将下载的位图回传给UI,是通过使用responseHandler发射消息。

loadImageOnUIMSG(Bitmap image)负责发送下载位图的消息给MessageActivity。 

请注意Message,我们正在使用Handler.obtainMessage(int what, Object obj)方法从全局池获取Message而不是从头开始创建Message对象  ,这样可以节省资源。 还要注意的是,我们调用了responseHandler的  obtainMessage()方法用来获取与MessageActivityLooper关联的Message。 有两种方法从全局池获取Message:  Message.obtain() 和  Handler.obtainMessage()。

剩下的任务是提供方法来发送  MessageWorkerThread以 开始下载进程。 注意,这一次我们会调用handlerMsgImgDownloaderMessage.obtain(Handler handler, int what, Object obj)  ,将消息与WorkerThread的looper进行关联。

另一个有趣的可能性是使用Message.sendMessageDelayed(Message msg, long timeMillis)方法发送  Message对象来延时处理。

我们创建了一个Handler用于发送延时消息。 我们不是继承Handler类,而是Handler使用Handler.Callback接口实例化Handler,为此我们实现了handleMessage(Message msg)方法来处理延时的  Message

4.结论

您现在已经了解了如何使用基本的HaMeR框架来管理Android中的并发。 在 final project stored on GitHub上面还有一些其他有趣的功能,强烈建议去看看。 

最后,有一些你应该记住的注意事项:

  •  在使用HaMeR和Threads时,不要忘记考虑Android的Activity生命周期。 否则,当线程尝试访问由于配置更改或其他原因导致activity销毁时,应用程序可能会失败。 常见的解决方案是使用RetainedFragment来存储线程,并在每次activity被销毁时使用activity引用来填充后台线程。 看看GitHub上的最终解决方案。
  • 由于Runnable和  Message对象,运行的任务 在Handlers上不会异步运行。 它们与handler相关联的线程同步运行。 为了使其异步,您需要创建另一个线程,在该线程上发送/发布Message/ Runnable对象并在适当的时候接收结果。

您可以看到,HaMeR框架有很多不同的可能性,它是一个相当开放的解决方案,有管理Android并发性的许多选项。 根据您的需要,这些特征可以优于AsyncTask。 探索更多的框架并阅读文档,您将创建更优秀的事物。

再见!

Envato qr branded
关注我们的公众号
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.