线程

由于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()方法以开始任务。

Android笔记(六):线程及线程通信的更多相关文章

  1. Android笔记(六十二)网络框架volley

    什么是Volley 很多时候,我们的APP都需要用到网络技术,使用HTTP协议来发送接收数据,谷歌推出了一个网络框架——volley,该框架适合进行数据量不大,但通信频繁的网络操作. 它的优点: (1 ...

  2. Android笔记(六十九) 仿微信界面(一)

          综合之前的Fragment和自定义组件的知识,实现微信界面 MainActivity.java package cn.lixyz.test; import android.app.Acti ...

  3. Android笔记(六十八) Fragment总结

    Fragment的产生: 为了适应各种尺寸的屏幕,谷歌推出Fragment,可以把Fragment成Activity的一个组成部分,它拥有自己的生命周期.可以接收并处理用户的各种事件,还可以动态的增删 ...

  4. Android笔记(六十七) 自定义控件

    实际编程中,系统提供的控件往往无法满足我们的需求,一来是样子丑陋,二来是一些复杂的组合需要多次使用的话,每次都写一堆控件的组合会很耗费时间,所以我们将这些组件的组合自定义为一个新的控件,以后使用的时候 ...

  5. Android笔记(六十六) android中的动画——XML文件定义属性动画

    除了直接在java代码中定义动画之外,还可以使用xml文件定义动画,以便重用. 如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在 ...

  6. Android笔记(六十五) android中的动画——属性动画(propertyanimation)

    补间动画只能定义起始和结束两个帧在“透明度”.“旋转”.“倾斜”.“位移”4个方面的变化,逐帧动画也只能是播放多个图片,无法满足我们日常复杂的动画需求,所以谷歌在3.0开始,推出了属性动画(prope ...

  7. Android笔记(六十四) android中的动画——补间动画(tweened animation)

    补间动画就是只需要定义动画开始和结束的位置,动画中间的变化由系统去补齐. 补间动画由一下四种方式: 1.AplhaAnimation——透明度动画效果 2.ScaleAnimation ——缩放动画效 ...

  8. Android笔记(六十三) android中的动画——逐帧动画( frame-by-frame animation)

    就好像演电影一样,播放实现准备好的图片,来实现动画效果. 逐帧动画需要用到AnimationDrawable类,该类主要用于创建一个逐帧动画,然后我们把这个动画设置为view的背景即可. androi ...

  9. Android笔记(三十四) Android中线程之间的通信(六)Handle中的post()方法详解

    我们之前都是使用sendMessage()方法来发送消息,使用handleMessage来处理消息的,今天我们来看另外一种方法,先看代码: package cn.lixyz.handlertest; ...

  10. Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息

    先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...

随机推荐

  1. 005_关于HTTP协议中的保持连接

    缘起 中午在群里讨论,用ab测试 一台只提供静态文件服务, 不与其他任何系统交互的时候,为什么也会产生大量的TIME WAIT状态的. 首先,我们可以简单的理解,在TCP连接的两端,谁主动断开连接(先 ...

  2. centos6.5环境基于corosync+cman+rgmanager实现RHCS及iscsi+gfs2+clvm的文件系统集群

    centos6.5环境基于corosync+cman+rgmanager实现RHCS及iscsi+gfs2+clvm文件系统集群 一.环境准备 服务器列表: ansible server : 192. ...

  3. 了解的CAP和BASE等理论

    CAP,BASE和最终一致性是NoSQL数据库存在的三大基石.而五分钟法则是内存数据存储的理论依据.这个是一切的源头. 几个名词解释: 网络分区:俗称“脑裂”.当网络发生异常情况,导致分布式系统中部分 ...

  4. Vue项目之背景图片打包后路径错误

    第一种方法: 原因: 首先,出错点在url-loader上面. // url-loader配置 // build/webpck.base.conf.js { test: /\.(png|jpe?g|g ...

  5. ANN算法总结

    kd-tree kd-tree works poorly in high dimensions (k<30) 自己实验的时候差不多20到30左右吧,超过之后,就真的很慢了 faiss suppo ...

  6. pytest十:用例 a 失败,跳过测试用例 b 和 c 并标记失败 xfail

    当用例 a 失败的时候,如果用例 b 和用例 c 都是依赖于第一个用例的结果,那可以直接跳过用例 b 和 c 的测试,直接给他标记失败 xfail用到的场景,登录是第一个用例,登录之后的操作 b 是第 ...

  7. POJ 2385 Apple Catching【DP】

    题意:2棵苹果树在T分钟内每分钟随机由某一棵苹果树掉下一个苹果,奶牛站在树#1下等着吃苹果,它最多愿意移动W次,问它最多能吃到几个苹果.思路:不妨按时间来思考,一给定时刻i,转移次数已知为j, 则它只 ...

  8. [转]Kindle 推送教程:教你用电子邮箱推送电子书

    Kindle 推送是什么意思?如何通过电子邮件附件推送?或许刚刚接触 Kindle 的朋友对这个概念不是很清楚,其实所谓 Kindle 推送是指亚马逊提供的一个“Kindle 个人文档服务”,我们只需 ...

  9. python全栈开发day20-类的三大特性继承、多态、封装

    1 继承 1.怎么继承,父类和子类 class 类名(父类):pass 除了__init__下对象属性不能自动继承外,其他的类属性和动态方法,子类对象都可以访问到. 2.子类对象查找属性的顺序,对象现 ...

  10. java.lang.NumberFormatException: multiple points问题

    一般这种问题主要是因为SimpleDateFormat在多线程环境下,是线程不安全的,所以如果你在多线程环境中共享了SimpleDateFormat的实例,比如你在类似日期类中定义了一个全局的Simp ...