使用HaMeR了解Android上的并发

1.简介

尝试Android开发的每个人都发现并发的重要性。 创建响应式应用程序的唯一方法是让UI线程尽可能自由,让所有艰苦的工作由后台线程异步完成。

由于Android的设计,仅使用java.lang.threadjava.util.concurrent包管理线程可能真的很困难。 在Android上使用低级线程程序包意味着您必须担心很多棘手的同步操作,以避免出现竞争状况。 幸运的是,Google的人们辛勤工作,并构建了一些出色的工具来IntentService我们的工作: AsyncTaskIntentServiceLoaderAsyncQueryHandlerCursorLoader都很有用,以及HaMeR类HandlerMessageRunnable 。 有很多很棒的选择供您选择,每种选择都有其优缺点。

但是, AsyncTask不应是工具带上的唯一工具。

对于长时间运行的操作,复杂的并发问题或在某些情况下要提高效率,您应该选择另一种解决方案。 如果您需要比AsyncTask更高的灵活性或效率,则可以使用HaMeR( HandlerMessageRunnable )框架。   在本教程中,我们将探索HaMeR框架,它是Android上最强大的并发模型之一,并且将学习何时以及如何使用它。 在后续教程中,我将向您展示如何编写应用程序代码以尝试HaMeR的一些可能性。

下一节将介绍背景线程对Android系统的重要性。 如果您熟悉此概念,请随时跳过它,直接进入第3节中有关HaMeR框架的讨论。

2.通过后台线程进行响应

启动Android应用程序时,其进程产生的第一个线程是主线程,也称为UI线程,它负责处理所有用户界面逻辑。 这是应用程序中最重要的线程。 它负责处理所有用户交互,还负责将应用程序的移动部分“绑在一起”。 Android非常重视这一点,如果您的UI线程在某项任务上停留超过几秒钟,则该应用程序将崩溃。

[UI线程]非常重要,因为它负责将事件调度到适当的用户界面小部件,包括绘图事件。 这也是您的应用程序与Android UI工具包中的组件( android.widgetandroid.view包中的组件)进行交互的线程。 这样,主线程有时也称为UI线程。 — 进程和线程 ,Android开发人员指南

问题在于,默认情况下,Android应用程序中的几乎所有代码都将在UI线程上执行。 由于线程上的任务是顺序执行的,因此这意味着您的用户界面可能会“冻结”,在处理其他工作时会变得无响应。

使用后台线程让UI线程尽可能自由

在UI上调用的长时间运行的任务可能会对您的应用程序致命,并且将出现ANR(应用程序无响应)对话框。 即使是很小的任务也会损害用户体验,因此正确的方法是使用后台线程从UI线程中删除尽可能多的工作。 如前所述,有很多方法可以解决此问题,我们将探索HaMeR框架,它是Android为解决这种情况而提供的核心解决方案之一。

3. HaMeR框架

HaMeR框架允许后台线程通过处理程序将消息或可运行对象发送到UI线程和任何其他线程的MessageQueue 。 HaMeR引用HandlerMessageRunnable 。 还有其他一些与HaMeR一起使用的重要类: LooperMessageQueue 。 这些对象共同负责促进Android上的线程管理,进行同步,并为后台线程与UI和其他线程通信提供简单方法。

这是HaMeR框架中的类如何组合在一起的方式。

HaMeR框架
  • Looper使用MessageQueue在线程上运行消息循环。
  • MessageQueue包含Looper调度的消息列表。
  • Handler允许向MessageQueue发送和处理MessageRunnable 。 它可用于在线程之间发送和处理消息。
  • Message包含描述和可以发送到处理程序的数据。
  • Runnable表示要执行的任务。

使用HaMeR框架,线程可以向自身或UI线程发送消息或发布可运行的对象。 HaMeR还通过Handler促进后台线程交互。

处理程序类

Handler是HaMeR的主力军。 它负责发送Message (数据消息)并将Runnable (任务消息)对象发布到与Thread关联的MessageQueue中。 将任务传递到队列后,处理程序从Looper接收对象,并在适当的时间使用与其关联的Handler消息。

Handler程序可用于在线程之间发送或发布MessageRunnable对象,只要这些线程共享相同的进程即可。 否则,有必要创建进程间通信(IPC) ,这种方法超出了本教程的范围。

实例化处理程序

Handler必须始终与Looper关联,并且此连接需要在其实例化期间进行。 如果不向Handler提供Looper ,它将绑定到当前ThreadLooper

// Handler uses current Thread's Looper
Handler handler = new Handler();

// Handler uses the Looper provides
Handler handler = new Handler(Looper);

请记住, Handler程序始终与Looper关联,并且此连接是永久的,一旦建立就无法更改。 但是, Looper的线程可以与多个Handler关联。 同样重要的是要注意,在将LooperHandler关联之前,必须将其激活。

Looper和MessageQueue

Java线程中LooperMessageQueue之间的协作工作创建了一个按顺序处理的任务循环。 这样的循环将在等待接收更多任务时使线程保持活动状态。 一个线程只能有一个Looper和一个与之关联的MessageQueue 。 但是,每个线程可以有多个处理程序。 处理程序负责处理队列中的任务,每个任务都知道哪个处理程序负责其处理。

为HaMeR准备线程

UI或主线程是唯一一种默认情况下已经具有HandlerLooperMessageQueue的线程。 其他线程必须与这些对象一起准备,然后才能与HaMeR框架一起使用。 首先,我们需要创建一个已经包含MessageQueueLooper并将其附加到线程。 您可以使用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内置的LooperMessageQueue并准备接收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.arg1int任意参数
  • Message.arg2int任意参数
  • 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值