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个核心方法,它们的含义如下:

  1. onPreExecute()
    这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  2. doInBackground(Params…)
    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成 。
  3. onProgressUpdate(Progress…)
    当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
  4. 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中的线程池

线程池的优点:

  1. 重用线程池的线程,避免因为线程的创建和销毁所带来的性能开销;
  2. 能有效的控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象;
  3. 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能;

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 执行任务时大致遵循如下规则:

  1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
  2. 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会直接插入到任务队列中排队等待执行。
  3. 如果在步骤2中无法将任务插入到任务队列里,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
  4. 如果步骤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详解)和线程池的主要内容,如果未能解决你的问题,请参考以下文章

Android进阶知识——Android线程和线程池

Android进阶知识——Android线程和线程池

Android进阶知识——Android线程和线程池

Android进阶知识——Android线程和线程池

Android线程池的使用

《Android开发艺术探索》第11章 Android的线程和线程池