有一篇关于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. 关于CentOS系统中,文件权限第11位上是一个点的解读

    http://blog.csdn.net/dashuai03091199/article/details/38920833 http://blog.csdn.net/xinlongabc/articl ...

  2. Woody的Python学习笔记3

    Python运算符 Python逻辑运算符 and布尔与-假设x为false.x and y返回false,否则它返回y的计算值. or 布尔或-假设x是true,它返回true.否则它返回y的计算值 ...

  3. 一句css代码让网站变灰

    <style> html{ -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms-filter: grays ...

  4. Java中分页功能源码实例

    一.源码(后附使用说明) package com.zhiyou100.crm.util; /** * 分页功能 * @author YangXianSheng * */ public class Pa ...

  5. poj3666 Making the grade【线性dp】

    Making the Grade Time Limit: 1000MS   Memory Limit: 65536K Total Submissions:10187   Accepted: 4724 ...

  6. java面试基础题------》抽象类和接口有什么异同

    划重点!!!! 1.抽象类(abstract class)和接口(interface)有什么异同? 相同点 * 都不能被直接实例化,都可以通过继承实现其抽象方法. * 都是面向抽象编程的技术基础,实现 ...

  7. Random/Stochastic

    ---恢复内容开始--- ===================================================== A random variable's possible valu ...

  8. php判断密码强度函数

    其实就是一些策略正则,写好了就留下来以后用. print_r(getPasswordStrength('s1212adsddfASD;\'g;\'gh.h,h..;')); function getP ...

  9. 关闭在chrome里使用双指前进后退页面的功能

    defaults write com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool FALSE

  10. python学习笔记(二十一)构造函数和析构函数

    python中的特殊方法,其中两个,构造函数和析构函数的作用: 比说“__init__”这个构造函数,具有初始化的作用,也就是当该类被实例化的时候就会执行该函数.那么我们就可以把要先初始化的属性放到这 ...