写点什么

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

用户头像
Android架构
关注
发布于: 2021 年 11 月 05 日

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


//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<Runnable> sPoolWorkQueue =


new LinkedBlockingQueue<Runnable>(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<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {


@Override


public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {


emitter.onNext(1);


}


});


observable.subscribeOn(Schedulers.io()) //表示在 io 线程执行订阅


.observeOn(AndroidSchedulers.mainThread()) //表示在主线程接收订阅


.subscribe(new Observer<Integer>() {


@Override


public void onSubscribe(Disposable d) {


//接收订阅之前调用


}


@Override


public void onNext(Integer integer) {


//接收订阅成功调用


doSomething();


}


@Override


public void onError(Throwable e) {


//接收订阅出错调用


}


@Over


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ride


public void onComplete() {


//接收订阅完成调用


}


});


}


emmmmm 看起来好像还是挺复杂的啊,能不能再整简单点?


OK,链式调用加 lambda 安排上


private void fun() {


Observable.create(emitter -> emitter.onNext(1))


.subscribeOn(Schedulers.io())


.observeOn(AndroidSchedulers.mainThread())


.subscribe(integer -> {


//接收订阅成功


doSomething();


}, throwable -> {});


}


嗯…有内味了。


这串代码我们是发送了一个 Integer 类型的数据;


subscribeOn()指定了我们发送的线程是在后台的 io 线程,就可以理解为一个子线程;


observeOn 指定了我们接收的线程为主线程;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android高工:细说 Android 多线程,探究原理知其所以然