文章目录
前言
这篇笔记是笔者关于Android线程和线程池作用的学习记录
一、线程池概念
线程池: 简单理解,它就是一个管理线程的池子。
- 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗。因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。
- 提高响应速度。 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
- 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。
好处
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
线程池的创建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
参数含义
- corePoolSize:线程池中的核心线程数
- maximumPoolSize:线程池中允许的最大线程数
- keepAliveTime:线程空闲时的存活时间(只在线程数大于corePoolSize时才有用)
- unit:keepAliveTime的时间单位
- workQueue:阻塞队列.当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待
- threadFactory:创建线程的工厂(线程的命名规则是“pool-数字-thread-数字”)
- handler:线程池的饱和策略.
工作机制
- 当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;
- 如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
- 如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize.
- 当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务
RejectedExecutionHandler(饱和策略)
二、Android线程形态
除了传统的Thread以外,Android还有AsyncTask、HandlerThread和IntentService等形态
1.AsyncTask
轻量级的异步任务,主要在线程池中执行任务,并将执行的进度和最终结果传递给主线程用于更新UI
从实现上来说,AsyncTask就是对Thread和Handler进行了一层封装,但是AsyncTask并不适合特别耗时的任务,特别耗时的任务,建议使用线程池
特点
- 轻量型异步类
- 使用时需要实现其子类
作用
- 实现多线程
- 异步通信 消息传递
- 在Android 11中被标记为过时,官方推荐使用Kotlin的协程解决相关问题
缺点
- 容易导致内存泄漏
- 忘记回调
- 横竖屏切换导致崩溃
- 不同版本的AsyncTask的兼容问题
生命周期
注意事项
1.生命周期
- AsyncTask不与任何组件绑定生命周期
解决方案: - 在Activity 或 Fragment中使用 AsyncTask时,最好在Activity 或 Fragment的onDestory()调用 cancel(boolean);//
2.内存泄漏
- 若AsyncTask被声明为Activity的非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
解决方案: - AsyncTask应被声明为Activity的静态内部类
3.屏幕旋转
当Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
使用建议
在Activity恢复时的对应方法 重启 任务线程
具体使用
具体使用过程如下
- 创建 AsyncTask 子类 & 根据需求实现核心方法
- 创建 AsyncTask子类的实例对象(即 任务实例)
- 手动调用execute(()从而执行异步线程任务
class MyAsyncTask extends AsyncTask<String,Integer,String>{
@Override
protected void onPreExecute() {
//数据初始化操作
super.onPreExecute();
}
@Override
protected String doInBackground(String... strings) {
//耗时操作,并将结果返回
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
//对进度条进行更新操作
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String s) {
//UI更新操作
super.onPostExecute(s);
}
}
- onPreExecute():我们在进行初始化数据的时候调用这个方法,这个方法是在主线程执行。
- doInBackground():我们在进行耗时操作的时候调用这个方法,所有的耗时操作都在这个里面进行操作。并且将耗时操作的结果返回回去。
- onProgressUpdata():对控件的进度进行操作。
- onPostExecute():这个方法里面会拿到doInBackground()方法中返回的参数结果,我们在这个方法里面可以对UI进行操作,从而达到更新UI的结果。
源码分析
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//要执行的任务,是对Callable接口的封装。可以获取到返回值
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
//AsyncTask要执行的任务分返回结果
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
执行流程如下:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
先回在主线程调用onPreExecute()方法。 将mFuture传递给了AsyncTask的执行器进行执行。AsyncTask的执行器缺省是sDefaultExecutor。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
@UnsupportedAppUsage
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
//双端队列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
每次调用execute,就创建一个Runnable匿名内部类对象,这个对象存入mTasks,在匿名内部类的run函数里面调用传入参数r.run()。然后通过一个scheduleNext函数把mTasks里面的所有对象通过THREAD_POOL_EXECUTOR.execute(mActive)执行一遍。
SerialExecutor类会把所有的任务丢入一个容器,之后把容器里面的所有对象一个一个的排队(串行化)执行THREAD_POOL_EXECUTOR.execute(mActive);
处理任务的线程池
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
}
SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
以前用的是LinkedBlockingQueue(最大容量是128).
结果和进度的通知
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
进入消息处理:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT://结果
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS://进度
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
小结
在创建了AsyncTask的时候,会默认创建两个线程池SerialExecutor和ThreadPoolExecutor,SerialExecutor负责将任务串行化,ThreadPoolExecutor是真正执行任务的地方,且无论有多少个AsyncTask实例,两个线程池都会只有一份。
在execute中,会执行run方法,当执行完run方法后,会调用scheduleNext()不断的从双端队列中轮询,获取下一个任务并继续放到一个子线程中执行,直到异步任务执行完毕。
在执行完onPreExecute()方法之后,执行了doInBackground()方法,然后就不断的发送请求获取数据;在这个AsyncTask中维护了一个InternalHandler的类,这个类是继承Handler的,获取的数据是通过handler进行处理和发送的。在其handleMessage方法中,将消息传递给onProgressUpdate()进行进度的更新,也就可以将结果发送到主线程中,进行界面的更新了。
通过观察代码我们可以发现,每一个new出的AsyncTask只能执行一次execute()方法,多次运行将会报错,如需多次,需要新new一个AsyncTask(参见如下代码)
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
2.HandlerThread
HanderThread其实就是一个内部包含了Looper的线程,说白了就是多线程+Handler的简化版
我们看看其Run方法便知道了
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
特点
- HandlerThread本质上是一个Thread对象,只不过其内部帮我们创建了该线程的Looper和MessageQueue;
作用
-
通过HandlerThread我们不但可以实现UI线程与子线程的通信同样也可以实现子线程与子线程之间的通信;
-
HandlerThread在不需要使用的时候需要手动的回收掉;
具体使用
/**
* 测试HandlerThread的基本使用
*/
HandlerThread mHandlerThread = new HandlerThread("myHandlerThreand");
mHandlerThread.start();
// 创建的Handler将会在mHandlerThread线程中执行
final Handler mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
Log.i("tag", "接收到消息:" + msg.obj.toString());
}
};
title = (TextView) findViewById(R.id.title);
title.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = new Message();
msg.obj = "11111";
mHandler.sendMessage(msg);
msg = new Message();
msg.obj = "2222";
mHandler.sendMessage(msg);
}
});
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
}
3.IntentService
IntentService 是Service 的子类,它使用工作线程逐一处理所有启动请求,如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandIntent方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。
IntentService可以执行后台耗时的任务,并且因为它是服务,优先级比单纯的线程要高很多,所以IntentService比较适合一些高优先级的后台服务
IntentService扩展类样例
public class MyIntentService extends IntentService {
public static final String TAG ="MyIntentService";
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
// 这里已经是工作线程,在这里执行操作就行
boolean isMainThread = Thread.currentThread() == Looper.getMainLooper().getThread();
Log.i(TAG,"is main thread:"+isMainThread);
// 执行耗时下载操作
mockDownload();
}
/**
* 模拟执行下载
*/
private void mockDownload(){
try {
Thread.sleep(5000);
Log.i(TAG,"下载完成...");
}catch (Exception e){
e.printStackTrace();
}
}
}
源码分析
IntentService自动为我们开启一个线程来进行耗时操作,并且在操作完成后自动停止服务,其源码如下
// 1,有一个Looper 变量和一个ServiceHandler 变量,ServiceHander 继承Handler 处理消息
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 在工作线程中调用onHandleIntent,子类根据Intent传递的数据执行具体的操作
onHandleIntent((Intent)msg.obj);
// 任务执行完毕后,自动停止Service
stopSelf(msg.arg1);
}
}
//2, 在OnCreate 方法中,创建了一个线程HandlerThread ,并启动线程
// 然后获取工作线程的Looper ,并用Looper 初始化Handler(我们都知道Handler 的创建需要一依赖Looper)
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
//3, 在onStart()方法中发送消息给Handler,并且把Intent 传给了Handler 处理
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
// 4,onStartCommand 直接调用的是onStart方法
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
// 5 最后就是一个子类需要实现的抽象方法,这个方法在handleMessage中调用,也就是在工作线程中执行。
protected abstract void onHandleIntent(@Nullable Intent intent);
需要注意的是,在IntentService第一次被启动时,它的OnCreate()方法会被调用,而后启动IntentService(),会调用它的onStartCommand()方法
总结
IntentService是Service 的子类,默认给我们开启了一个工作线程执行耗时任务,并且执行完任务后自 动停止服务。扩展IntentService比较简单,提供一个构造方法和实现onHandleIntent 方法就可了,不用重写父类的其他方法。但是如果要绑定服务的话,还是要重写onBind 返回一个IBinder 的。使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。
参考文章
Android Service和IntentService知识点详细总结
线程池和AsyncTask的源码分析