Android开发艺术探索:Android的线程与线程池

AsyncTask封装了线程池和Handler,方便用来子线程里面更新UI,HandlerThread具有消息循环的线程,可以直接在内部使用Handler,IntentService封装方便地执行后台任务,内部使用HanlderThread来执行任务。
IntentService做为一种服务,不容易被系统杀死,如果一个线程里面附加到四大组件,优先级很低,容易被系统回收,关于进程优先级可以参考官方文档Processes and Threads

前台进程 > 可见进程 > 服务进程 > 后台进程 > 空进程

Android的线程形态

AsyncTask实例(官方文档里面的):

 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {

    protected void onPreExecute() {
        //init
    }

     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }

//use
  new DownloadFilesTask().execute(url1, url2, url3);

AsyncTask三个泛型参数:Params为执行doInBackground里面接收的参数,Progress为doInBackground里执行publicProgress,在onProgressUpdate接收的参数,Result为onPostExecute执行结果接收的参数。

四个重要的方法:onPreExecute doInBackground onProgressUpdate onPostExecute,除了doInBackground是在Worker Thread里面执行的,其余的都在UI线程里面执行。

使用条件限制:

protected abstract void onHandleIntent(Intent intent);

IntentService可用于执行耗时的任务,当任务执行结束之后会自动停止,由于是一种Service,优先级比单独的线程高,不容易被系统回收。IntentService封装了HandlerThread与Handler,从实现的onCreate可看的出来

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

启动服务的时候,会执行onStartCommand,然后使用mServiceHandler向HandlerThread发消息,mRedelivery表示Service被意外杀死后的行为,true表示START_REDELIVER_INTENT重启Service并重新发送Intent,false为START_NOT_STICKY不做任何操作,可以通过setIntentRedelivery来控制。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

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

ServiceHandler的实现,收到消息后调用onHandleIntent处理我们发来的请求,结束之后调用stopSelf(int startId)来尝试停止服务,注意调用stopSelf()会立刻停止服务,stopSelf(int startId)会在停止服务之前判断启动服务的次数是否和startId相等,如果相等就停止服务,这个策略是有ActivityManagerService来控制的。

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

由于使用的是Handler机制来处理,所以执行任务是串行的。

Android中的线程池

使用线程池能够对线程做管理,并且减小线程创建和销毁的开销,控制线程的并发数,避免大量线程抢占系统资源。

线程池主要使用ThreadPoolExecutor来实现,通过配置构造方法的参数来达到想要的效果:

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

corePoolSize:线程池核心线程数,核心线程默认情况下会一直存活,即使线程池处于闲置状态

maximumPoolSize:允许线程池的最大数量,达到这个值之后,后续的任务将会被阻塞

keepAliveTime:非核心线程闲置的超时时长,超过就会被回收

TimeUnit:keepAliveTime的单位

workQueue:线程池里面的任务队列

threadFactory:线程池工厂,为线程池提供创建线程的策略

handler:当线程池慢了的拒绝策略,默认的策略是抛RejectedExecutionException

ThreadPoolExecutor线程池执行任务规则:

线程池的分类

创建线程池可以使用JDK给我们提供的Executors来创建,下面四种比较常见的线程池:

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

线程数量固定的线程池,处于空闲空闲状态也不会被回收,任务队列没有大小限制。

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

线程数量不定的线程池,只有非核心线程,线程数量不能大于Integer.MAX_VALUE,提交新任务就会启动新线程,空闲线程只能存活60s,任务队列相当于一个空集合,提交任何任务都会被立即执行。

  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

核心线程固定,非核心线程没有限制的线程池,并且当非核心线程闲置时会被立即回收,主要用来执行定时任务和具有固定周期的重复任务。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

内部只有一个核心线程,确保所有任务都按照顺序在同一个线程中执行,所以任务之间不需要处理线程同步的问题。