Android笔记(六):线程及线程通信

线程

由于Android的Activity中默认所有代码都在主线程(UI线程)中执行,如果在这里面执行耗时任务(例如下载),界面就会无反应且不可操作,直到耗时任务执行完毕。

如果想在执行耗时任务的同时又想让界面不会没有反应,就需要新开一个线程(Thread)。系统会在UI线程和新开的线程之间不断切换,由于切换速度极快且可以操作界面,就会给人一种没有在执行耗时任务的感觉。

JAVA中的线程

在JAVA中,有两个跟线程关系最紧密的类或接口:

  • Runnable接口:只有一个抽象方法run()。这是线程实际执行的方法,应该实现这个方法并把要在线程中执行的代码放到这里。

  • Tread类:实现了Runnable接口。通过执行start()来开启线程,并在新的线程中执行run()的代码。

    能否直接执行Tread的run()方法呢?答案是不行。如果直接在Thead执行run(),它仍然会在当前线程(例如UI线程)里执行run()的代码,而不是在新的线程中运行。

以下是线程的一种写法:

new Thread(new Runnable() {
    @Override
    public void run() {
        // 在线程中运行的代码
    }
}).start();

Android中的线程

任务(task):执行一个特定操作的一个Runnable对象或者Runnable对象集。

在Android中,可以使用上面介绍的方法开启线程。不过那样的写法会使得一旦线程的任务结束,不会再次运行这个线程(也就是一次性线程)。

当你想为不同数据集而重复执行某个任务时,可以使用IntentService。不过要注意,同一段时间只处理一个数据集。

如果上面两者都不符合你的要求,那么可以试试 ThreadPoolExecutor ,它可以实现以下功能:

  • 当资源准备好时自动执行任务
  • 多个任务同时执行

当 ThreadPoolExecutor 的线程池中有一个线程为空闲时, ThreadPoolExecutor 会从一个队列(queue)中取出一个任务来执行。

线程间数据交换

因为新开的线程和处理界面的线程是分开的,于是在Android中使用线程会遇到一个问题:线程处理完的数据如何更新到界面上?

JAVA

你可以实现 BlockingQueue 接口。将其实例传入线程中,在线程里面使用put(Object)将数据放入队列,使用take()从队列中取出数据。

以下是官方的例子:

 class Producer implements Runnable {
   private final BlockingQueue queue;
   Producer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { queue.put(produce()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   Object produce() { ... }
 }

 class Consumer implements Runnable {
   private final BlockingQueue queue;
   Consumer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { consume(queue.take()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   void consume(Object x) { ... }
 }

 class Setup {
   void main() {
     BlockingQueue q = new SomeQueueImplementation();
     Producer p = new Producer(q);
     Consumer c1 = new Consumer(q);
     Consumer c2 = new Consumer(q);
     new Thread(p).start();
     new Thread(c1).start();
     new Thread(c2).start();
   }
 }

Android

Android添加了几个类,用来处理数据交换。

  • Handler : 线程之间交换数据的通道,用于接收和发送消息。如果在UI线程里创建Handler,则该Handler里的handleMessage(Message)方法会在UI线程里执行。
  • Message : ​线程A发送消息给线程B时,需要将消息封装到Message里面。通过在线程A内部执行线程B的Handler.sendMessage(Message)将Message传给线程B,此时会执行Handler的handleMessage(Message)方法。
  • MessageQueue : 当发送多个Message时,为了不造成混乱,将这些Message组成一个队列,逐个处理。每个线程中只能有一个MessageQueue。这个队列由Looper管理。
  • Looper : 管理MessageQueue。每次从队列里面取出一个Message,交给Handler处理。Handler处理完毕后再去队列中取出Message。

这些类太多了,有时候写起来麻烦。为了简化线程的写法,Android将上面那些封装起来,于是就有了AsyncTask。

Android对线程类的封装

AsyncTask有四个重要的方法:

  • onPreExecute() :(UI线程)后台任务执行前在UI线程上做某些初始化操作
  • doInBackground(Params ...) :(子线程)相当于Runnable的run()方法。可以在这个方法内计算进度,并调用publishProgress(Progress ...)传递给onProgressUpdate(Progress ...)方法
  • onProgressUpdate(Progress ...) : ​(UI线程)用于更新界面上的进度信息
  • onPostExecute(Result) :(UI线程)用于子线程处理完毕后对结果进行处理

AsyncTask是一个抽象类,需要创建一个类去继承它。AsyncTask有三个泛型参数,它们用途依次为:

  • ​执行任务所需要的参数
  • 当前进度的单位
  • 任务的结果

执行AsyncTask的execute()方法以开始任务。

posted @ 2017-02-24 23:19  schaepher  阅读(396)  评论(6编辑  收藏  举报