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

整理如下。

In multithreaded appplications, tasks can run in parallel and collaborate to produce a result. Hence, threads have to be able to communicate to enable true asynchronous processing. In Android, the importance of thread communication is emphasized in the platform-specific handler/looper mechanism that is the focus in this chapter, together with the traditional Java techniques. The chapter covers:

• Passing data through a one-way data pipe
• Shared memory communication
• Implementing a consumer-producer pattern with BlockingQueue
• Operations on message queues
• Sending tasks back to the UI Thread

在多线程应用程序中,任务可以并行运行并进行协作以产生结果。 因此,线程必须能够通信才能实现真正的异步处理。 在Android中,线程通信的重要性在平台特定的处理程序/打圈机制中被强调,这是本章的重点,以及传统的Java技术。 本章涵盖:
•通过单向数据管道传递数据
•共享内存通信
•使用BlockingQueue实现消费者生产者模式
•消息队列的操作
•将任务发回UI线程

Pipes

Pipes are a part of the java.io package. That is, they are general Java functionality and not Android specific. A pipe provides a way for two threads, within the same process, to connect and establish a one-way data channel. A producer thread writes data to the pipe, whereas a consumer thread reads data from the pipe.
管道
管道是java.io包的一部分。 也就是说,它们是一般的Java功能,而不是Android特定的。 管道为同一进程中的两个线程提供连接和建立单向数据通道的方式。 生产者线程将数据写入管道,而消费者线程从管道读取数据。

The Java pipe is comparable to Unix and Linux pipe operator(the | shell character) that is used to redirect the output from one command to the input for another command. The pipe operator works across Processes in Linux, but java pipes works across threads in the virtual machine, for example, within a process.

Java管道与用于将输出从一个命令重定向到另一个命令的输入的Unix和Linux管道操作符(| shell字符)相当。 管道操作员在Linux中跨进程工作,但Java管道可以跨虚拟机的线程工作,例如在一个进程中。

The pipe itself is a circular buffer allocated in memory, available only to the two connected threads. No other threads can access the data. Hence, thread safety—discussed in “Thread Safety” on page 19—is ensured. The pipe is also one-directional, permitting just one thread to write and the other to read (Figure 4-1).

管道本身是分配在内存中的循环缓冲区,仅对两个连接的线程可用。 没有其他线程可以访问数据。 因此,确保了在第19页的“螺纹安全”中讨论的螺纹安全。 管道也是单向的,只允许一个线程写入,另一个线程读取(图4-1)。

《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第1张图片

Pipes are typically used when you have two long-running tasks and one has to offload data to another continuously. Pipes make it easy to decouple tasks to several threads, instead of having only one thread handle many tasks. When one task has produced a result on a thread, it pipes the result on to the next thread that processes the data further. The gain comes from clean code separation and concurrent execution. Pipes can be used between worker threads and to offload work from the UI thread, which you want to keep light to preserve a responsive user experience.

当您有两个长时间运行的任务并且必须将数据连续卸载到另一个时,通常会使用管道。 管道使得轻松将任务分离到多个线程,而不是只有一个线程处理许多任务。 当一个任务在一个线程上产生一个结果时,它将结果管理到进一步处理数据的下一个线程。 增益来自于干净的代码分离和并发执行。 管道可以在工作线程之间使用,并从UI线程卸载工作,您希望保持光线以保持响应式用户体验。

A pipe can transfer either binary or character data. Binary data transfer is represented by PipedOutputStream (in the producer) and PipedInputStream (in the consumer), whereas character data transfer is represented by PipedWriter (in the producer) and PipedReader (in the consumer). Apart from the data transfer type, the two pipes have similar functionality. The lifetime of the pipe starts when either the writer or the reader thread establishes a connection, and it ends when the connection is closed.

管道可以传输二进制或字符数据。 二进制数据传输由PipedOutputStream(在生产者中)和PipedInputStream(消费者)中表示,而字符数据传输由PipedWriter(在生产者中)和PipedReader(在消费者中)表示。 除了数据传输类型,两个管道具有相似的功能。 当作者或阅读器线程建立连接时,管道的使用寿命将开始,并且在连接关闭时结束。
Basic Pipe Use

The fundamental pipe life cycle can be summarized in three steps: setup, data transfer (which can be repeated as long as the two threads want to exchange data), and discon‐ nection. The following examples are created with PipedWriter/PipedReader, but the same steps work with PipedOutputStream/PipedInputStream.
1. Set up the connection:
PipedReader r = new PipedReader(); PipedWriter w = new PipedWriter(); w.connect(r);
Here, the connection is established by the writer connecting to the reader. The connection could just as well be established from the reader. Several constructors also implicitly set up a pipe. The default buffer size is 1024 but is configurable from the consumer side of the pipe, as shown later:

int BUFFER_SIZE_IN_CHARS = 1024 * 4;
PipedReader r = new PipedReader(BUFFER_SIZE_IN_CHARS);
PipedWriter w = new PipedWriter(r);

基本的管道生命周期可以分为三个步骤:设置,数据传输(只要两个线程要交换数据就可以重复)和断开。 以下示例使用PipedWriter / PipedReader创建,但与PipedOutputStream / PipedInputStream相同的步骤也可以使用。
1.设置连接:
PipedReader r = new PipedReader(); PipedWriter w = new PipedWriter();w.connect(R);
这里连接是由写入器连接读写器建立的。 连接也可以从读者身上建立起来。 几个构造函数也隐含地设置了一个管道。 默认缓冲区大小为1024,但可以从管道的消费者端进行配置,如下所示:
int BUFFER_SIZE_IN_CHARS = 1024 * 4;
PipedReader r = new PipedReader(BUFFER_SIZE_IN_CHARS);
PipedWriter w = new PipedWriter(r);

  1. Pass the reader to a processing thread:
    Thread t = new MyReaderThread(r);
    t.start();
    After the reader thread starts, it is ready to receive data from the writer.

2.将阅读器传递给处理线程:
Thread t = new MyReaderThread(r);
t.start();
阅读器线程启动后,就可以从作者接收数据。

  1. Transfer data:
    // Producer thread: Write single character or array of characters
    w.write(‘A’);
    // Consumer thread: Read the data
    int result = r.read();

3.传输数据:
//生产者线程:写入单个字符或字符数组
w.write( ‘A’);
//消费线程:读取数据
int result = r.read();

Communication adheres to the consumer-producer pattern with a blocking mech‐ anism. If the pipe is full, the write() method will block until enough data has been read, and consequently removed from the pipe, to leave room for the data the writer is trying to add. The read() method blocks whenever there is no data to read from the pipe. It’s worth noticing that the read() method returns the character as an integer value to ensure that enough space is available to handle various encoding with different sizes. You can cast the integer value back to a character.
In practice, a better approach would look like this:
// Producer thread: Flush the pipe after a write.
w.write(‘A’);
w.flush();
// Consumer thread: Read the data in a loop.
int i;
while((i = reader.read()) != -1){
char c = (char) i;
// Handle received data
}
Calling flush() after a write to the pipe notifies the consumer thread that new data is available. This is useful from a performance perspective, because when the buffer is empty, the PipedReader uses a blocking call to wait() with one-second timeout. Hence, if the flush() call is omitted, the consumer thread may delay the reading of data up to one second. By calling flush(), the producer cuts short the wait in the consumer thread and allows data processing to continue immediately.

沟通坚持消费者生产者模式,阻止机制。如果管道已满,则write()方法将阻塞,直到读取足够的数据,并从管道中删除,为作者尝试添加的数据留下空间。只要没有数据从管道中读取,read()方法就会阻塞。值得注意的是,read()方法将字符返回为整数值,以确保有足够的空间来处理不同大小的各种编码。您可以将整数值转换回一个字符。
实际上,一个更好的方法是这样的:
//生产者线程:写入后冲洗管道。
w.write( ‘A’);
w.flush();
//消费者线程:读取循环中的数据。
int i
while((i = reader.read())!= -1){
char c =(char)i;
//处理收到的数据
}
在写入管道后调用flush()会通知消费者线程新的数据可用。这从性能角度来看是有用的,因为当缓冲区为空时,PipedReader会使用一秒钟超时的wait()调用。因此,如果省略flush()调用,消费者线程可能会延迟数据读取长达1秒。通过调用flush(),生产者缩短消费者线程中的等待时间,并允许数据处理立即继续。

  1. Close the connection.
    When the communication phase is finished, the pipe should be disconnected:
    // Producer thread: Close the writer.
    w.close();
    // Consumer thread: Close the reader.
    r.close();
    If the writer and reader are connected, it’s enough to close only one of them. If the writer is closed, the pipe is disconnected but the data in the buffer can still be read. If the reader is closed, the buffer is cleared.

4.关闭连接。
通信阶段完成后,管道应断开连接:
//生产者线程:关闭作者。
w.close();
//消费者线程:关闭阅读器。
r.close();
如果作家和读者连接在一起,只需关闭其中一个。 如果写入器关闭,则管道断开,但缓冲区中的数据仍然可以读取。 如果读卡器关闭,则清除缓冲区。

Example: Text Processing on a Worker Thread
This next example illustrates how pipes can process text that a user enters in an Edit Text. To keep the UI thread responsive, each character entered by the user is passed to a worker thread, which presumably handles some time-consuming processing:

示例:工作线程上的文本处理
下一个例子说明了管道如何处理用户在编辑文本中输入的文本。 为了保持UI线程的响应,用户输入的每个字符都被传递给一个工作线程,这可能会处理一些耗时的处理:

package com.yilihuyu.cartoonc;import android.app.Activity;import android.os.Bundle;import android.text.Editable;import android.text.TextWatcher;import android.widget.EditText;import java.io.IOException;import java.io.PipedReader;import java.io.PipedWriter;import static android.R.attr.editable;/** * Created by fotoable on 2017/5/24. *///public class TestAAAA {public class PipeExampleActivity extends Activity {    private static final String TAG = "PipeExampleActivity";    private EditText editText;    PipedReader r;    PipedWriter w;    private Thread workerThread;    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        r = new PipedReader();        w = new PipedWriter();        try {            w.connect(r);        } catch (IOException e) {            e.printStackTrace();        }        setContentView(R.layout.activity_pipe);        editText = (EditText) findViewById(R.id.edit_text);        editText.addTextChangedListener(new TextWatcher() {            @Override            public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {            }            @Override            public void onTextChanged(CharSequence charSequence, int start, int before, int count) {                try {// Only handle addition of characters if(count > before) {                    // Write the last entered character to the pipe                    w.write(charSequence.subSequence(before, count).                            toString());                } catch (IOException e) {                    e.printStackTrace();                }            }            @Override            public void afterTextChanged(Editable editable) {            }        });        workerThread = new Thread(new TextHandlerTask(r));        workerThread.start();    }    @Override    protected void onDestroy() {        super.onDestroy();        workerThread.interrupt();        try {            r.close();            w.close();        } catch (IOException e) {        }    }    private static class TextHandlerTask implements Runnable {        private final PipedReader reader;        public TextHandlerTask(PipedReader reader) {            this.reader = reader;        }        @Override        public void run() {            while (Thread.currentThread().isInterrupted()) {                try {                    int i;                    while ((i = reader.read()) != -1) {                        char c = (char) i;                        //ADD TEXT PROCESSING LOGIC HERE Log.d(TAG, "char = " + c);                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}

When the PipeExampleActivity is created, it will show an EditText box, which has a listener (TextWatcher) for changes in the content. Whenever a new character is added in the EditText, the character will be written to the pipe and read in the TextHandler Task. The consumer task is an infinite loop that reads a character from the pipe as soon as there is anything to read. The inner while-loop will block when calling read() if the pipe is empty.

示例:工作线程上的文本处理
下一个例子说明了管道如何处理用户在编辑文本中输入的文本。 为了保持UI线程的响应,用户输入的每个字符都被传递给一个工作线程,这可能会处理一些耗时的处理:……

Be careful when involving the UI thread with pipes, due to the pos‐ sible blocking of calls if the pipe is either full (producer blocks on its write() call) or empty (consumer blocks on its read() call).

如果管道已满(生成器块在其write()调用)或空(消耗块阻塞其read()调用)时,由于可能阻塞调用,请注意使用UI线程。

Shared Memory

Shared memory (using the memory area known in programming as the heap) is a com‐ mon way to pass information between threads. All threads in an application can access the same address space within the process. Hence, if one thread writes a value on a variable in the shared memory, it can be read by all the other threads, as shown in Figure 4-2.
共享内存
共享内存(使用编程中已知的存储区作为堆)是在线程之间传递信息的通用方式。 应用程序中的所有线程都可以在进程内访问相同的地址空间。 因此,如果一个线程在共享内存中的变量上写入一个值,那么所有其他线程都可以读取它,如图4-2所示。

《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第2张图片

If a thread stores data as a local variable, no other thread can see it. By storing it in shared memory, it can use the variables for communication and share work with other threads. Objects are stored in the shared memory if they are scoped as one of the following:
• Instance member variables
• Class member variables
• Objects declared in methods
The reference of an object is stored locally on the thread’s stack, but the object itself is stored in shared memory. The object is accessible from multiple threads only if the method publishes the reference outside the method scope, for example, by passing the reference to another object’s method. Threads communicate through shared memory by defining instance and class fields that are accessible from multiple threads.

如果线程将数据存储为局部变量,则其他线程都不会看到它。 通过将其存储在共享内存中,它可以使用变量进行通信,并与其他线程共享工作。 对象存储在共享内存中,如果它们的范围如下:
•实例成员变量
•类成员变量
•方法中声明的对象
一个对象的引用被本地存储在线程的堆栈上,但对象本身存储在共享内存中。 只有方法在方法范围外发布引用,例如通过将引用传递给另一个对象的方法,该对象才可从多个线程访问。 线程通过共享内存通过定义可从多个线程访问的实例和类字段进行通信。

Signaling

While threads are communicating through the state variables on the shared memory, they could poll the state value to fetch changes to the state. But a more efficient mech‐ anism is the Java library’s built-in signaling mechanism that lets a thread notify other threads of changes in the state. The signaling mechanism varies depending on the syn‐ chronization type (see Table 4-1).
Table 4-1. Thread signaling

信令
当线程通过共享内存上的状态变量进行通信时,他们可以轮询状态值以获取状态更改。 但是更有效的机制是Java库的内置信令机制,让线程通知其他线程的状态变化。 信令机制取决于同步类型(见表4-1)。
表4-1。 线程信令

《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第3张图片

When a thread cannot continue execution until another thread reaches a specific state, it calls wait()/wait(timeout) or the equivalents await()/await(timeout), depending on the synchronization used. The timeout parameters indicate how long the calling thread should wait before continuing the execution.
当线程无法继续执行,直到另一个线程达到特定状态时,根据所使用的同步,它会调用wait()/ wait(timeout)或等效物await()/ await(timeout)。 超时参数指示调用线程在继续执行之前应该等待多长时间。
When another thread has changed the state, it signals the change with notify()/noti fyAll() or the equivalents signal()/signalAll(). Upon a signal, the waiting thread continues execution. The calls thus support two different design patterns that use con‐ ditions: the notify() or signal() version wakes one thread, chosen at random, whereas the notifyAll() or signalAll() version wakes all threads waiting on the signal.
当线程无法继续执行,直到另一个线程达到特定状态时,根据所使用的同步,它会调用wait()/ wait(timeout)或等效物await()/ await(timeout)。 超时参数指示调用线程在继续执行之前应该等待多长时间。……

Because multiple threads could receive the signal and one could enter the critical section before the others wake, receiving the signal does not guarantee that the correct state is achieved. A waiting thread should apply a design pattern where it checks that the wanted condition is fulfilled before executing further. For example, if the shared state is pro‐ tected with synchronization on the intrinsic lock, check the condition before calling wait():
synchronized(this) {
while(isConditionFulfilled == false) {
wait();
}
// When the execution reaches this point,
// the state is correct.
}
因为多个线程可以接收信号,并且在其他线程唤醒之前可以进入临界区,所以接收信号不能保证实现正确的状态。 等待线程应该应用设计模式,在进一步执行之前检查所需条件是否被满足。 例如,如果共享状态在固有锁上保持同步,请在调用wait()之前检查条件:
synchronized(this){
while(isConditionFulfilled == false){
wait();
}
//当执行到达这一点时,
//状态是正确的。
}

This pattern checks whether the condition predicate is fulfilled. If not, the thread blocks by calling wait(). When another thread notifies on the monitor and the waiting thread wakes up, it checks again whether the condition has been fulfilled and, if not, it blocks again, waiting for a new signal.

该模式检查条件谓词是否得到满足。 如果没有,线程通过调用wait()阻塞。 当另一个线程通知监视器并且等待的线程唤醒时,它将再次检查条件是否已经满足,如果不是,则再次阻塞,等待新的信号。

A very common Android use case is to create a worker thread from the UI thread and let the worker thread produce a result to be used by some UI element, so the UI thread should wait for the result. However, the UI thread should not wait for a signal from a back‐ ground thread, as it may block the UI thread. Instead, use the An‐ droid message passing mechanism discussed later.

一个很常见的Android用例是从UI线程创建一个工作线程,让工作线程产生一个UI元素使用的结果,所以UI线程应该等待结果。 然而,UI线程不应该等待来自背景线程的信号,因为它可能会阻止UI线程。 而是使用后面讨论的安全消息传递机制。

BlockingQueue

Thread signaling is a low-level, highly configurable mechanism that can be adapted to fit many use cases, but it may also be considered as the most error-prone technique. Therefore, the Java platform builds high-level abstractions upon the thread signaling mechanism to solve one-directional handoff of arbitrary objects between threads. The abstraction is often called “solving the producer-consumer synchronization problem.” The problem consists of use cases where there can be threads producing content (pro‐ ducer threads) and threads consuming content (consumer threads). The producers hand off messages for the consumers to process. The intermediator between the threads is a queue with blocking behavior, i.e., java.util.concurrent.BlockingQueue (see Figure 4-3).
《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第4张图片

BlockingQueue的
线程信令是一种低级,高度可配置的机制,可以适应许多用例,但也可以认为是最容易出错的技术。 因此,Java平台在线程信令机制上构建高级抽象,以解决线程之间任意对象的单向切换。 抽象通常被称为“解决生产者 - 消费者同步问题”。问题在于可以使用线程生成内容(生产者线程)和线程消费内容(消费者线程)。 生产者将消息传递给消费者进行处理。 线程之间的中间件是具有阻塞行为的队列,即java.util.concurrent.BlockingQueue(见图4-3)。

The BlockingQueue acts as the coordinator between the producer and consumer threads, wrapping a list implementation together with thread signaling. The list contains a configurable number of elements that the producing threads fill with arbitrary data messages. On the other side, the consumer threads extract the messages in the order that they were enqueued and then process them. Coordination between the producers and consumers is necessary if they get out of sync, for example, if the producers hand off more messages than the consumers can handle. So BlockingQueue uses thread conditions to ensure that producers cannot enqueue new messages if the BlockingQueue list is full, and that consumers know when there are messages to fetch. Synchronization between the threads can be achieved with thread signaling, as “Example: Consumer and Producer” on page 24 shows. But the BlockingQueue both blocks threads and signals the important state changes—i.e., the list is not full and the list is not empty.

BlockingQueue充当生产者和消费者线程之间的协调器,将列表实现与线程信令相结合。该列表包含生成线程填充任意数据消息的可配置数量的元素。另一方面,消费者线程按照排入队列的顺序提取消息,然后处理它们。如果生产者和消费者失去同步,则生产者和消费者之间的协调是必要的,例如,如果生产者处理比消费者处理的消息更多的信息。因此,BlockingQueue使用线程条件来确保生产者无法在BlockingQueue列表已满时排入新消息,并且消费者知道何时有要获取的消息。可以使用线程信号来实现线程之间的同步,如第24页的“示例:消费者和生产者”所示。但是BlockingQueue阻止线程并发出重要的状态变化,即列表不满,列表不为空。

The consumer-producer pattern implemented with the LinkedBlockingQueue- implementation is easily implemented by adding messages to the queue with put(), and removing them with take(), where put() blocks the caller if the queue is full, and take() blocks the caller if the queue is empty:

使用LinkedBlockingQueue实现实现的消费者生产者模式可以通过使用put()将消息添加到队列中轻松实现,并使用take()删除它们,其中如果队列已满,put()会阻塞调用者,并且take() 如果队列为空,则阻止主叫方:

 public class ConsumerProducer {        private final int LIMIT = 10;        private BlockingQueue blockingQueue = new LinkedBlockingQueue(LIMIT);        public void produce() throws InterruptedException {            int value = 0;            while (true) {                blockingQueue.put(value++);            }        }        public void consume() throws InterruptedException {            while (true) {                int value = blockingQueue.take();            }        }    }

Android Message Passing

So far, the thread communication options discussed have been regular Java, available in any Java application. The mechanisms—pipes, shared memory, and blocking queues— apply to Android applications but impose problems for the UI thread because of their tendency to block. The UI thread responsiveness is at risk when using mechanisms with blocking behavior, because that may occasionally hang the thread.

The most common thread communication use case in Android is between the UI thread and worker threads. Hence, the Android platform defines its own message passing mechanism for communication between threads. The UI thread can offload long tasks by sending data messages to be processed on background threads. The message passing mechanism is a nonblocking consumer-producer pattern, where neither the producer thread nor the consumer thread will block during the message handoff.

The message handling mechanism is fundamental in the Android platform and the API is located in the android.os package, with a set of classes shown in Figure 4-4 that implement the functionality.

到目前为止,所讨论的线程通信选项是常规Java,可在任何Java应用程序中使用。 这些机制 - 管道,共享内存和阻塞队列 - 适用于Android应用程序,但是由于它们阻止的趋势,因此为UI线程施加问题。 使用具有阻止行为的机制时,UI线程的响应性处于危险之中,因为这可能偶尔挂起线程。

Android中最常见的线程通信用例在UI线程和工作线程之间。 因此,Android平台为线程之间的通信定义了自己的消息传递机制。 UI线程可以通过发送要在后台线程上处理的数据消息来卸载长任务。 消息传递机制是一个非阻塞的消费者生产者模式,其中生产者线程和消费者线程都不会在消息切换期间阻塞。

消息处理机制是Android平台的基础,API位于android.os包中,其中包含一组如图4-4所示的类,实现该功能。

《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第5张图片

android.os.Looper
A message dispatcher associated with the one and only consumer thread.

android.os.Handler
Consumer thread message processor, and the interface for a producer thread to insert messages into the queue. A Looper can have many associated handlers, but they all insert messages into the same queue.

android.os.MessageQueue
Unbounded linked list of messages to be processed on the consumer thread. Every Looper—and Thread—has at most one MessageQueue.

android.os.Message
Message to be executed on the consumer thread.
Messages are inserted by producer threads and processed by the consumer thread, as

illustrated in Figure 4-5.
1. Insert: The producer thread inserts messages in the queue by using the Handler
connected to the consumer thread, as shown in “Handler” on page 60.
2. Retrieve: The Looper, discussed in “Looper” on page 58, runs in the consumer thread
and retrieves messages from the queue in a sequential order.
3. Dispatch: The handlers are responsible for processing the messages on the con‐ sumer thread. A thread may have multiple Handler instances for processing mes‐ sages; the Looper ensures that messages are dispatched to the correct Handler.

《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第6张图片

android.os.Looper
与唯一的消费者线程相关联的消息分派器。

android.os.Handler
消费者线程消息处理器,以及生产者线程将消息插入队列的接口。 Looper可以有许多相关的处理程序,但是它们都将消息插入到同一个队列中。

android.os.MessageQueue
在消费者线程上处理消息的无界链接列表。每个Looper和Thread–最多有一个MessageQueue。

android.os.Message
在消费者线程上执行的消息。
消息由生产者线程插入,并由消费者线程进行处理

如图4-5所示。
1.插入:生产者线程使用处理程序将消息插入到队列中
连接到消费者线程,如第60页的“处理程序”所示。
2.检索:在第58页的“Looper”中讨论的Looper在消费者线程中运行
并按顺序从队列中检索消息。
3.调度:处理程序负责处理消费者线程上的消息。一个线程可能有多个处理器实例用于处理消息; Looper确保将消息分派到正确的Handler。

图4-5。 多个生产者线程和一个消费者线程之间的消息传递机制的概述。 每个消息都指向队列中的下一个消息,这里由左箭头指示。

Example: Basic Message Passing

Before we dissect the components in detail, let’s look at a fundamental message passing example to get us acquainted with the code setup.
The following code implements what is probably one of the most common use cases. The user presses a button on the screen that could trigger a long operation, such as a network operation. To avoid stalling the rendering of the UI, the long operation, rep‐ resented here by a dummy doLongRunningOperation() method, has to be executed on a worker thread. Hence, the setup is merely one producer thread (the UI thread) and one consumer thread (LooperThread).
Our code sets up a message queue. It handles the button click as usual in the on Click() callback, which executes on the UI thread. In our implementation, the callback inserts a dummy message into the message queue. For sake of brevity, layouts and UI components have been left out of the example code:

public class LooperActivity extends Activity {    LooperThread mLooperThread;    private static class LooperThread extends Thread {        public Handler mHandler;        public void run() {            Looper.prepare();            mHandler = new Handler() {                public void handleMessage(Message msg) {                    if (msg.what == 0) {                       doLongRunningOperation();                    }                }            };            Looper.loop();        }    }    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mLooperThread = new LooperThread();        mLooperThread.start();    }    public void onClick(View v) {        if (mLooperThread.mHandler != null) {            Message msg = mLooperThread.mHandler.obtainMessage(0);            mLooperThread.mHandler.sendMessage(msg);        }    }    private void doLongRunningOperation() {         // Add long running operation here.    }    @Override    protected void onDestroy() {        super.onDestroy();        mLooperThread.mHandler.getLooper().quit();    }}
package com.jiang.memorydemo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;/** * Created by jiang on 2017/6/1. */public class LooperActivity extends Activity {    LooperThread mLooperThread;    //1.Definition of the worker thread, acting as a consumer of the message queue.    private static class LooperThread extends Thread {        public Handler mHandler;        public void run() {            //2.Associate a Looper—and implicitly a MessageQueue—with the thread.            Looper.prepare();            /*            3.Set up a Handler to be used by the producer for inserting messages in the queue.            Here we use the default constructor so it will bind to the Looper of the current thread.            Hence, this Handler can created only after Looper.prepare(),            or it will have nothing to bind to.             */            mHandler = new Handler() {                /*                4.Callback that runs when the message has been dispatched to the worker thread.                It checks the what parameter and then executes the long operation.                 */                public void handleMessage(Message msg) {                    if (msg.what == 0) {                       doLongRunningOperation();                    }                }            };            /*                5.Start dispatching messages from the message queue to the consumer thread.                This is a blocking call, so the worker thread will not finish.             */            Looper.loop();        }    }    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //6.Start the worker thread, so that it is ready to process messages.        mLooperThread = new LooperThread();        mLooperThread.start();    }    public void onClick(View v) {        /*          7.There is race condition between the setup of mHandler on a background thread            and this usage on the UI thread. Hence, validate that mHandler is available.         */        if (mLooperThread.mHandler != null) {            //8.Initialize a Message-object with the what argument arbitrarily set to 0.            Message msg = mLooperThread.mHandler.obtainMessage(0);            //9.Insert the message in the queue.            mLooperThread.mHandler.sendMessage(msg);        }    }    private void doLongRunningOperation() { // Add long running operation here.    }    @Override    protected void onDestroy() {        super.onDestroy();        /*        10.Terminate the background thread.         The call to Looper.quit() stops the dispatching of messages and releases Looper.loop()         from blocking so the run method can finish, leading to the termination of the thread.         */        mLooperThread.mHandler.getLooper().quit();    }}

1.Definition of the worker thread, acting as a consumer of the message queue.

2.Associate a Looper—and implicitly a MessageQueue—with the thread.

3.Set up a Handler to be used by the producer for inserting messages in the queue. Here we use the default constructor so it will bind to the Looper of the current thread. Hence, this Handler can created only after Looper.prepare(), or it will have nothing to bind to.

4.Callback that runs when the message has been dispatched to the worker thread. It checks the what parameter and then executes the long operation.

5.Start dispatching messages from the message queue to the consumer thread. This is a blocking call, so the worker thread will not finish.

6.Start the worker thread, so that it is ready to process messages.

7.There is race condition between the setup of mHandler on a background thread and this usage on the UI thread. Hence, validate that mHandler is available.

  1. Initialize a Message-object with the what argument arbitrarily set to 0.

  2. Insert the message in the queue

10.Terminate the background thread. The call to Looper.quit() stops the dispatching of messages and releases Looper.loop() from blocking so the run method can finish, leading to the termination of the thread.

1.定义工作线程,充当消息队列的消费者。

2.与线程相关联,并隐式地使用MessageQueue。

3.设置一个处理程序,以供生产者用于在队列中插入消息。这里我们使用默认构造函数,以便它绑定到当前线程的Looper。因此,此处理程序只能在Looper.prepare()之后创建,否则它将无法绑定到。

4.将消息分派到工作线程时运行的回调。它检查什么参数,然后执行长操作。

5.从消息队列开始向消费者线程发送消息。这是一个阻塞调用,所以工作线程不会完成。

6.启动工作线程,以便它可以处理消息。

背景线程上的mHandler设置和UI线程上的这种用法之间有竞争条件。因此,验证mHandler是否可用。

8.使用任意设置为0的参数初始化消息对象。

9.将消息插入队列

10.终止后台线程。对Looper.quit()的调用会阻止消息的发送,并释放Looper.loop()从阻止,所以run方法可以完成,导致线程的终止。

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).

《Effieicntt Android Threading》 Chapter4 -- Thread Communication(1)_第7张图片

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

未完待续(2)

更多相关文章

  1. Android消息循环的同步屏障机制及UI渲染性能的提升(Android Q)
  2. Android使用Sensor感应器实现线程中刷新UI创建android测力计的功
  3. Android引入广播机制的用意。单线程模型Message、Handler、Messa
  4. UWP与Android中如何在多线程中刷新UI
  5. android中异步消息的处理机制
  6. Android BroadcastReceiver(广播)实现消息发送
  7. android的线程封装
  8. Android 跨线程更新 UI
  9. android将线程绑定在指定CPU

随机推荐

  1. Python主讲移动端自动化测试框架Appium
  2. 【大云制造】为云而生 - 大云BEK内核
  3. 【干货分享】Kubernetes容器网络之CNI漫
  4. 【干货分享】Linux操作系统自动化测试平
  5. 知道 Redis-Cluster 么?说说其中可能不可
  6. 【干货分享】硬件加速介绍及Cyborg项目代
  7. PHP数组常用函数
  8. 【干货分享】虚拟机热迁移性能优化
  9. 全面演示: 函数的作用域与闭包,回调的使
  10. 国际域名争议,仲裁机构VS法院,谁说的算?