Android高工:细说 Android 多线程,探究原理知其所以然

本文详细介绍了Android应用中多线程的重要性,包括为何需要多线程、如何使用Thread类、Runnable接口、线程池、Handler、AsyncTask和IntentService等机制,以及线程池的原理和优化。还提及了RxJava的简洁性。旨在帮助开发者理解和掌握Android多线程的最佳实践。
摘要由CSDN通过智能技术生成

我告诉你,可以,都可以。

单线程的APP只包括Android的UI线程也是能运行的;一个APP多个进程也是可以达到的,实现方式涉及到Android的IPC机制,这里不细说。

1.2 为什么要使用多线程?

这里杠精可能会说,那你单线程也能跑,我为啥还要整多线程?

我告诉你,首先这句话从Android开发的角度来讲,近似于一个假命题。因为谷歌爸爸现在强制规定了不能在UI线程进行耗时操作,必须放到子线程里面去,除非你的程序不涉及耗时操作。究其原因,是因为在UI线程进行耗时操作的话,给用户的使用体验就是界面“卡顿”。同时,如果UI线程被阻塞超过一定时间会触发ANR(Application Not Responding)错误。

从底层的角度来讲,多线程可以使得整个环境能够异步执行,这有助于防止浪费CPU时钟周期从而提高效率。换言之,多线程能更充分的利用CPU资源,从而提高程序的运行效率。

二、那我怎么进行多线程编程?

==========================================================================

2.1 Thread类和Runnable接口 要想定义一个线程只需要新建一个类继承自Thread,然后重写父类的run方法即可

class MyThread extends Thread {

@Override

public void run() {

doSomething();

}

}

//在需要的时候启动线程

new MyThread().start();

优化一下?

我们可以没必要继承整个Thread类,只实现Runnable接口就好了

class MyThread implements Runnable {

@Override

public void run() {

doSomething()

}

}

//启动线程

MyThread myThread = new MyThread();

new Thread(myThread).start();

那我不想专门再写一个线程类怎么办?可以使用匿名类

new Thread(new Runnable() {

@Override

public void run() {

doSomething();

}

}).start();

2.2 线程池

  • 2.2.1 线程池的意义

既然我都会用Runnable接口来创建线程了,还要线程池干啥?其实不然,随意创建线程的操作在实际开发中是极为不推荐的。为啥?因为线程也是一种资源,反复的创建和销毁线程会带来一定性能上的额外开销。与其相比,线程池主要有以下几个优点:

  • 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销

  • 能有效控制线程池的最大并发数,避免大量的线程之间因相互抢占系统资源而导致的阻塞现象

  • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能

  • 2.2.2 线程池的结构和原理

一个完整的线程池应该有这么几个组成部分

  • 核心线程

  • 任务队列

  • 非核心线程

当我们通过线程池执行异步任务的时候,其实是依次进行了下面的流程

  1. 检查核心线程数是否到达最大值,否则创建新的核心线程执行任务,是则进行下一步

  2. 检查任务队列是否已满,否则将任务添加到任务队列中,是则进行下一步

  3. 检查非核心线程数是否到达最大值,否则创建新的非核心线程执行任务,是则说明这个线程池已经饱和了,执行饱和策略。默认的饱和策略是抛出RejectedExecutionException异常

下面手搓一个线程池的实现

//CPU核心数

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

//核心线程数

private static final int CORE_POOL_SIZE = CPU_COUNT + 1;

//最大线程数

private static final int MAX_POOL_SIZE = CPU_COUNT * 2 + 1;

//非核心线程闲置的超时时间

private static final int KEEP_ALIVE_TIME = 1;

//任务队列

private static final BlockingQueue sPoolWorkQueue =

new LinkedBlockingQueue(128);

//线程池

private ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,

MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, sPoolWorkQueue);

private void fun(){

Runnable runnable = new Runnable() {

@Override

public void run() {

//子线程处理耗时操作

doSomething();

}

};

poolExecutor.execute(runnable);

}

这样我们就实现了一个简单的线程池,核心线程数为CPU数量+1,非核心线程数为CPU数量*2+1,非核心线程的闲置时间为1秒,任务队列的大小为128。

线程池还有具体的好几种分类和相应不同的实现方式,这里不再细说。

2.3 Handler

有朋友可能会说,你讲的这些都是Java多线程里面的东西,能不能整点咱Android特有的?OK,现在进入专业时间。

Handler是Android提供的一种异步消息处理机制,要学会使用Handler我们首先来了解下消息处理四兄弟:

  • Message

  • Handler

  • MessageQueue

  • Looper

Handler可以帮助我们实现在不同的线程之间传递消息,这里的Message就是消息本体,也就是我们想要传递的那个东西。

Handler在这里扮演的角色是消息处理者,它的主要作用是发送和处理消息。

MessageQueue是一个消息队列,Handler发送过来的消息会放在这个队列里面,每个线程只会有一个MessageQueue对象。

Looper是线程中消息队列的管家,它会无限循环运行,每发现MessageQueue中存在一条消息,它就会把消息取出然后发送给Handler。每一个线程也只能有一个Looper对象。

好了,基本原理已经了解,现在我们来反手搓一个Handler

private static final int FLAG = 1;

private Handler mHandler = new Handler(){

@Override

public void handleMessage(@NonNull Message msg) {

if (FLAG == msg.what){

//这里已经回到主线程了

doSomething();

}

}

};

private void fun(){

new Thread(new Runnable() {

@Override

public void run() {

//子线程发送消息

Message message = new Message();

message.what = FLAG;

mHandler.sendMessage(message);

}

}).start();

}

2.4 AsyncTask

除了Handler以外,谷歌爸爸还给我们提供AsyncTask来进行线程的切换。AsyncTask是一种轻量级的异步任务,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程。从实现原理上来讲,AsyncTask是对Thread和Handle的再次封装。

AsyncTask本身是一个抽象的泛型类,有四个亲儿子:

  • onPreExecute()

  • doInBackground(Params…params)

  • onProgressUpdate(Progress…values)

  • onPostExecute(Result result)

最先执行的是方法是onPreExecute()方法,位于主线程中,一般用来做一些准备工作。

然后执行doInBackground()方法,位于线程池中,用来执行异步任务,params表示异步任务的输入参数。这个方法需要返回结果给onPostExecute()方法。

onProgressUpdate()方法在主线程中执行,当后台任务的执行进度发生变化时这个方法会被调用。

onPostExecute()方法在最后异步任务完成之后会被调用,位于主线程中,result参数是后台任务的返回值,即doInBackground()的返回值。

OK,基本原理已经了解了,现在我们来手搓一个AsyncTask

class DownloadTask extends AsyncTask<Void,Integer,Boolean> {

@Override

protected void onPreExecute() {

//这里我们使用了一个显示进度的Dialog,具体实现不表

progressDialog.show();

}

@Override

protected Boolean doInBackground(Void… voids) {

try {

while (true){

//调用我们的doDownload下载方法,具体实现不表

int downloadPercent = doDownload();

//使用publishProgress方法来更新执行的进度

publishProgress(downloadPercent);

if (downloadPercent >= 100)

break;

}

} catch (Exception e) {

e.printStackTrace();

}

return true;

}

@Override

protected void onProgressUpdate(Integer… values) {

//更新下载进度

progressDialog.setMessage(“Download “+values[0]+”%”);

}

@Override

protected void onPostExecute(Boolean aBoolean) {

//下载完成

progressDialog.dismiss();

}

}

这里我们创建了一个Download类继承自AsyncTask,有三个泛型,void表示不需要给后台任务传入参数,Integer表示用整数类型来作为进度显示的单位,Boolean表示用布尔类型来反馈后台任务的执行结果。

要让我们的这个AsyncTask跑起来也很简单,只需要执行:

new DownloadTask().execute();

2.5 IntentService

IntentService是一种特殊的Service,它继承了Service并且是一个抽象类,我们可以创建它的子类来使用。IntentService也可以用于执行后台的耗时任务,并且当任务执行完毕之后它会自动停止。

IntentService因为是服务的原因,所以和单纯的线程相比它的优先级要高很多,从而更不容易被系统杀死。

IntentService的内部实现是封装了HandlerThread和Handler,使用的话要遵循Service的使用方法,这里先略过后面有机会在Service的专栏里面再详细介绍。

2.6 RxJava

有杠精可能会说,你讲的这些方法,一个比一个长,一个比一个复杂,就不能整个简单又粗暴的东西?

这个时候就需要祭出神兵利器RxJava了。

  • 2.6.1 RxJava又是个啥?

其实网络上RxJava的入门文章多如过江之鲫,这里不打算过多的深入介绍。RxJava是一种响应式编程,大家不是很明白的话可以粗暴的理解为更优雅的多线程实现即可。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

[外链图片转存中…(img-ElOA6VI5-1712603873724)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-G84UtXsq-1712603873725)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值