zoukankan      html  css  js  c++  java
  • 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()方法以开始任务。

  • 相关阅读:
    LeetCode剑指Offer03
    腾讯软件开发客户端开发实习生二面
    luogu P2801 教主的魔法 分块
    luogu P3396 哈希冲突 根号算法
    luogu P1972 [SDOI2009]HH的项链 树状数组
    BZOJ 2440: [中山市选2011]完全平方数 莫比乌斯函数 容斥原理 二分答案
    柳阴直,烟里丝丝弄碧
    卡通别名
    它们
    高中随笔
  • 原文地址:https://www.cnblogs.com/schaepher/p/6440687.html
Copyright © 2011-2022 走看看