Android 的线程(AsyncTaskHandlerThreadIntentService详解)和线程池
Posted Young_xiaoT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 的线程(AsyncTaskHandlerThreadIntentService详解)和线程池相关的知识,希望对你有一定的参考价值。
android 的线程和线程池
在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都有一定的开销。
当系统中存在大量的线程时,系统会通过时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并发,除非线程数小于等于CUP的核心数,一般来说这是不可能的。如果在一个进程中频繁的创建和销毁线程,这显然不是高效的做法。正确的做法是采用线程池,一个线程池中会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。Android中的线程池来源于Java,主要是通过Executor来派生特定类型的线程池,不同种类的线程池又具有各自的特性。
主线程和子线程
主线程是指进程所拥有的线程,在Java中默认情况下一个进程只有一个线程,这个线程就是主线程。
子线程也叫工作线程,除了主线程以为的线程都是子线程。
Android 中的线程形态
除了传统的 Thread 以外,还包括 AsyncTask、HandlerThread 以及 IntentService,这三者底层实现也是线程。
AsyncTask
AsyncTask 是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终的结果传递给主线程并在主线程中更新UI。
从实现上来说,AsyncTask封装了 Thread 和 Handler,通过 AsyncTask 可以更加方便的执行后台任务以及在主线程中访问UI,但是不适合执行特别耗时的后台任务,对于特别耗时的任务来说,还是使用线程池比较好。
AsyncTask 是一个抽象的泛型类,它提供了 Params、Progerss 和 Result 这三个泛型参数,其中 Params 表示参数的类型,Progress 表示后台任务执行进度的类型,而 Result 则表示后台任务的返回结果的类型,如果不需要传递具体的参数,那么这三个泛型参数可以使用 Void 来代替:
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask 提供了4个核心方法,它们的含义如下:
- onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。 - doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成 。 - onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。 - onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
除了上述四个方法,AsyncTask 还提供了 onCancelled()方法,它同样在主线程执行,当异步任务被取消时,onCancelled() 方法会被调用,这个时候 onPostExecute则不会被调用。
AsyncTask 的工作原理
从它的 execute 方法开始分析,execute 方法又会调用 executeOnExecutor 方法:
public final AsyncTask<Params, Progress, Result> execute(Params... params)
return executeOnExecutor(sDefaultExecutor, params);
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;
在上面的代码中,sDefaultExecutor 实际上是一个串行的线程池,一个进程中的所有的 AsyncTask 全部在这个串行的线程池中排队执行。
在 executeOnExecutor 中,AsyncTask 的 onPreExecute() 方法最先被执行,然后线程池开始执行。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
protected synchronized void scheduleNext()
if ((mActive = mTasks.poll()) != null)
THREAD_POOL_EXECUTOR.execute(mActive);
首先系统会把 AsyncTask 的 Params 参数封装为 FutureTask 对象,FutureTask 是一个并发类,在这里它充当了 Runnable 的作用。接着这个FutureTask会交给 SerialExecutor 的 execute 方法去处理,SerialExecutor 的 execute 方法首先会把 FutureTask 对象插入到任务队列 mTask中,如果这个时候没有正在活动的 AsyncTask 任务,那么就会调用SerialExecutor的scheduleNext方法来执行下一个AsyncTask任务。
同时当一个 AsyncTask 任务执行完后,AsyncTask 会继续执行其他任务直到所有的任务都被执行为止,从这一点可以看出,在默认情况下, AsyncTask 是串行执行的。
AsyncTask 有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个 Handler(IntentHandler),其中线程池 SerialExecutor 用于任务的排队,而线程池 THREAD_POOL_EXECUTOR 用于真正的执行任务,InternalHandler 用于将执行环境从线程池切换到主线程。
在 AsyncTask 的构造方法中有如下一段代码,由于 FutureTask 的 run 方法会调用 mWork 的 call 方法,因此 mWork 的 call 方法最终会在线程池中执行。
public AsyncTask()
mWorker = new WorkerRunnable<Params, Result>()
public Result call() throws Exception
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
;
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);
;
在 mWorker 的call方法中,首先将 mTaskInvoked 设置为true,表示当前任务已经调用过了,然后执行 AsyncTask 的 doBackground 方法,接着将其返回值传递给 postResult 方法。
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 Handler getHandler()
synchronized (AsyncTask.class)
if (sHandler == null)
sHandler = new InternalHandler();
return sHandler;
postResult 方法会通过 sHandler 发送一个 MESSAGE_POST_RESULT 的消息:
private static class InternalHandler extends Handler
public InternalHandler()
super(Looper.getMainLooper());
@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;
可以发现,sHandler 是一个静态对象,为了能够将执行环境切换到主线程,这就要求 sHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就要求 AsyncTask 的类必须要在主线程中加载,否则同一个进程中的 AsyncTask 都将无法正常工作。
sHandler 收到 MESSAGE_POST_RESULT 这个消息后会调用 AsyncTask 的 finish 方法:
private void finish(Result result)
if (isCancelled())
onCancelled(result);
else
onPostExecute(result);
mStatus = Status.FINISHED;
如果 AsyncTask 被取消的话,那么就会调用onCancelled 方法,否则就会调用 onPostExecute 方法,doBackground 的返回结果会传递给 onPostExecute 方法,到这里整个 AsyncTask 的工作工程分析完毕了。
HandlerThread
HandlerThread 继承了Thread,它是一种可以使用 Handler 的 Thread,在 run 方法中通过 Looper.prepare() 来创建消息队列,并通过 Looper.loop() 来开启消息循环。
HandlerThread 的 run 方法:
public void run()
mTid = Process.myTid();
Looper.prepare();
synchronized (this)
mLooper = Looper.myLooper();
notifyAll();
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
在 HandlerThread 的内部创建了一个具体的任务,外界需要通过Handler的消息方式来通知 HandlerThread 执行一个具体的任务。HandlerThread 的 run 方法是无限循环的,因此当明确不需要再使用 HandlerThread 时,可以通过它的 quit 或者 quitSafely 方法来终止线程的执行。
IntentService
IntentService 是一种特殊的 Service ,它继承了 Service 并且它是一个抽象类,因此必须创建它的子类才能使用 IntentService。
IntentService 可用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于 IntentService 是服务的原因,这导致它的优先级比单纯的线程要高很多,比较适合一些高优先级的后台任务。
IntentService 封装了 HandlerThread 和 Handler:
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);
当 IntentService 被第一次启动时,它的 onCreate 方法就会被调用,onCreate方法会创建一个 HandlerThread,然后使用它的 Looper 来构造一个 Handler 对象 mServiceHandler,这样通过mServiceHandler 发送的消息最终都会在 HandlerThread 中执行,从这个角度来看,IntentService 也可用于执行后台任务。
每次启动 IntentService,它的 onStartCommand 方法就会调用一次,IntentService 在 onStartCommand 中处理每个后台任务的 Intent。
public void onStart(Intent intent, int startId)
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
IntentService 仅仅通过 mServiceHandler 发送一个消息,这个消息会在 HandlerThread 中处理。 mServiceHandler 收到消息后,会将 Intent 对象传递给 onHandleIntent 方法去处理。
注意:这个 Intent 对象的内容和外界的 startService(intent) 中的内容是完全一致的,通过这个 Intent 对象即可解析出外界启动 IntentService 时所传入的参数,这样 onHandleIntent 方法就可以对不同的后台任务做处理了。当 onHandleIntent 方法执行结束后,IntentService 会通过 stopSelf(int startId) 方法来尝试停止服务。
private final class ServiceHandler extends Handler
public ServiceHandler(Looper looper)
super(looper);
@Override
public void handleMessage(Message msg)
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
IntentService 的 onHandleIntent 方法是一个抽象方法,它的作用就是从 Intent 参数中区分具体的任务并执行这些任务。 如果目前只存在一个后台任务,那么 onHandleIntent 方法执行完这个任务后,stopSelf(int starId)就会直接停止任务,如果有多个任务,会执行完最后一个任务再停止服务,这些任务会按照外界发起的顺序排队执行。
Android中的线程池
线程池的优点:
- 重用线程池的线程,避免因为线程的创建和销毁所带来的性能开销;
- 能有效的控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象;
- 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能;
Android 中的线程池来源于 Java 中的 Executor,Executor 是一个接口,真正的线程池实现为 ThreadPOOLExecutor。
ThreadPoolExecutor
ThreadPoolExecutor 是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
corePoolSie
线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个事件间隔由keepAliveTime 所指定,当等待事件超过 keepAliveTime 所指定的时间,核心线程会被终止。maximumPoolSize
线程池中所能容纳的最大线程数,当活动线程数达到这个数值后,后续的任务会被阻塞。keepAliveTime
非核心线程闲置是的超时时长,超过这个时长,非核心线程就会被回收。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true,keepAliveTime 同样会作用于核心线程。unit
用于指定 keepAliveTime 参数的时间单位,这是一个枚举,常用的有 TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等。workQueue
线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个参数中。threadFactory
线程工厂,为线程池创建新线程的功能。ThreadFactory 是一个接口,它只有一个方法: Thread newThread(Runnable r).
ThreadPoolExecutor 执行任务时大致遵循如下规则:
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
- 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会直接插入到任务队列中排队等待执行。
- 如果在步骤2中无法将任务插入到任务队列里,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
- 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor 会调用 RejectedExecutionHandler 的 rejectedExecution 方法来通知调用者。
线程池的分类
1. FixedThread
通过Executors 的 newFixedThreadPool 方法来创建,它是一种线程数量固定的线程池,当线程处于空闲状态时,它们不会被回收,除非线程池被关闭了。
当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有空闲线程。由于此线程池只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求。
2. CacheThreadPool
通过Executors 的 newCacheThreadPool 方法来创建,它是一种线程数量不定的线程池,它只有非核心线程,并且最大线程数为 Integer.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就利用空闲的线程来处理新任务。线程池中的空闲线程超时时长为60s,超过就会被回收,线程池的任务会立即执行。所以这类线程池比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候CacheThreadPool 之中实际上是没有任何线程的,它几乎是不占用任何系统资源的。
3. ScheduleThreadPool
通过Executors 的 ScheduleThreadPool 方法来创建,它的核心线程数是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。ScheduleThreadPool 这类线程池主要用来执行定时任务和具有固定周期的重复任务。
4. SingleThreadExecutor
通过Executors 的 SingleThreadExecutor 方法来创建,这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor 的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。
以上是关于Android 的线程(AsyncTaskHandlerThreadIntentService详解)和线程池的主要内容,如果未能解决你的问题,请参考以下文章