《Effieicntt Android Threading》 Chapter4 -- Thread Communication(2)

来源:互联网 发布:mpg制作软件 编辑:程序博客网 时间:2024/05/18 03:50

序:
阅读英文原版,受益匪浅。修饰词形容惟妙惟肖。
其中举例之图,经典,且秒不可言。亦当细细品味之。
翻译为谷歌翻译。 重点单词日后补上,可用有道词典积累,认识的单词会越来越多
不认识的单词亦会越来越少,而翻译并不能完全体现作者本意,亦可备注评论之上。
佛祖拈花 迦叶一笑。观者千人,体会亦千面。 所思所想亦可评论,互相交流。
亦可以锻炼英文文档阅读能力。

整理如下。

接上一篇

Classes Used in Message Passing

Let’s take a more detailed look now at the specific components of message passing and their use.

MessageQueue

The message queue is represented by the android.os.MessageQueue class. It is built with linked messages, constituting an unbound one-directional linked list. Producer threads insert messages that will later be dispatched to the consumer. The messages are sorted based on timestamps. The pending message with the lowest timestamp value is first in line for dispatch to the consumer. However, a message is dispatched only if the timestamp value is less than the current time. If not, the dispatch will wait until the current time has passed the timestamp value.
Figure 4-6 illustrates a message queue with three pending messages, sorted with time‐ stamps where t1 < t2 < t3. Only one message has passed the dispatch barrier, which is the current time. Messages eligible for dispatch have a timestamp value less than the current time (represented by “Now” in the figure).

这里写图片描述

消息队列由android.os.MessageQueue类表示。 它是用链接消息构建的,构成一个未绑定的单向链接列表。 生产者线程插入以后将被分派给消费者的消息。 消息按时间戳排序。 具有最低时间戳值的待处理消息首先符合消费者的调度。 但是,只有当时间戳值小于当前时间时才调度消息。 如果没有,则调度将等待直到当前时间通过时间戳值。

图4-6示出了具有三个待处理消息的消息队列,其中t1’<’ t2 ‘<’t3的时间戳排序。 只有一条消息已经通过了调度屏障,这是目前的时间。 符合调度条件的消息具有小于当前时间的时间戳值(由图中的“Now”表示)。

Figure 4-6. Pending messages in the queue. The rightmost message is first in queue to be processed. The message arrows denote references to the next message in the queue.

图4-6。 待处理消息在队列中。 最右边的消息首先在队列中进行处理。 消息箭头表示对队列中下一个消息的引用。

If no message has passed the dispatch barrier when the Looper is ready to retrieve the next message, the consumer thread blocks. Execution is resumed as soon as a message passes the dispatch barrier.

The producers can insert new messages in the queue at any time and on any position in the queue. The insert position in the queue is based on the timestamp value. If a new message has the lowest timestamp value compared to the pending messages in the queue, it will occupy the first position in the queue, which is next to be dispatched. Insertions always conform to the timestamp sorting order. Message insertion is dis‐ cussed further in “Handler” on page 60.

如果当Looper准备好检索下一条消息时,没有消息已经通过调度障碍,消费者线程将阻塞。 一旦消息通过调度屏障,就会恢复执行。

生产者可以随时在队列中的任何位置将新消息插入到队列中。 队列中的插入位置基于时间戳值。 如果新消息与队列中的待处理消息相比具有最低的时间戳值,则它将占据队列中的第二个位置,该队列旁边将被分派。 插入始终符合时间戳排序顺序。 第60页的“处理程序”进一步介绍了消息插入。

MessageQueue.IdleHandler

If there is no message to process, a consumer thread has some idle time. For instance, Figure 4-7 illustrates a time slot where the consumer thread is idle. By default, the consumer thread simply waits for new messages during idle time; but instead of waiting, the thread can be utilized to execute other tasks during these idle slots. This feature can be utilized to let noncritical tasks postpone their execution until no other messages are competing for execution time.

这里写图片描述

Figure 4-7. If no message has passed the dispatch barrier, there is a time slot that can be utilized for execution before the next pending message needs to be executed

When a pending message has been dispatched, and no other message has passed the dispatch barrier, a time slot occurs where the consumer thread can be utilized for execution of other tasks. An application gets hold of this time slot with the android.os.MessageQueue.IdleHandler-interface, a listener that generates callbacks when the consumer thread is idle. The listener is attached to the MessageQueue and detached from it through the following calls:

// Get the message queue of the current thread.
MessageQueue mq = Looper.myQueue();
// Create and register an idle listener.
MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler(); mq.addIdleHandler(idleHandler)
// Unregister an idle listener.
mq.removeIdleHandler(idleHandler)

MessageQueue.IdleHandler

如果没有消息要处理,消费者线程有一些空闲时间。 例如,图4-7显示了消费者线程空闲的时隙。 默认情况下,消费者线程在空闲时间内等待新消息; 而不是等待,线程可以用于在这些空闲时隙期间执行其他任务。 可以利用此功能让非关键任务推迟执行,直到没有其他消息竞争执行时间为止。

图4-7。 如果没有消息通过调度障碍,那么在下一个挂起的消息需要执行之前,有一个时隙可以用于执行

当调度等待消息,并且没有其他消息已经通过调度障碍时,发生消费者线程可用于执行其他任务的时隙。 应用程序通过android.os.MessageQueue.IdleHandler接口(一个在消费者线程空闲时产生回调)的侦听器来掌握这个时隙。 侦听器附加到MessageQueue并通过以下调用从其中分离:

//获取当前线程的消息队列。
MessageQueue mq = Looper.myQueue();
//创建并注册一个空闲的侦听器。
MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler();
mq.addIdleHandler(idleHandler)
//取消注册空闲侦听器。
mq.removeIdleHandler(idleHandler)

The idle handler interface consists of one callback method only:

interface IdleHandler { boolean queueIdle();}

When the message queue detects idle time for the consumer thread, it invokes queueI dle() on all registered IdleHandler-instances. It is up to the application to implement the callback responsibly. You should usually avoid long-running tasks because they will delay pending messages during the time they run.
The implementation of queueIdle() must return a Boolean value with the following meanings:

true

The idle handler is kept active; it will continue to receive callbacks for successive idle time slots.

false

The idle handler is inactive; it will not receive anymore callbacks for successive idle time slots. This is the same thing as removing the listener through Message Queue.removeIdleHandler().

空闲处理程序接口仅由一个回调方法组成:

interface IdleHandler { boolean queueIdle();}

当消息队列检测到消费者线程的空闲时间时,它会在所有注册的IdleHandler实例上调用queueIdle()。 由应用程序负责地实现回调。 您通常应避免长时间运行的任务,因为它们将在运行时延迟等待的消息。
queueIdle()的实现必须返回一个具有以下含义的布尔值:

真正

空闲处理程序保持活动状态; 它将继续接收连续空闲时隙的回叫。

空闲处理程序处于非活动状态; 它不会再接收到连续空闲时隙的回调。 这与删除通过Message Queue.removeIdleHandler()的侦听器是一样的。

Example: Using IdleHandler to terminate an unused thread

All registered IdleHandlers to a MessageQueue are invoked when a thread has idle slots, where it waits for new messages to process. The idle slots can occur before the first message, between messages, and after the last message. If multiple content producers should process data sequentially on a consumer thread, the IdleHandler can be used to terminate the consumer thread when all messages are processed so that the unused thread does not linger in memory. With the IdleHandler, it is not necessary to keep track of the last inserted message to know when the thread can be terminated.

This use case applies only when the producing threads insert mes‐ sages in the MessageQueue without delay, so that the consumer thread is never idle until the last message is inserted.

示例:使用IdleHandler终止未使用的线程

当线程有空闲插槽时,调用MessageQueue的所有注册的IdleHandler,并等待新消息处理。 空闲时隙可以在第一个消息之前,消息之间和最后一个消息之后发生。 如果多个内容制作者应该在消费者线程上顺序处理数据,则可以使用IdleHandler在处理所有消息时终止消费者线程,以使未使用的线程不会停留在内存中。 使用IdleHandler,没有必要跟踪最后插入的消息,以知道线程何时被终止。

此用例仅适用于生产线程在MessageQueue中无延迟地插入消息,以便消耗线程在插入最后一条消息之前不会空闲。

The ConsumeAndQuitThread method shows the structure of a consuming thread with Looper and MessageQueue that terminates the thread when there are no more messages to process:

public class ConsumeAndQuitThread extends Thread    implements MessageQueue.IdleHandler {    private static final String THREAD_NAME = "ConsumeAndQuitThread";    public Handler mConsumerHandler;    private boolean mIsFirstIdle = true;    public ConsumeAndQuitThread() {        super(THREAD_NAME);    }    @Override    public void run() {        Looper.prepare();        mConsumerHandler = new Handler() {            @Override            public void handleMessage(Message msg) {                    // Consume data            }        };        /*1.Register the IdleHandler on the background thread when it is started and the Looper is prepared so that the MessageQueue is set up.        */        Looper.myQueue().addIdleHandler(this);        Looper.loop();    }    @Override    public boolean queueIdle() {    /*    2.Let the first queueIdle invocation pass, since it occurs before the first message is received.    */        if (mIsFirstIdle) {             mIsFirstIdle = false;            /*            3.Return true on the first invocation so that the IdleHandler still is registered.            */            return true;         }        //4.Terminate the thread.        mConsumerHandler.getLooper().quit();         return false;    }    public void enqueueData(int i) {        mConsumerHandler.sendEmptyMessage(i);    }}

1.Register the IdleHandler on the background thread when it is started and the Looper is prepared so that the MessageQueue is set up.
2.Let the first queueIdle invocation pass, since it occurs before the first message is received.
3.Return true on the first invocation so that the IdleHandler still is registered.
4.Terminate the thread.
1.在启动后,在后台线程上注册IdleHandler,并准备好Looper,以便MessageQueue被设置。
2.第一个队列调用通过,因为它发生在第一个消息被接收之前。
3.在第一次调用时返回true,以便IdleHandler仍然被注册。
4.终止线程。

The message insertion is done from multiple threads concurrently, with a simulated randomness of the insertion time:
消息插入是从多个线程同时完成的,具有插入时间的模拟随机性:

final ConsumeAndQuitThread consumeAndQuitThread = new ConsumeAndQuitThread(); consumeAndQuitThread.start();for (int i = 0; i < 10; i++) {    new Thread(new Runnable() {        @Override        public void run() {            for (int i = 0; i < 10; i++) {                SystemClock.sleep(new Random().nextInt(10));                consumeAndQuitThread.enqueueData(i);            }        }    }).start();

Each item on the MessageQueue is of the android.os.Message class. This is a container object carrying either a data item or a task, never both. Data is processed by the consumer thread, whereas a task is simply executed when it is dequeued and you have no other processing to do:

MessageQueue上的每个项目都是android.os.Message类。 这是一个携带数据项或任务的容器对象,从不两者。 数据由消费者线程处理,而任务在出队时就被简单地执行,并且没有其他处理可以做:

The message knows its recipient processor—i.e., Handler—and can enqueue itself through Message.sendToTarget():

Message m = Message.obtain(handler, runnable); m.sendToTarget();

As we will see in “Handler” on page 60, the handler is most commonly used for message enqueuing, as it offers more flexibility with re‐ gard to message insertion.

该消息知道其接收者处理器(即Handler),并且可以通过Message.sendToTarget()入队:

Message m = Message.obtain(handler, runnable); m.sendToTarget();

正如我们将在第60页的“处理程序”中看到的,处理程序最常用于消息排队,因为它提供了更多的灵活性,适用于消息插入。

Data message
The data set has multiple parameters that can be handed off to the consumer thread, as shown in Table 4-2.

数据信息
数据集有多个可以切换到消费者线程的参数,如表4-2所示。

这里写图片描述

Task message
The task is represented by a java.lang.Runnable object to be executed on the consumer thread. Task messages cannot contain any data beyond the task itself.
A MessageQueue can contain any combination of data and task messages. The consumer thread processes them in a sequential manner, independent of the type. If a message is a data message, the consumer processes the data. Task messages are handled by letting the Runnable execute on the consumer thread, but the consumer thread does not receive a message to be processed in Handler.handleMessage(Message), as it does with data messages.

The lifecycle of a message is simple: the producer creates the message, and eventually it is processed by the consumer. This description suffices for most use cases, but when a problem arises, a deeper understanding of message handling is invaluable. Let us take a look into what actually happens with the message during its lifecycle, which can be split up into four principal states shown in Figure 4-8. The runtime stores message objects in an application-wide pool to enable the reuse of previous messages; this avoids the overhead of creating new instances for every handoff. The message object execution time is normally very short, and many messages are processed per time unit.

任务信息
该任务由在消费者线程上执行的java.lang.Runnable对象表示。任务消息不能包含任务本身以外的任何数据。
MessageQueue可以包含数据和任务消息的任意组合。消费者线程以连续的方式处理它们,而与类型无关。如果消息是数据消息,则消费者处理数据。任务消息由lett处理Runnable在消费者线程上执行,但消费者线程不会收到要在Handler.handleMessage(Message)中处理的消息,与数据消息一样。

消息的生命周期很简单:生产者创建消息,最终由消费者处理。这个描述对于大多数用例来说就足够了,但是当出现问题时,对消息处理的深入理解是无价的。让我们来看看消息在其生命周期中实际发生的情况,可以分为四个主要状态,如图4-8所示。运行时将消息对象存储在应用程序范围的池中,以便重新使用以前的消息;这避免了为每次切换创建新实例的开销。消息对象执行时间通常很短,并且每个时间单位处理许多消息。

这里写图片描述

The state transfers are partly controlled by the application and partly by the platform. Note that the states are not observable, and an application cannot follow the changes from one state to another (although there are ways to follow the movement of messages, explained in “Observing the Message Queue” on page 70). Therefore, an application should not make any assumptions about the current state when handling a message.

Initialized

In the initialized state, a message object with mutable state has been created and, if it is a data message, populated with data. The application is responsible for creating the message object using one of the following calls. They take an object from the object pool:

状态转移部分由应用程序控制,部分由平台控制。 请注意,状态是不可观察的,并且应用程序不能跟踪从一个状态到另一个状态的更改(尽管有多种方法可以跟踪消息的移动,在第70页的“观察消息队列”中进行了说明)。 因此,应用程序在处理消息时不应对当前状态做出任何假设。

初始化

在初始化状态下,已经创建了具有可变状态的消息对象,如果它是数据消息,则填充数据。 应用程序负责使用以下调用之一创建消息对象。 他们从对象池中获取一个对象:

• Explicit object construction:

Message m  = new Message();

Factory methods:
— Empty message:

Message m = Message.obtain();    

— Data message:

Message m = Message.obtain(Handler h);Message m = Message.obtain(Handler h, int what); Message m = Message.obtain(Handler h, int what, Object o); Message m = Message.obtain(Handler h, int what, int arg1, int arg2); Message m = Message.obtain(Handler h, int what, int arg1, int arg2,                           Object o);

— Task message:

 Message m = Message.obtain(Handler h, Runnable task);

— Copy constructor:

 Message m = Message.obtain(Message originalMsg);

Pending

The message has been inserted into the queue by the producer thread, and it is waiting to be dispatched to the consumer thread.

Dispatched

In this state, the Looper has retrieved and removed the message from the queue. The message has been dispatched to the consumer thread and is currently being processed. There is no application API for this operation because the dispatch is controlled by the
Looper , without the influence of the application. When the Looper dispatches a message, it checks the delivery information of the message and delivers the message to the correct recipient. Once dispatched, the message is executed on the consumer thread.

Recycled

At this point in the lifecycle, the message state is cleared and the instance is returned to the message pool. The Looper handles the recycling of the message when it has finished
executing on the consumer thread. Recycling of messages is handled by the runtime and should not be done explicitly by the application.

有待
消息已由生产者线程插入到队列中,并且正在等待被分派到消费者线程。
调度
在这种状态下,Looper已经从队列中检索并删除了该消息。 消息已经被分发到消费者线程,并且正在被处理。 没有应用程序API用于此操作,因为调度由…控制
Looper,没有应用程序的影响。 当Looper发送消息时,它会检查消息的传递信息,并将消息传递给正确的收件人。 一旦分派,消息在消费者线程上执行。
回收
在生命周期的这一时刻,消息状态被清除,实例返回到消息池。 Looper处理完消息后的消息回收
在消费者线程上执行。 消息的回收由运行时处理,不应由应用程序明确地完成。

&&&&
Once a message is inserted in the queue, the content should not be altered. In theory, it is valid to change the content before the mes‐ sage is dispatched. However, because the state is not observable, the message may be processed by the consumer thread while the pro‐ ducer tries to change the data, raising thread safety concerns. It would be even worse if the message has been recycled, because it then has been returned to the message pool and possibly used by another producer to pass data in another queue.

一旦消息插入队列中,内容不应该被更改。 在理论上,在发送消息之前更改内容是有效的。 然而,由于状态不可观察,消息线程可能由消费者线程处理,同时生产者尝试更改数据,从而提高线程安全性。 如果消息已经被回收,那么它将更糟,因为它已经被返回到消息池,并且可能被另一个生产者使用以将数据传递到另一个队列中。

阅读全文
0 0
原创粉丝点击