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()方法以开始任务。
Android笔记(六):线程及线程通信的更多相关文章
- Android笔记(六十二)网络框架volley
什么是Volley 很多时候,我们的APP都需要用到网络技术,使用HTTP协议来发送接收数据,谷歌推出了一个网络框架——volley,该框架适合进行数据量不大,但通信频繁的网络操作. 它的优点: (1 ...
- Android笔记(六十九) 仿微信界面(一)
综合之前的Fragment和自定义组件的知识,实现微信界面 MainActivity.java package cn.lixyz.test; import android.app.Acti ...
- Android笔记(六十八) Fragment总结
Fragment的产生: 为了适应各种尺寸的屏幕,谷歌推出Fragment,可以把Fragment成Activity的一个组成部分,它拥有自己的生命周期.可以接收并处理用户的各种事件,还可以动态的增删 ...
- Android笔记(六十七) 自定义控件
实际编程中,系统提供的控件往往无法满足我们的需求,一来是样子丑陋,二来是一些复杂的组合需要多次使用的话,每次都写一堆控件的组合会很耗费时间,所以我们将这些组件的组合自定义为一个新的控件,以后使用的时候 ...
- Android笔记(六十六) android中的动画——XML文件定义属性动画
除了直接在java代码中定义动画之外,还可以使用xml文件定义动画,以便重用. 如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在 ...
- Android笔记(六十五) android中的动画——属性动画(propertyanimation)
补间动画只能定义起始和结束两个帧在“透明度”.“旋转”.“倾斜”.“位移”4个方面的变化,逐帧动画也只能是播放多个图片,无法满足我们日常复杂的动画需求,所以谷歌在3.0开始,推出了属性动画(prope ...
- Android笔记(六十四) android中的动画——补间动画(tweened animation)
补间动画就是只需要定义动画开始和结束的位置,动画中间的变化由系统去补齐. 补间动画由一下四种方式: 1.AplhaAnimation——透明度动画效果 2.ScaleAnimation ——缩放动画效 ...
- Android笔记(六十三) android中的动画——逐帧动画( frame-by-frame animation)
就好像演电影一样,播放实现准备好的图片,来实现动画效果. 逐帧动画需要用到AnimationDrawable类,该类主要用于创建一个逐帧动画,然后我们把这个动画设置为view的背景即可. androi ...
- Android笔记(三十四) Android中线程之间的通信(六)Handle中的post()方法详解
我们之前都是使用sendMessage()方法来发送消息,使用handleMessage来处理消息的,今天我们来看另外一种方法,先看代码: package cn.lixyz.handlertest; ...
- Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息
先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...
随机推荐
- mongodb数据库安装及常见操作
客户端和服务端的安装 # rpm -ivh mongo-10gen-2.4.6-mongodb_1.x86_64.rpm mongo-10gen-server-2.4.6-mongodb_1.x86_ ...
- Android数据存储:SDCard
Android数据存储之SDCard 0.获取sd卡路径. 1.讲述 Environment 类. 2.讲述 StatFs 类. 3.完整例子读取 SDCard 内存 0.获取sd卡路径 方法一: p ...
- Spark学习之概念了解
Spark简介: Spark是一个快速且通用的集群计算模型: 1.Spark是快速的:快速是指处理几T到几批数据量的时候,他的处理时间是几秒钟或几分钟,相对于hadoop的几分钟到几小时是非常快速的, ...
- 使用ueditor的时候,style样式传递到后台时被过滤没了
在项目中,使用ueditor的时候,style样式传递到后台时被过滤没了 转:https://www.cnblogs.com/theroad/p/5761743.html 经过chrome的一番调试后 ...
- 配置本地无密码 SSH登录远程服务器
下面这幅图简单来说就是你本地有一把钥匙,服务器也有一把钥匙,当登录的时候本地的钥匙与服务器的进行对比,通过算法的判定,监测是否具有权限的用户 第一步,在本地配置这把钥匙生成私钥与公钥: 打开.ssh目 ...
- 【linux】centos6.9安装gearman
1.确认yum源没问题,如果有问题,参照这里更换 2. yum install -y boost-devel gperf libevent-devel libuuid-devel yum instal ...
- ubuntu git hub 建立仓库
https://www.cnblogs.com/woider/p/6533709.html 1.安装git apt-get install git 2.配置 Git 用户信息 把用户名和邮箱换成你自己 ...
- hdu5672 尺取
因为没有初始化ans搞了一晚上 还是尺取,枚举所有l 然后寻找对应满足条件的r,这个串可以被后面所有的串包含,所以每个l 的贡献就是len-r+1 #include<bits/stdc++.h& ...
- MyBatis之Hello world(Mybatis入门)
MyBatis中文网,超详细的:http://www.mybatis.org/mybatis-3/zh/index.html MyBatis英文网:http://www.mybatis.org/myb ...
- 「BZOJ2882」工艺
题解: 就是个最小表示法 大概做法就是扩大2倍原串 然后双指针比较,如果不相同了直接跳 原理随便画画就知道了