0.为啥要学异步通信?

先来看个场景:
用户想同时做两件事,比如一边用网易云听歌,一边看网易云音乐的评论,总不能为了看个评论把歌曲停下来吧?

用多线程的话就很容易解决这个问题,再开一条子线程,然后主线程做自己的事情,什么时候子线程吧事情做完了,再告诉主线程,主线程再把结果拿回来就行了。

这个场景涉及了两个问题
1)怎么实现多线程?
2)怎么线程间的通信?

1.怎么实现多线程

有五种方式,分别是
1)继承Thread类
2)实现Runnable接口
3)Handler
4)AsyncTask
5)HandlerThread
下面来说说每种方式的优缺点,以便在实际中选择合适的方式去实现。

继承Thread类

为啥用它?

最早在java开发中就涉及到要多线程开发的场景,因此给开发者提供了一个线程的工具类Thread类,来方便开发者方便创建多个线程,实现多线程。

怎么使用?

创建一个新的类继承thread类,重写run方法。

好用吗?

优点:光看使用方法的话,实现起来很简单。
缺点:有三点。
1)java是单继承,继承thread类以后就不能继承其他类了。
2)thread线程就是一次性消耗品,执行完一个线程会被自动销毁,每个耗时任务都得重复地创建喝销毁,很耗费系统资源。
3)相对独立,无法进行资源共享。

实现Runnable接口

为啥用它?

弥补第一种方法的不足,于是java提供了实现多线程的方法。

怎么用?

创建一个新的类实现Runnable接口,重写run方法。

好用吗?

优点:
1)一个类可以继承多个接口,因此相较于继承thread类的方式,避免了单继承的局限性。
2)适合多个线程处理同一资源。

线程池

为什么用线程池?

传统的多线程方式(Thread,Runnable)会重复创建销毁线程,浪费系统资源。
于是就出现了线程池这种方式,他有两个优点:
1)重用缓存在线程池中的线程,避免重复创建销毁;这样变相提高执行效率。
2)可以统一管理线程,优化线程的执行顺序,从而避免线程间互相抢占系统资源导致阻塞。

怎么用

分三步:创建线程池–向线程池提交任务–关闭线程池。

创建线程池

线程池分为好几种,不同的线程池有不同的应用场景。常见的线程池有四种:
定长线程池(FixedThreadPool)
定时线程池(ScheduledThreadPool )
可缓存线程池(CachedThreadPool)
单线程化线程池(SingleThreadExecutor)。

定长线程池

为什么用这种线程池?

如果线程很多的话,会导致并发问题(多个线程同时读写同一资源),所以这种线程池固定了线程数量,方便控制线程最大并发数。

定时线程池

为什么用这种线程池?

有些线程需要定时启动,或者周期性地启动,那就需要用到这种线程池了。

可缓存线程池

为什么用这种线程池?

当线程任务比较多,且这些线程耗时比较少,这个适合就可以创建这种线程池了。

单线程化线程池

为什么用这种线程池?

多个线程读写同一资源就会产生同步问题,如果把所有子线程放在一个核心里执行,那所有子线程就都同步执行,就不会抢夺同一资源,因此产生了单线程化线程池。

这种线程池只有一个核心线程,保证所有任务按照顺序在一个线程中执行。

2.线程间的通信

实现方法有二:Handler和AsyncTask

2.1Handler

2.1.1为啥用Handler?

前两种方法都是在java开发中使用的实现多线程的方法,但在安卓开发中,为了UI操作的线程安全,只允许UI线程更新UI组件。但子线程也想操作UI组件怎么办?同时要保证线程安全。
于是产生了UI线程和子线程之间的中间商Handler,当子线程想更新UI线程,直接告诉Handler,然后Handler告诉UI线程,主线程再去更新UI。

2.1.2Handler是怎么工作的?

首先得先了解跟Handler打交道的角色。

跟Handler打交道的角色:

1)主线程和子线程,这个前面提到过了。
2)消息(Message),线程间通信的数据。
3)消息队列(Message Queen),用来存放消息的一种数据结构(先进先出)
4)循环器(Looper)消息队列和Handler的通信媒介。

工作流程

分四步。

第一步 准备异步通信

主要是创建Handler;
以及和Handler两个一起工作的家伙:消息队列和循环器,对了,这俩货都属于主线程(UI线程)。

第二步 发送消息

子线程通过Handler发送一条消息到消息队列中。

第三步 消息循环

主线程的循环器会定时看看消息队列里有没有来新消息,来了新消息就取出来,然后发给Handler。

第四步 消息处理

Handler收到循环器的消息以后,就开始根据消息进行UI操作

2.1.3 Handler怎么用?

有两种方式:使用Handler.sendMessage()和使用Handler.post()

2.1.3.1使用Handler.sendMessage

这种使用方式又有两种实现方式(差不多,第二种方式比较简洁点):新建Handler子类和匿名Handler子类。

新建Handler子类

1)新建一个继承Handler的子类,重写handleMessage方法,在方法中根据不同线程发过来的消息,执行不同的UI操作。
2)在主线程创建Handler的实例
3)在子线程中创建所需要的消息对象
4)在子线程通过handler对象发送消息到消息队列中
5)启动子线程

匿名内部类

1)在主线程通过匿名内部类创建Handler的实例,重写handleMessage方法,在方法中根据不同线程发过来的消息,执行不同的UI操作。
2)在子线程中创建所需要的消息对象
3)在子线程通过handler对象发送消息到消息队列中
4)启动子线程

2.1.3.2 使用使用Handler.post

1)在主线程创建Handler的实例,此时不需要重写方法;
2)在子线程中调用Handler对象的post方法,该昂发需要传入一个Runnable对象,实例化一个Runnable对象,重写run方法,在run方法中写要操作的UI内容。
3)开启子线程;

2.1.3.3 这两种使用方式有啥区别?

handler.post和handler.sendMessage本质上是没有区别的。
在源码中可以发现,post这个方法是把任务Runnable转成一个message放进了handler所在的线程中的messageQueue消息队列中,而且消息队列和handler都是依赖于同一个线程的。post只是一种更方便的用法而已。

2.2AsyncTask

2.2.1为啥用它

本质原理还是(继承承Thread类、实现Runnable接口、Handler)的组合实现,是安卓为了方便开发者使用而封装好的一个类。
有两个优点:
1)与Handler相比,可以更简单地实现异步通信。
2)由于它内部采用了线程池,因此避免了频繁创建线程带来的系统开销。

2.2.2 怎么用

1)创建一个实现AsyncTask 的子类 & 根据需求重写相应的核心方法:
onPreExecute用来执行线程任务前的操作;
doInBackground必须重写的方法,用来自定义线程任务;
onProgressUpdate可以在主线程显示线程执行的进度;
onPostExecute也是必须重写的方法,接受线程任务结束的结果,执行自定义的UI操作;
onCancelled,将异步任务设置为取消状态。

2)创建子类对象;
3)手动调用execute方法开始执行异步任务。