有一篇关于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线程学习心得的更多相关文章

  1. android 线程学习

    很多人觉得线程难理解,主要有两个问题: 线程休眠,既然线程已经休眠了,程序的运行速度还能提高吗? 线程体一般都进行死循环,既然线程死循环,程序就应该死掉了,就会没有反应. 1.关于线程休眠问题 对线程 ...

  2. Android 的学习心得

    https://www.jianshu.com/p/f93a6c75940c    一个2年安卓开发者的一些经验分享

  3. Linux学习心得之 Linux下命令行Android开发环境的搭建

    作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Linux学习心得之 Linux下命令行Android开发环境的搭建 1. 前言2. Jav ...

  4. Android(java)学习笔记267:Android线程池形态

    1. 线程池简介  多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.     假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...

  5. Android(java)学习笔记211:Android线程池形态

    1. 线程池简介  多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.     假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...

  6. Android学习心得(13) --- Android代码混淆(1)

    我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下最新的ADT环境下怎样进行Android混淆 在新版本号的ADT创建项目时.混码的文件不再是proguard.cf ...

  7. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

  8. 我的MYSQL学习心得(六) 函数

    我的MYSQL学习心得(六) 函数 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

  9. 我的MYSQL学习心得(十六) 优化

    我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

随机推荐

  1. Ubuntu 16.04 LTS 安装libvips出现”Package vips was not found in the pkg-config search path”

    使用libvips来操作图像,libvips的部署参考一个Node.js工程:https://github.com/lovell/sharp 在MAC下安装很顺利,到Linux环境下(Ubuntu 1 ...

  2. 修改tomcat配置通过域名直接访问项目首页

    1.在自己项目的web.xml中配置欢迎页面 <welcome-file-list> <welcome-file>index.html</welcome-file> ...

  3. C语言switch语句

    C语言虽然没有限制 if else 能够处理的分支数量,但当分支过多时,用 if else 处理会不太方便,而且容易出现 if else 配对出错的情况.例如,输入一个整数,输出该整数对应的星期几的英 ...

  4. mybatis 控制台打印出来的sql 执行结果为空 但是将sql放到mysql执行有数据

    mybatis中的sql如下 select airln_Cd airlnCd,city_coordinate_j cityCoordinateJ,city_coordinate_w cityCoord ...

  5. SQL ALTER TABLE 命令

    SQL ALTER TABLE 命令 SQL ALTER TABLE 命令用于添加.删除或者更改现有数据表中的列. 你还可以用 ALTER TABLE 命令来添加或者删除现有数据表上的约束. 语法: ...

  6. [NOIP2017]宝藏 状压DP

    [NOIP2017]宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖 ...

  7. 【BZOJ3195】[Jxoi2012]奇怪的道路 状压DP

    [BZOJ3195][Jxoi2012]奇怪的道路 Description 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外.考古学家已经知道,这个文明在全盛时期有n座 ...

  8. anguglar 2+ md5加密

    1.安装 npm install ts-md5 --save 2.使用 import {Md5} from "ts-md5/dist/md5"; constructor() {co ...

  9. 2017 Multi-University Training Contest - Team 3——HDU6063 RXD and math

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6063 题目意思:字面意思,给出n,k,算出这个式子的答案. 思路:比赛的时候是打表找规律过了,赛后仔细 ...

  10. git学习(4)远程库和分支管理

    git学习(4)远程库和分支管理 1.1建立本地git库和远程库联系 我使用的是GitHub上的库,首先在GitHub上新建一个库,在建立与远程库的联系之前需要建立ssh key.建立ssh key可 ...