android进阶(十一)-----Android线程和线程池

线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则用于执行耗时操作。

android找那个线程有很多种实现方式:AsyncTask、IntentService、HandlerThread。

AsyncTask封装了线程池和Handler,主要为了方便开发者在子线程中更新UI

HandlerThread是具有消息循环的线程,内部可以使用Handler

IntentService是一个服务,内部采用HandlerThread执行任务,任务执行完毕后会自动退出

一、主线程和子线程

Android沿用了java的线程模型,分为主线程和子线程,其中主线程也叫UI线程。主线程的作用是运行四大组件以及处理他们和用户的交互,而子线程则执行耗时任务,比如网络请求。I/O操作等。从android3.0开始系统要求网络访问必须在子线程中进行,否则会抛出NetworkOnMainThreadException异常,这样做是为了避免主线程由于被耗时操作阻塞而出现的ANR。

二、Android中的线程形态

1、AsyncTask:

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

AsyncTask是一个抽象的泛型类,提供了Params、Progress和Result三个泛型参数,Params表示参数类型,Progress表示后台任务的执行进度的类型,Result表示后台任务的返回结果的类型

2、AsyncTask提供4个核心方法:

(1)onPreExecute():在主线程执行,在异步任务执行之前会被调用,可以做一些准备工作。

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

(3)onProgressUpdate(Progress...values):在主线程执行,当后台任务的执行进度发生改变时调用此方法

(4)onPostExecute(Result result):在主线程中执行,在异步任务执行之后会被调用,result参数是后台任务的返回值,即doInBackground的返回值。

代码实例:

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

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

if(isCancelled()){

break;

}

return totalSize;

}

}

protected void onProgressUpdate(Integer...progress){

setProgressPercent(progress[0]);

}

protected void onPostExecute(Long result){

showDialog("Downloaded"+result+"bytes");

}

}

3、AsyncTask在使用过程中有一些条件限制,主要有以下几点:

(1)AsyncTask的类必须在主线程中加载。

(2)AsyncTask的对象必须在主线程中创建

(3)execute方法必须在UI线程调用

(4)不要在程序中直接调用onPreExecute、onPostExecute、doInBackground和onProgressUpdate方法

(5)一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常

4、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");

}

}

mStatus = Status.RUNNING;

onPreExecute();

mWroker.mParams = params;

exec.execute(mFuture);

return this;

}

sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行。在executeOnExcutor方法中,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);

}

}

}

AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境从线程池切换到主线程。

 

5、HandlerThread

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

public void run(){

mTid = Process.myTid();

Looper.prepare();

synchronized(this){

mLooper = Looper.myLooper();

notifyAll();

}

Process.setThreadPriority(mPriority);

onLooperPrepared();

Looper.loop();

mTid = -1;

}

6、IntentService

IntentService是一种特殊的Service,继承了Service并且他是一个抽象类,因此必须创建他的子类才能使用。IntentService用于执行后台耗时任务,当任务执行完成会自动停止,同时它的优先级比线程高很多。IntentService封装了HandlerThread和Handler,可以从它的onCreate中看出,代码如下:

public void onCreate(){

super.onCreate();

HandlerThread thread = new HandlerThread("IntentService["+mName+"]");

thread.start();

mServiceLooper = thread.getLopper();

mServiceHandler = new ServiceHandler(mServiceLooper);

}

当IntentService第一次启动时,oncreate会被调用并创建一个HandlerThread,然后使用它的Looper构造一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行。从这个角度看,IntentService也可以执行后台任务。每次启动IntentService,他的onStartCommand方法就会调用一次,IntentService在onStartCommand中处理每个后台任务的Intent。下面看一下onStartCommand如何处理外界Intent。代码如下:

public void onStart(Intent intent,int startId){

Message msg = mServiceHandler.obtainMessage();

msg.arg1 = startId;

msg.obe = intent;

mServiceHandler.sendMessage(msg);

}

下面来看一下IntentService的工作方式,示例代码:

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

SystemClock.sleep(3000);

if("com.ryg.action.TASK1".equals(action)){

Log.d(TAG,"handle task:"+action);

}

}

@Override

public void onDestroy(){

super.onDestroy();

}

}

三、Android中的线程池

1、线程池的优点:

(1)重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销

(2)能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。

(3)能够对线程进行简单的管理,并提供定时执行以及制定间隔循环执行等功能。

Android中的线程池来源于java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。

2、ThreadPoolExecutor

(1)ThreadPoolExecutor是线程池的真正实现,他的构造方法提供了一系列参数来配置线程池,下面是ThreadPoolExecutor的一个常用的构造方法

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,

BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory)

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

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

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

unit:用于指定keepAilveTime参数的时间单位,这是一个枚举,常用的有TimeUnit、MILLISECONDS、TimeUnit.SECONDS以及TimeUnit.MINUTES等。

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

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

(2)ThreadPoolExecutor执行任务时大致遵循如下规则:

  • 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
  • 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
  • 如果无法将任务插入到任务队列中,这往往由于任务队列已满,这时候如果线程数量未达到线程池规定的最大值,会立刻启动一个非核心线程来执行任务。
  • 如果线程数量已经达到最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者

(3)ThreadPoolExecutor的参数配置在AsyncTask中有明显的体现,代码如下

private static final int CUP_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);

public Thread newThread(Runnable r){

return new Thread(r,"AsyncTask #"+mCount.getAndIncrement());

}

};

private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);

public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);

AsyncTask对THREAD_POOL_EXECUTOR这个线程池进行了配置:

核心线程数等于CPU核心数+1

线程池的最大线程数为CPU核心数的2倍+1

核心线程无超时机制,非核心线程在闲置时的超时时间为1秒

任务队列的容量为128

 

四:线程池的分类

Android中最常见的四种具有不同功能特性的线程池,他们都直接或间接通过配置ThreadPoolExecutor来实现自己的功能特性,四种线程池分别是:FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor。

1、FixedThreadPool:

Executors的newFixedThreadPool方法来创建。他是一种线程数量固定的线程池,当线程处于空闲状态时,他并不会被回收,除非线程池被关闭了。示例代码

public static ExecutorService newFixedThreadPool(int nThreads){

return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());

}

2、CachedThreadPool:

Executors的newCachedThreadPool方法来创建。他是一种线程数量不定的线程池,他只有非核心线程,并且最大线程数为Integer.MAX_VALUE。由于Integer.MAX_VALUE是一个很大的数,实际上相当于最大线程数.

public static ExecutorService newCachedThreadPool(int nThreads){

return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());

}

3、ScheduledThreadPool:

通过Executors的newScheduledThreadPool来创建。他的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){

return new ScheduledThreadPoolExecutor(corePoolSize);

}

public ScheduledThreadPoolExecutor(int corePoolSize){

super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());

}

4、SingleThreadExecutor:

通过Executors的newSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,他确保所有任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。代码如下:

public static ExecutorService newSingleThreadExecutor(){

return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>()));

}

除了上面系统提供的4类线程池以外,也可以根据实际需要灵活低配置线程池。示例代码:

Runnable command = new Runnable(){

@Override

public void run(){

SystemClock.sleep(2000);

}

};

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);

fixedThreadPool.execute(command);



ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

cachedThreadPool.execute(command);



ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);

scheduledThreadPool.schedule(command,2000,TimeUnit.MILLISECONDS);

scheduledThreadPool.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);



ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

singleThreadExecutor.execute(command);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章