Android开发艺术探索 - 第11章 Android的线程和线程池
Android的主线程主要是运行四大组件以及处理其与用户的交互;而子线程用于执行耗时任务,如网络请求,I/O操作。
1.Android中的线程形态
- AsyncTask
在线程池中执行后台任务,其封装了Thread和Handler,通过execute方法执行AsyncTask。关键方法:- onPreExecute:在执行execute方法的线程中运行,一般为主线程,用于执行在开始异步任务之前的一些准备工作。
- doInBackground:在线程池中运行,用于执行异步任务。方法中通过publishProgress方法更新任务进度,进而调用到onProgressUpdate方法。此方法需要返回计算结果给onPostExecute。
- onProgressUpdate:主线程中运行,异步任务更新进度时被回调。
- onPostExecute:主线程中运行,异步任务执行完毕被回调。
- onCancelled:主线程中运行,当异步任务被取消时被回调,此时onPostExecute不会被调用。
限制:
* AsyncTask的类必须要在主线程中加载。所以第一次访问AsyncTask时需要在主线程中进行,Android 4.1以上会自动完成这个过程,在ActivityThread的main方法中。
* AsyncTask的对象必须在主线程中创建。
* execute方法必须在UI线程调用。
* 不要直接调用AsyncTask的关键方法。
* 一个AsyncTask对象只能执行一次,即execute只能调用一次,否则会抛出异常。
* 可以通过exectueOnExecutor来指定线程池执行任务。
原理:
从execute方法开始。execute会使用默认的线程池,即SerialExecutor。一个进程中所有的AsyncTask都会在这个线程池中排队(static成员):
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); public final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
之后调用executeOnExecutor,先执行onPreExecute方法,然后线程池开始执行:
public final AsyncTask executeOnExecutor(Executor exec, Params... params) { ... mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this;}
SerialExecutor的实现。在实例化AsyncTask的时候,会创建一个FutureTask对象,该类是一个并发类,充当了Runnable的作用,其中会包含execute传入的参数。当线程池开始执行,首先将当前的FutureTask对象插入到任务队列中,如果当前没有正在活动的任务,则调用scheduleNext方法执行下一个任务;同时一个任务执行完毕,也会调用scheduleNext方法执行下一个任务:
private static class SerialExecutor implements Executor { final ArrayDeque mTasks = new ArrayDeque(); 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); } }}
scheduleNext方法则通过另一个线程池,THREAD_POOL_EXECUTOR去具体执行任务,该线程池是个并发线程池。默认情况下,AsyncTask会通过SERIAL_EXECUTOR接收Runnable,串行的每次执行一个,而具体的执行任务交由THREAD_POOL_EXECUTOR;可以让AsyncTask并发的去执行Runnable,只要在其execute方法中,直接指定THREAD_POOL_EXECUTOR这个线程池就可以了。
当任务开始执行,会调用FutureTask的run方法。在实例化AsyncTask的时候,会创建一个WorkerRunnable,同时绑定到FutureTask上,FutureTask的run方法会调用WorkerRunnable的call方法,此时doInBackground方法便会被执行:
public AsyncTask(@Nullable Looper callbackLooper) { mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); mWorker = new WorkerRunnable() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } }; mFuture = new FutureTask(mWorker) { ... }; }
doInBackground执行完毕,会调用postResult发送出结果,其任务就是通过AsyncTask内部的Handler发送一个msg:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result;}
该Handler在创建时,Looper指定为main looper,保证其回调发生在主线程。当任务处理完成,Handler在主线程中执行AsyncTask#finish方法,当更新进度则回调AsyncTask#onProgressUpdate方法:
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; } } }
finish方法中,则根据是否AsyncTask是否被取消了,去回调onCancel或者onPostExecute:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED;}
- HandlerThread
继承自Thread,并在run方法中创建消息队列和开启了消息循环,之后便可直接通过该Thread创建相应的Handler去执行任务。其run方法:
@Overridepublic void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1;}
- IntentService
继承自Service,内部使用了HandlerThread和Handler来实现,任务在子线程中的串行执行。onCreate方法中创建HandlerThread的实例,并通过其Looper创建Handler:
@Overridepublic 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);}
而Handler的具体实现,是回调onHandleIntent方法,将具体的任务实现交由其子类,任务执行完毕后调用stopSelf尝试停止service。所有任务完成后,service销毁:
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); }}
client使用时,直接startService,IntentService#onStart方法中就会通过Handler将Intent包裹为msg发出,等待Looper去执行:
@Overridepublic void onStart(@Nullable Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg);}
2.Android中的线程池
优点:
- 重用线程池中的线程,减少创建和销毁带来的性能开销;
- 能控制最大并发数,避免大量线程抢占资源导致阻塞;
- 能对线程进行简单的管理,提供定时执行和间隔循环执行等功能。
Android中的线程池为接口Executor,具体实现类为ThreadPoolExecutor,通过为其配置不同的参数,可以创建不同的线程池。
- ThreadPoolExecutor
常用的构造方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {
* corePoolSize:核心线程数。默认情况下,核心线程会在线程池中一直存活,即使处于闲置状态。* maximumPoolSize:最大线程数。包括了核心线程和非核心线程。* keepAliveTime:非核心线程闲置的超时时间,超过这个时间就就会被回收。当把allowCoreThreadTimeOut设置为true,同时也会作用于核心线程的。* unit:keepAliveTime的时间单位。* workQueue:线程池中的任务队列,通过execute方法提交的Runnable会添加到这里。* threadFactory:创建线程时使用的工具类。* RejectedExecutionHandler类型的不常用参数,用于指定,当无法执行新任务时,线程池会回调该handler的rejectedExecution方法,默认实现是直接抛出一个RejectedExecutionHaException异常。
ThreadPoolExecutor执行任务的规则:
1. 如果线程池中的核心线程数未达到上限,则直接启动一个核心线程去执行;
2. 如果核心线程数已达上限,则将任务插入到队列中排队;
3. 如果队列也满了,则尝试创建非核心线程去执行;
4. 如果总的线程数已经达到了上限,此时已经无法执行任务,此时便会调用RejectedExecutionHandler#rejectedExecution方法,通知调用者。
ThreadPoolExecutor在AsyncTask中的创建示例:
```
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); }};private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue(128);/** * An {@link Executor} that can be used to execute tasks in parallel. */public static final Executor THREAD_POOL_EXECUTOR;static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor;}```* 核心线程数为2~4个* 最大线程数为2*Cpu核心数+1* 核心线程有闲置超时* 任务队列容量为128
- 四类线程池
四类线程池都是通过Executors的静态方法进行创建。- FixedThreadPool:具有固定的核心线程,且不会被回收,当所有的核心线程处于活跃状态,新任务则排队等待。能够更快的响应外界的需求。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
());} - CachedThreadPool:没有核心线程,具有无限数量的非核心进程,且线程闲置超时会被回收;其任务队列相当于空集,所以不会缓存任何任务,有新任务便会马上执行。适合执行大量且耗时少的任务。同时因为闲置时线程会被回收,所以其闲置状态几乎不占用系统资源。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
());} - ScheduledThreadPool:具有固定数量的核心线程和无限的非核心线程,非核心线程闲置超时会被回收。用于执行定时任务和周期性任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
// ScheduledThreadPoolExecutor.java private static final long DEFAULT_KEEPALIVE_MILLIS = 10L; public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
- SingleThreadPool:只有一个核心线程,所有的任务按顺序执行。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
()));}
更多相关文章
- Android(安卓)中级学习心得
- Android(安卓)8.0 EditText 焦点无法移动到其他控件
- 在Eclipse下编译Android原生APK方法
- 实现静默安装和卸载应用
- H5与android原生的JS交互
- 让android的webview中的按钮,触发事件,也能像原生按钮一样使用
- Android中的对象序列化方法
- Android客户端与PC服务端、android服务端通过WiFi通信
- android view类分析