第十一章-Android线程和线程池(AsyncTask、HandlerThread、IntentService)

本文深入探讨Android中的线程形态,包括AsyncTask的执行流程、HandlerThread的原理和IntentService的应用。重点分析AsyncTask的四个核心方法,以及其在不同Android版本中的变化。同时介绍了HandlerThread如何实现使用Handler的子线程,以及IntentService的后台任务处理方式。文章最后讨论了线程池的优势和Android线程池的四种类型。

Android中的线程形态

本节将对Android中的线程形态做一个全面的介绍,除了传统的Thread以外,还包含了AsyncTask,HandlerThread以及IntentService,这三者的底层实现也是线程,但是他们具有特殊的表现形式,同时在使用上也各有优缺点,下面从源码的角度来分析AsyncTask的执行过程。

一、AsyncTask

AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,通过AsyncTask我们更加方便的执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。

AsyncTask是一个抽象的泛型类,他提供了Params,Progress和Result这三个泛型参数,其中Params表示参数的类型,Progress表示后台任务的执行进度,Result表示后台任务的返回结果的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型可以用Void来代替。AsyncTask这个类的声明如下所示。

public abstract class AsyncTask<Params, Progress, Result> 

AsyncTask提供了4个核心方法,它们的含义如下所示。

  • onPreExecute()

在主线程执行,在异步任务执行之前被调用,一般可以用于做一些准备工作。

  • doInBackground(Params… params)

在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress会调用onProgressUpdate方法。此外此方法需要返回计算结果给onPostExecute方法。

  • onProgressUpdate(Progress… values)

在主线程执行,当后台任务的执行进度发生改变时此方法会被调用。

  • onPostExecute(Result result)

在主线程中执行,在异步任务结束之后,此方法被调用,其中result参数是后台任务的返回值,即DoInBackground的返回值。

上面几个方法,onPreExecute先执行,接着是doInBackground,最后才是onPostExecute。除了上述四个方法以外,AsyncTask还提供了一个onCancelled()方法,它同样在主线程中执行,当异步任务被取消时,onCancelled就会被调用,这个时候onPostExecute就不会被调用。下面提供一个典型的示例(这个示例就是源码中的)

   class DownloadTask extends AsyncTask<URL,Integer,Long>{

        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < totalSize; i++) {
                totalSize += DownLoad.downloadFile(urls[i]);
                publicProgress((int)((i / (float)count)*100));
                if(isCancelled()){
                    break;
                }
            }
            return totalSize;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
           setProgressPercent(values[0]);
        }

        @Override
        protected void onPostExecute(Long aLong) {
            showDialog(aLong);
        }
    }

AsyncTask在使用的过程中也有一些条件限制的,主要有如下几点:

  • 1、AsyncTask的类必须在主线程加载,也就意味着第一次访问AsyncTask必须发生在主线程,当然这个过程在Android4.1之后已经被系统自动完成了,在Android5.0的源码中,可以看到ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中加载这个条件了。
  • 2、AsyncTask的对象必须在主线程创建。
  • 3、execute方法必须在UI线程调用。
  • 4、不要再程序中直接使用onPreExecute、onPostExecute、DoInBackground和onProgressUpdate方法。
  • 5、一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会运行时异常。
  • 6、在Android1.6之前,AsyncTask是串行执行任务的,Android1.6之后开始采用线程池并行处理,但是从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行任务。尽管如此,在Android3.0以及以后的版本中,我们也可以通过AsyncTask的executteOnExecutor方法来并行的执行任务。

二、AsyncTask的工作原理
为了分析AsyncTask的工作原理,我们从它的execute方法开始分析

	android.os.AsyncTask#execute
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
		//private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
        return executeOnExecutor(sDefaultExecutor, params);
    }

	android.os.AsyncTask#executeOnExecutor
	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);
            }
        }
    }

在这里插入图片描述

	mWorker = new WorkerRunnable<Params, Result>() {
		public Result call() throws Exception {
			mTaskInvoked.set(true);

			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
			//noinspection unchecked
			return postResult(doInBackground(mParams));
		}
	};

在mWorker的call方法里,首先将mTaskInvoked设置为true,表示当前任务已经被调用过了,然后执行AsyncTask的doInBackgroud方法,接着将其返回值传递给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 Handler getHandler() {
        return mHandler;
    }
	
	private final Handler mHandler;
	在下面代码赋值
	mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
	? getMainHandler()
	: new Handler(callbackLooper);
			
	private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());//创建实例
            }
            return sHandler;
        }
    }
	

postResult方法会通过sHandler发送给一个MESSAGE_POST_RESULT的消息,这个sHandler的定义如下所示。

    private static class InternalHandler extends Handler {
        @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是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就是要求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的finish方法的逻辑比较简单,如果AsyncTask被取消执行了,那么就会调用onCancelled方法,否则就会调用onPostExecute方法,可以看到doInBackground的返回结果会传递给onPostExecute方法,到这里AsyncTack的整个工作过程就完毕了。

三、HandlerThread
HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了,HandlerThread的run如下:

比如我们可以使用一个HandlerThread,将我们的业务切换到子线程执行(和主线程的逻辑刚好相反)。

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

在这里插入图片描述

四、IntentService

在这里插入图片描述

    @Override
    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也可以用于执行后台任务。每次启动onstartCommon就会调用一次,IntentService在onStartCommon中处理每个后台任务的intent。下面看一下onStartCommon方法是如何处理外界的intent的,onStartCommon调用了onStart,onStart方法实现如下所示。

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

在这里插入图片描述

    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);
        }
    }

这里是引用
在这里插入图片描述

public class LocalIntentService extends IntentService {

    private static final String TAG = "LocalIntentService";

    public LocalIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent( Intent intent) {
        String action = intent.getStringExtra("task_action");
        Log.i(TAG,"receiver action:" + action);
        SystemClock.sleep(3000);
        if("com.lgl.test.ACTION".equals(action)){
            Log.i(TAG,"handler action:" + action);
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG,"onDestroy");
        super.onDestroy();
    }
}

五、Android中的线程池
线程池有如下优点。

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

Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池,从线程池的功能特性上来说,Android的线程池主要分为4类,可以通过Executors所提供的工厂方法得到的。

1、ThreadPoolExecutor
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列的参数来配置,构造如下所示。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue
                              ThreadFactory threadFactory) {
  • corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个间隔由keepAliveTime指定,当等待时间超过keepAliveTime所指的时长后,核心线程就会被终止。

  • maximumPoolSize

线程池所容纳最大的线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。

  • keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。

  • unit

用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit,毫秒,秒以及分钟等。

  • workQueue

线程池的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。

  • threadFactory

线程工厂,为线程池提供创建新线程的功能,ThreadFactory是一个接口,它只有一个方法,Thread new
Thread(Runnable r)。

这里是引用
在这里插入图片描述

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {

        private final AtomicInteger mCount = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingDeque<Runnable>(128);
    public static final Executor THREAD_POOL_EXECUTOR =
            new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

在这里插入图片描述

2、线程池的分类
常见的4中不同功能特性的线程池

  • FixedThreadPool

在这里插入图片描述

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
  • CachedThreadPool

这里是引用

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • ScheduleThreadPool

这里是引用

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
  • SingleTheardExecutor

这里是引用

    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }

下面演示下系统预置的4中线程池的典型用法。

Runnable common = new Runnable() {
	@Override
	public void run() {
		SystemClock.sleep(2000);
	}
};

ExecutorService fixThreadPool = Executors.newFixedThreadPool(4);
fixThreadPool.execute(common);

ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
cacheThreadPool.execute(common);

ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(4);
scheduleThreadPool.schedule(common,2000,TimeUnit.MICROSECONDS);
scheduleThreadPool.scheduleAtFixedRate(common,10,1000,TimeUnit.MICROSECONDS);

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
singleThreadPool.execute(common);

总结:线程和线程池都介绍完成了。平时开发中可以封装一个线程池工具类。这样避免随意的new Thread导致一些性能开销或内存泄漏等问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值