Android 常见的多线程设计
Android端的开发多线程并发编程其实并不是很复杂,因为大多数业务都比较简单,都是从服务器拉去数据,在UI上面进行渲染显示,再加上好多网络库内部都封装好了多线程的处理,我们直接使用就好了,但它的内部实现也比较简单 (线程池 + 回调) ; 但也有比较复杂的多线程并发编程,比如,一个播放器的开发设计它的内部就是多线程并发编程的真实体现;
下面就从两个方面介绍Android 常见的多线程设计:
-
Android 上层业务常见的多线程设计
-
一个播放器的多线程设计
一. Android 上层业务常见的多线程设计
Android 在 mainThread 不能处理耗时的任务,只能更新UI. 所以当我们需要处理耗时的任务的时候都需要创建线程,我们常用的几种方式:
-
采用Thread的方式
当我们需要在后台处理一些耗时的单个任务且并不需要更新UI时,通过Thread
方式实现;比如日志的上报 -
Thread + Handler
Handler 负责向Thread 发送消息; Thread负责在线程中处理消息;Android SDK 封装了一个HandlerThread轻量级异步类;这种设计完全把消息的发送和消息的处理完全解藕;有些项目模块比如OpenGL渲染模块经常采用自定义Thread + Handler处理; Thread 创建的时候先进行EGL 环境的初始化,然后通过Handler发送消息控制OpenGL 的渲染等逻辑,非常好用的多线程设计模式,屡试不爽;后面有时间会讲解Thread + Handler 设计的巧妙和实用性;
-
Thread + Callable + FutureTask
先举个栗子:我到一家西服店定制一款西服,西服制作过程大概需要2天的时间,这个时候西服店往往会给你开个凭据,两天后让你拿着这个凭据来去西服;
上面的栗子就是一个典型的Future多线程开发模式,这里Future就是凭据的意思,通过Future 拿到我们期望的结果,如果这时“西服”还没有做好,我们甚至可以取消;
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "西服";
}
};
Future<String> future = new FutureTask<>(callable);
new Thread((FutureTask)future).start();
//两天时间到了,拿西服
String name = future.get();
//如果西服还没有做好,可以取消
future.cancel(false);
-
ExecutorService + Callable + FutureTask
随着西服店生意越来越好,西服店老本为了节约成本与让顾客等待的时间进行平衡, 又招了3个员工做西服,那这时其实就是多线程开发里面的线程池原理;通过线程池对线程的缓存,避免系统不断的启动和关闭新线程,降低消耗系统资源;
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "西服";
}
};
ExecutorService executorService = Executors.newFixedThreadPool(10);
Future<String> submitFuture = executorService.submit(callable);
String name = submitFuture.get();
submitFuture.cancel(false);
-
AsyncTask
AsyncTask是安卓开发中使用的一种轻量级异步任务类。其作用是在线程池中执行后台任务,并在执行过程中将执行进度传递给主线程,当任务执行完毕后,将最终结果传递给主线程。
它的内部实现可以分为三部分:
-
SerialExecutor + ThreadPoolExecutor
SerialExecutor: 负责任务的调度,当一个Task执行完成之后再执行下一个任
ThreadPoolExecutor: 负责任务的执行 -
WorkerRunnable + FutureTask
WorkerRunnable就是一个Callable,他负责任务执行的内容及获取返回值
FutureTask让外界可以获取任务执行之后的结果及取消任务; -
InternalHandler
负责给主线程发送消息
二. 一个播放器的多线程设计
要实现一个播放器其实是很复杂的,这里就只是简单介绍下哪里需要线程去实现以及线程之间通信;
1. 视频播放器的原理图:
2.播放器的多线程设计
- 通过MediaExtractor或者AVInputFormat 获取音视频的封装格式数据到一个音视频队列中(一个线程)
- 通过MediaCodec或者AVCodec进行音视频解码(两个线程),数据格式从FFmpeg角度分析AVPacket → AVFrame;
- 视频数据通过OpenGL 渲染处理(一个线程)
- 音频数据通过AudioTrack或OpenSSL播放;(一个线程)
通过上面的分析要5个线程同步处理,这五个线程之间的通信就是通过同步锁+信号机制+队列实现;
三. 总结
Android 上层系统为方便我们开发自动提供了好多 多线程并发开发的类,好多情况下在我们开发中直接使用就可以了,但有时可能我们的项目设计的比较复杂,SDK提供的类可能满足不了我们的需求,这时候就要靠我们自己积累的多线程并发开发的基础知识自己去实现一套适合自己的多线程架构;特别在涉及到音视频的时候,这时候可能更多的处理通过JNI 技术C/C++语言实现的,我们还要熟悉C/C++多线程的开发;它们的原理与Java实现都是一样的,只是语言的实现不同而已;