优化一下?
我们可以没必要继承整个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 线程池的结构和原理
一个完整的线程池应该有这么几个组成部分
-
核心线程
-
任务队列
-
非核心线程
当我们通过线程池执行异步任务的时候,其实是依次进行了下面的流程
-
检查核心线程数是否到达最大值,否则创建新的核心线程执行任务,是则进行下一步
-
检查任务队列是否已满,否则将任务添加到任务队列中,是则进行下一步
-
检查非核心线程数是否到达最大值,否则创建新的非核心线程执行任务,是则说明这个线程池已经饱和了,执行饱和策略。默认的饱和策略是抛出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是一种响应式编程,大家不是很明白的话可以粗暴的理解为更优雅的多线程实现即可。
- 2.6.2 那怎么操作RxJava?
先手搓一个RxJava的普通实现方式
private void fun(){
Observable observable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
最后
感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?
Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。
最后,赠与大家一句诗,共勉!
不驰于空想,不骛于虚声。不忘初心,方得始终。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
[外链图片转存中…(img-s87Q8ktq-1714474659348)]
以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。
最后,赠与大家一句诗,共勉!
不驰于空想,不骛于虚声。不忘初心,方得始终。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!