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 ...
随机推荐
- windows下配置mysql数据库监视工具Mysqlreport
该工具除了可以监控本机Mysql数据库外,也可以监控远程服务器mysql数据库 需要的工具: 1:perl脚本解析工具安装: http://www.activestate.com/activeperl ...
- PYTHON-绑定方法 反射 内置函数
'''绑定方法类中定义函数分为了两大类: 1. 绑定方法 特殊之处: 绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数自动传入 如何用: 绑定给对象的方法: 在类中定义函数没有被任何装饰器修饰 ...
- LeetCode(45): 跳跃游戏 II
Hard! 题目描述: 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃次数到达数组的最后一个位置. 示例: 输入: [ ...
- ROS----TUT-RIM协作机器人和Actin-ROS接口
TUT-RIM: Collaborative Intelligent Heavy Machinery and Robotics http://www.ros.org/news/2017/02/tutr ...
- 使用siege执行压力测试
没有安装siege? 可参考我的另一篇博客 使用siege执行压力测试笔记 场景分析 使用siege对https://www.baidu.com/进行加压. 要求 模拟20个用户同时访问 一共跑3个循 ...
- python 全栈开发,Day10(动态参数,命名空间,作用域,函数嵌套)
一.动态参数 def func(a,b,c,d,e,f,g): pass func(1,2,3,4,5,6,7) 如果加30个参数呢?有没有万能的参数,可以代表一切参数呢? *args 动态参数,万能 ...
- docker安装sonarqube及实际应用
由于平台的多样化,在不同环境的安装方式可能也不一样,为了避免环境不一致带来的差异,特记一笔容器安装: 一.Sonar可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前5种代码质量问题. 1. ...
- SqlServer基础语法(三)
1.数据库备份的方法: 完整数据库备份GPOSDB 文件大小:23MB 日志备份 GPOSDB日志备份文件大小:211KB --完整备份 Backup DATABASE GPOSDB To disk= ...
- 【Leetcode】404. Sum of Left Leaves
404. Sum of Left Leaves [题目]中文版 英文版 /** * Definition for a binary tree node. * struct TreeNode { * ...
- Java Timer, TimerTask, Timer.Schedule
schedule的意思(时间表.进度表) timer.schedule(new TimerTask(){ void run()},0, 60*60*1000);timer.schedule(new M ...