android线程学习心得
有一篇关于android线程讲的非常好,大家可以参考下,其中有一句话讲的非常好,就拿来做开篇之句:
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理,所以主线程通常又被叫做UI线程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
咱新手在第一次接触android线程的背景是这样的:
1:需求中要进行一次http交互
步骤大概是这样
1.1:点击提交按钮
1.2:http同步获取结果
1.3:将结果写入到TextView上。
这样写,执行时会报错,不能在UI线程上发起同步的http网络请求。(耗时可能会导致ANR,application not respond)
2:OK,既然不能在UI线程上发起http请求,那咱新开一个子线程。
于是步骤变成了这样:
1.1:点击提交按钮
1.2:开启一个子线程
1.3:在子线程中,http同步获取结果
1.4:在子线程中,将结果写入到TextView上。
一执行,BOOM,报错,大概意思是子线程里不能直接操作UI元素,为什么呢?请再看看本文开头的句子(线程安全)。
3:好吧,那就在子线程里通过Handler来操作UI的元素吧
最后,测试通过的代码大概是这样
1.1:点击提交按钮
1.2:开启一个子线程
1.3:在子线程中,http同步获取结果
1.4:在子线程中,将结果作为Message,传递给Handler
1.5:在Handler中,将结果写入到TextView上。
tips:如果你用android studio 的code inspect功能,就会发现,它提示你,这样的做法可能会导致内存泄露,为什么呢?因为Handler里持有了UI里面的元素的引用,当UI结束掉自己时(此时handler还在耐心等待http访问结果,生命周期比前者长),发现某个元素被Handler持有,那个元素就不能被GC回收了,这就会造成内存泄露。解决办法很简单,Handler改为static,消除内部匿名引用,同时,将对象的引用改为WeakReference<>即可。一篇详细解释原因的文章,具体代码参考如下,来源于咱的通讯录APP
static class ImageDoneHandler extends Handler {
WeakReference<ImageView> imageView;
WeakReference<Bitmap> bitmap;
WeakReference<String> url;
ZImage.CacheType cacheType;
ImageDoneHandler(Looper looper, ImageView _imageView, Bitmap _bitmap, String url, ZImage.CacheType cacheType) {
super(looper);
imageView = new WeakReference<>(_imageView);
bitmap = new WeakReference<>(_bitmap);
this.url = new WeakReference<>(url);
this.cacheType = cacheType;
}
@Override
public void handleMessage(Message msg) {
if (msg.what != MSG_IMAGE_LOAD_DONE)
return;
ImageView _imageView = imageView.get();
Bitmap _bitmap = bitmap.get();
String _url = url.get();
if (_imageView == null || _bitmap == null)
return;
if (_url.equals(_imageView.getTag().toString())) {
_imageView.setImageBitmap(_bitmap);
if (cacheType == ZImage.CacheType.DiskMemory)
ZImage.getInstance().putToMemoryCache(_url, _bitmap);
}
}
}
写到现在,咱还是不懂,为啥Handler里面就可以改UI里面的元素呢?
这时候就需要理解android异步消息处理的四大部分了( Message、 Handler、 MessageQueue 和Looper)。
咱才疏学浅,因此下面的知识来源于《第一行代码》书籍的节选片段,非常的精彩,值得反复阅读,大家深呼吸下,系好安全带,开始咯~
先来一张异步消息处理的整个流程图解,大家对照着图解看更直观(图片来源《第一行代码》)

1. Message
Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线
程之间交换数据。上一小节中我们使用到了 Message 的 what 字段,除此之外还可以使
用 arg1 和 arg2 字段来携带一些整型数据,使用 obj 字段携带一个 Object 对象。
2. Handler
Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消
息一般是使用 Handler 的 sendMessage()方法,而发出的消息经过一系列地辗转处理后,
最终会传递到 Handler 的 handleMessage()方法中。
3. MessageQueue
MessageQueue 是消息队列的意思,它主要用于存放所有通过 Handler 发送的消息。
这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个 MessageQueue
对象。
4. Looper
Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 loop()方法后,就会
进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将它取
出,并传递到 Handler 的 handleMessage()方法中。每个线程中也只会有一个 Looper 对象。
了解了 Message、 Handler、 MessageQueue 以及 Looper 的基本概念后我们再来对异步消息处理的整个流程梳理一遍。
1:首先需要在主线程当中创建一个 Handler 对象,并重写handleMessage()方法。
2:然后当子线程中需要进行 UI 操作时,就创建一个 Message 对象,并通过 Handler 将这条消息发送出去。
3:之后这条消息会被添加到 MessageQueue 的队列中等待被处理,
4:而 Looper 则会一直尝试从 MessageQueue 中取出待处理消息,最后分发回 Handler的 handleMessage()方法中。
5:由于 Handler 是在主线程中创建的,所以此时 handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行 UI 操作了。
AsyncTask异步任务类
幸运的是,在一些大部分的场合,android为我们提供了一个AsyncTask异步任务抽象类,通过实现他可以非常方便的执行各种耗时操作,而不必担心UI线程被卡住,同时也避免了原生异步线程与UI线程交互繁琐的写法。
在继承时,我们可以指定三个泛型参数类型(都是引用类型哦,值类型的记得也要改成引用类型,比如int ->Integer),它们分别是:
1:参数Param ,传递给子线程执行的
2:进度提示Progress,如果需要实时在界面更新异步处理进度,就可以通过这个参数反馈
3:结果Result,在主线程里,我们就获取到了异步执行的结果。
来个例子吧
/**
* http请求用户是否存在,穿入http的url地址,返回布尔类型是否存在
*/
class QueryUserExistTask extends AsyncTask<String,Void,Boolean>
{ /**
* 在异步请求处理之前
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
} /**
* 异步处理,这里同样不能交互UI元素哦
* @param params
* @return
*/
@Override
protected Boolean doInBackground(String... params) {
return null;
} /**
* 异步处理完了,切回到主线程,返回处理结果
* @param aBoolean
*/
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
}
}
调用例子:
new QueryUserExistTask().execute("http://192.168.1.1/u/kimmy");
总结:
1:保持主线程流畅度很重要,耗费大量资源的工作尽量放到子线程完成。
2:大部分情况下AsyncTask都能胜任异步的重任。
3:高并发的异步任务、或者异步任务之间彼此需要调度的情况,需要自己编写线程池来处理
android线程学习心得的更多相关文章
- android 线程学习
很多人觉得线程难理解,主要有两个问题: 线程休眠,既然线程已经休眠了,程序的运行速度还能提高吗? 线程体一般都进行死循环,既然线程死循环,程序就应该死掉了,就会没有反应. 1.关于线程休眠问题 对线程 ...
- Android 的学习心得
https://www.jianshu.com/p/f93a6c75940c 一个2年安卓开发者的一些经验分享
- Linux学习心得之 Linux下命令行Android开发环境的搭建
作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Linux学习心得之 Linux下命令行Android开发环境的搭建 1. 前言2. Jav ...
- Android(java)学习笔记267:Android线程池形态
1. 线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...
- Android(java)学习笔记211:Android线程池形态
1. 线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...
- Android学习心得(13) --- Android代码混淆(1)
我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下最新的ADT环境下怎样进行Android混淆 在新版本号的ADT创建项目时.混码的文件不再是proguard.cf ...
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
- 我的MYSQL学习心得(六) 函数
我的MYSQL学习心得(六) 函数 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...
- 我的MYSQL学习心得(十六) 优化
我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
随机推荐
- WPF进阶之接口(2):IDisposable,ICollectionView
废话不多说,进入正题,先来说说IDisposable,看例子(来自MSDN): using System; using System.ComponentModel; // 下面的例子将展示一个实施了I ...
- Maven clean命令不能执行问题Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:2.5:clean (default-clean) on project
执行clean tomcat7:run时Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:2.5:clean (de ...
- 面试题思考:Java RMI与RPC,JMS的比较
RPC:(Remote Procedure Call) 被设计为在应用程序间通信的平台中立的方式,它不理会操作系统之间以及语言之间的差异. 支持多语言 RMI:(Remote Method Invo ...
- SDL 威胁建模工具入门 threat modeling tool
http://msdn.microsoft.com/zh-cn/magazine/dd347831.aspx threat modeling tool 威胁建模工具 minifuzz 文件模糊工具 c ...
- 【BZOJ4636】蒟蒻的数列 STL
[BZOJ4636]蒟蒻的数列 Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个 ...
- js 跨域 之 修改服务器配置-XAMPP-Apache (nginx 拉到最后!)
js高程第21章提到了ajax 跨域技术,方法有很多,如图: 我主要讲这个: 其实代码就是这样就好了,当然只兼容 IE9 及之后的版本 ,IE9 之前的版本请去原书看吧,Page 600 var xh ...
- Python全栈day21(调用模块路径BASEDIR的正确方法)
正常写python程序会有一个可执行的bin.py文件,假如这个文件需要导入my_module里面定义的模块,应该怎么设置sys.path 文件夹目录结构如下,因为bin不在与my_module同级目 ...
- Windows MFC 打开文本
MFC的CFileDialog自动封装了文件相关的对话框,提供一种简单的文件打开和文件存盘对话框功能. 要使用CFileDialog类,首先要构造一个对象, 项目实例: CFileDialog fil ...
- 2017-2018-2 20165330 实验三《敏捷开发与XP实现》实验报告
实验内容 P基础 XP核心实践 相关工具 实验步骤 (一)敏捷开发与XP 软件开发:即将软件需求分析.软件设计.软件构建.软件测试和软件维护这些相关技术和过程统一到一个体系中 敏捷开发:是一种以人为核 ...
- Android Activity 去掉标题栏及全屏显示
默认生成的活动(Activity)界面中包含标题栏,并带有状态栏.有时不需要这两个控件. 1.去掉标题栏 (三种方法) a:在setContentView()方法前 添加:requestWindowF ...