Looper和Handler在Android的UI程序设计中用得很多。到底它们能做什么呢?其实,用它们能构成一个常用的并发模型,这里称之为PipelineThread。我们对该模型的特征作以下描述。
举例比喻PipelineThread的一种工作场景:一个流水线作业的模型中,传送带的前端有一个工人Handler负责将其他线程的Task和Msg搬运上传送带(PipelineThread)。传送带另一端的工人Loope负责将作业一个一个地依次行处理(Run)。有时,也安排一个传话的人站在旁边溜达,当PipelineThread需要向其他线程发送消息时就告诉工人Listener。
其实我们的UI主线程就是一个PipelineThread的典型应用。正因为它具备该模型的多任务顺序处理的特点,我们才不用去担心改变按钮文字和用户点击按钮这两个事件同时发生该怎么办。除此之外,该模型还常常用在以下情况。
接下来我们就举例说明PipelineThread如何应用在文件下载中。
【代码要点】
我们将DownloadThread设计成PipelineThread。DownloadThread继承自Thread,Looper将普通的Thread变成PipelineThread。先看看在run()函数里面的实现。
DownloadThread.java片段:
@Override public void run() { try { // preparing a looper on current thread // the current thread is being detected implicitly Looper.prepare(); Log.i(TAG, "DownloadThread entering the loop"); // now, the handler will automatically bind to the // Looper that is attached to the current thread // You don't need to specify the Looper explicitly handler = new Handler(); // After the following line the thread will start // running the message loop and will not normally // exit the loop unless a problem happens or you // quit() the looper (see below) Looper.loop(); Log.i(TAG, "DownloadThread exiting gracefully"); } catch (Throwable t) { Log.e(TAG, "DownloadThread halted due to an error", t); } }
其他线程通过他线程通过handler.post将处理(Task)发送ipelineThread线程的的消息队列中。DownloadThread类中公开了这样两个方法。
DownloadThread.java片段:
// This method is allowed to be called from any thread public synchronized void requestStop() { // using the handler, post a Runnable that will quit() // the Looper attached to our DownloadThread // obviously, all previously queued tasks will be executed // before the loop gets the quit Runnable handler.post(new Runnable() { @Override public void run() { // This is guaranteed to run on the DownloadThread // so we can use myLooper() to get its looper Log.i(TAG, "DownloadThread loop quitting by request"); Looper.myLooper().quit(); } }); } public synchronized void enqueueDownload(final DownloadTask task) { // Wrap DownloadTask into another Runnable to track the statistics handler.post(new Runnable() { @Override public void run() { try { task.run(); } finally { // register task completion synchronized (DownloadThread.this) { totalCompleted++; } // tell the listener something has happened signalUpdate(); } } }); totalQueued++; // tell the listeners the queue is now longer signalUpdate(); } public synchronized int getTotalQueued() { return totalQueued; } public synchronized int getTotalCompleted() { return totalCompleted; } // Please note! This method will normally be called from the download thread. // Thus, it is up for the listener to deal with that (in case it is a UI component, // it has to execute the signal handling code in the UI thread using Handler - see // DownloadQueueActivity for example). private void signalUpdate() { if (listener != null) { listener.handleDownloadThreadUpdate(); } }