AsyncTask,IntentService工作原理分析&Android线程池

一,android中的主线程和子线程

android中的主线程可以认为是UI线程,在主线程不可以执行耗时的操作,否则就会给人一种卡顿的感觉。而主线程主要用于处理四大组件,以及处理它们和用户的交互。anroid的子线程的主要功能就是处理耗时操作。

要知道”在android3.0之后,要求网络访问必须在子线程执行,否则会抛出NetWorkOnMainThreadException异常。”

二,Android中的线程形态

Android中的线程状态,除了传统的Thread,还包含AsyncTask,HandlerThread,IntentService。

它们的底层其实都是Thread,它们的出现都是为了简化子线程更新UI的过程。

下面来逐一的介绍它们的使用和原理。

1,AsyncTask

在AsyncTask中,会在线程池中处理后台任务,并把处理的结果和处理的进度传递到主线程中去,方便更新UI。

使用它可以很方便的执行后台任务和更新UI。它实现的原理,是Handler和Thread,只是AsyncTask封装了它们。

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象类,并且它的子类需要传递泛型进来。

Params:表示参数类型
Progress:表示后台任务的进度类型
Result:表示后台任务的返回值类型

它的方法:

下面的AsyncTask被用来访问一张网络上的图片。图片的url是String,所以它的Params为String类型。所需要更新的进度类型为数字,为Integer类型。不需要处理后台任务的结果,所以Result为Void类型。

class AsuleAsyncTask extends AsyncTask<String,Integer,Void>{
        //异步操作执行之前调用,在主线程运行
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.i(Constants.LOG,"onPreExecute");
        }

        //执行异步任务,在线程池中执行,params表示异步任务的输入参数
        //在此方法中可以通过 publishProgress(进度值)来更新任务进度条,会去调用onProgressUpdate
        //后台任务的结果返回给onPostExecute
        @Override
        protected Void doInBackground(String... params) {
            Log.i(Constants.LOG, "doInBackground");
            String url=params[0];
            Log.i(Constants.LOG, "url" + url);
            //根据url去请求图片,执行异步任务
            //...........
            return null;
        }

        //主线程运行,更新进度UI
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        //异步操作执行之后调用,在主线程运行,
        //它的参数是doInBackground方法的返回值。处理后台任务的结果
        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            Log.i(Constants.LOG, "onPostExecute");
        }
    }

执行后台任务:

new AsuleAsyncTask().execute("http://f10.topitme.com/o/201102/17/12979229866129.jpg");

2,从源码上分析AsyncTask的工作原理

AsyncTask的execute方法:

sDefaultExecutor实际上是一个串行的线程池。

AysncTask的onPreExecute方法会先执行,接下来线程池开始执行任务。
FutureTask交给SerialExecutor来执行。

线程池的执行过程:
mFuture是FutureTask类型的变量。
如:private final FutureTask<Result> mFuture;
它在AstncTask的构造方法中初始化。

FutureTask实现了Runnable接口,可以当作是操作任务的类,我们都知道Runnable实现类可以理解为一个任务,线程负责执行Runnable任务。
而FutureTask不仅可以被线程当作一个任务来执行,而且它还可以调度任务。
比如FutureTask的cancel方法,可以中断执行任务的线程,记录任务结束状态。
相对于Runnable而言,FutureTask可以对任务进行管理。

mTasks是一个存储着Runnable的ArrayDeque,它表示着一个任务队列。
首先execute方法会把FutureTask对象插入到mTasks的任务队列中去。
如果没有正在活动的AsyncTask任务,就会调用scheduleNext方法。
而当一个任务执行完之后,AysncTask会继续执行其他任务,直到所有的任务都被执行完毕。

Async中有两个线程池,一个就是下面的SerialExecutor,还有就是THREAD_POOL_EXECUTOR线程池。

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

SerialExecutor用于任务的排列,而THREAD_POOL_EXECUTOR是真正的用于处理任务。(scheduleNext处理任务)

处理任务时,会去执行FutureTask的run方法。

AsyncTask的构造方法,对FutureTask进行了初始化,并且把WorkerRunnable实例作为FutureTask的构造参数。
在FutureTask的run中会call方法,这里callable代表的就是WorkerRunnable对象。
调用call方法,mTaskInvoked设为true,表示当前任务已经调用过了,
然后执行AsyncTask的doInBackground方法,将其返回值传递给postResult方法。
所以可以推断出doInBackground是运行在线程池中。



postResult会通过InternalHandler发送一个标记为MESSAGE_POST_RESULT的Message,
InternalHandler在收到这个消息后,会调用AsyncTask的finish方法。

InternalHandler的作用是把执行环境切换到主线程,所以InternalHandler的创建必须要是在主线程中。
由于InternalHandler是静态的,AsyncTask类一加载就会进行初始化。
同样,AsyncTask的类也必须要在主线程加载。

而finish方法就很简单了,如果AsyncTask任务被取消了,就会调用onCancelled,
如果没有,代表后台任务已经执行完毕,就会把返回结果交由onPostExecute方法。
到这里,AsyncTask的整个工作过程就完毕了。







3,IntentService

IntentService继承自Service,并且它是抽象类,创建它的子类实现抽象方法才可以使用它。
IntentService负责执行后台的耗时的任务,也因为IntentService是服务的原因,这导致它的优先级比单纯的线程要高。
所以IntentService适用于执行高优先级的后台任务。

在IntentService的onCreate中:

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

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
 thread.start();
 HandlerThread继承自Thread代表一个线程,start方法会去调用HandlerThread的run。
 如下:
 public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
Looper.prepare方法会创建消息队列MessageQueue和Looper对象。
Looper.loop();就开始在消息队列中轮询消息。

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
根据获取的Looper,构造一个Handler对象ServiceHandler。     

要知道,prepare方法是由ActivityThread的main方法调用的,
也就是说应用一启动,MessageQueue和Looper对象实际上就已经被创建了。
这里又创建一次的原因,我自己是这样想的,ActivityThread的main创建的是代表着主线程的MessageQueue和Looper。
而我们这里是为一个子线程创建MessageQueue和Looper对象。
而MessageQueue和Looper对象是构建Handler的基础。

所以就认为ServiceHandler发送的消息和处理消息都是在HandlerThread这个线程中。
每次一启动IntentService,除了onCreate初始化之外,
IntentService会在onStartCommand中处理后台任务的intent。

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

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

onStartCommand方法中,mServiceHandler发送了一条消息,
这个消息会在ServiceHandler中去处理。怎么处理呢?
msg.obj代表的是你开启IntentService的Intent对象,
这个时候可以从这个Intent对象中解析出外界所要传递的参数,通过这些参数就可以区分具体的后台任务。

onHandleIntent执行完毕后,stopSelf(taskId)会等待所有的消息执行完毕之后才终止服务。
判断所有消息执行完毕:最近启动服务的次数和startId是否相等,相等就认为所有消息都执行完毕,否则就不会终止服务。

执行顺序的问题:
执行一次后台任务开启一次IntentService,而内部是通过Handler发送消息来处理的,
而Looper轮询消息是有顺序的,所以当有多个后台任务时,会按照外界发起的顺序来执行。

例子:

class MyIntentService extends IntentService{
        public MyIntentService(String name) {
            super(name);
        }

        @Override
        protected void onHandleIntent(Intent intent) {
            String name=intent.getStringExtra("football");
            Log.i(Constants.LOG,name);
        }

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

 Intent service=new Intent(this,MyIntentService.class);
 service.putExtra("football","ronaldo");
 startService(service);
 service.putExtra("football","messi");
 startService(service);


4,android中的线程池

线程池出现的原因:

1,可以重用线程池中的线程,避免因为线程的开启和销毁带来的性能开销。
2,可以控制线程池的最大并发数,避免因为大量线程抢占系统资源而导致阻塞。
3,可以对线程进行管理,比如提供了定时执行和间隔循环执行的功能。

Android中的线程池来自于Java的Executor,它是一个接口,真正的实现是ThreadPoolExecutor类。

android中的几种线程池,底层都是由ThreadPoolExecutor来配置。

下面就来看看ThreadPoolExecutor。

ThreadPoolExecutor有很多的构造函数,通过指定这些参数来配置我们想要的线程池。

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

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

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

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

unit:用于指定keepAliveTime的时间单位,是个枚举

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

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

Android中常见的线程池:

通过查看其构造方法,根据ThreadPoolExecutor的参数信息,就可以知道每个线程池的功能特性。

创建FixedThreadPool
Executors.newFixedThreadPool(nThreads);

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
它是线程数量固定的线程池,并且线程都是核心线程。
由于都是核心线程,所有线程空闲的时候,也不会被回收。

当所有的线程都处于活动状态,没有闲置的线程时,如果有新任务过来,会处于等待状态。
只有核心线程,并且不回收,意味着FixedThreadPool可以更迅速的响应外界的请求。
创建CachedThreadPool
Executors.newCachedThreadPool()

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
所有的线程都是非核心线程,并且非核心线程的数量没有上限。
当所有的线程都处于活动状态,如果有新任务过来,就会创建新的线程去执行新的任务。
TimeUnit为60秒,当空闲的线程超过60秒还是空闲,将会被回收。
由于会进行回收,所以几乎不怎么占用系统资源,适用于执行大量的耗时较少的任务。
创建ScheduledThreadPool
Executors.newScheduledThreadPool()

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

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
}
DEFAULT_KEEPALIVE_MILLIS=10L

核心线程数量固定,非核心线程数量没有上限。并且非核心线程如果空闲会很快被回收。
适用于执行定时任务和具有固定周期的重复任务。
创建SingledThreadPool
Executors.newSingleThreadExecutor()

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

核心线程有且只有一个,并且不会被回收。
它确保所有的任务都在同一个线程中按顺序执行。
它可以统一所有的外界任务在同一个线程中按顺序执行,使得这些线程之间不需要解决线程同步的问题。
时间: 2024-12-25 20:12:36

AsyncTask,IntentService工作原理分析&Android线程池的相关文章

深入理解AsyncTask的工作原理

一.为什么需要工作者线程 我们知道,Android应用的主线程(UI 线程)肩负着绘制用户界面和及时响应用户操作的重任,为了避免“用户点击按钮后没反应”这样的糟糕用户体验,我们就要确保主线程时刻保持着较高的响应性.为了做到这一点,我们就要把耗时的任务移出主线程,那么耗时的任务交给谁来完成呢?答案就是工作者线程.Android开发中我们通常让主线程负责前台用户界面的绘制以及响应用户的操作,让工作者线程在后台执行一些比较耗时的任务.Android中的工作者线程主要有AsyncTask.IntentS

从使用到原理学习Java线程池

来源:SilenceDut http://www.codeceo.com/article/java-threadpool-learn.html 线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收. 所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁.如何利用已有对象来服务就是一个需要解决的关键问题,其实这

Android线程池(二)——ThreadPoolExecutor及其拒绝策略RejectedExecutionHandler使用演示样例

MainActivity例如以下: package cc.vv; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import android.os.Bundle; import android.app.Activity; /** * Demo描写叙述: * 线程池(Threa

Android线程池(二)——ThreadPoolExecutor及其拒绝策略RejectedExecutionHandler使用示例

MainActivity如下: package cc.vv; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import android.os.Bundle; import android.app.Activity; /** * Demo描述: * 线程池(ThreadPoo

原理剖析-Netty之服务端启动工作原理分析(下)

一.大致介绍 1.由于篇幅过长难以发布,所以本章节接着上一节来的,上一章节为[原理剖析(第 010 篇)Netty之服务端启动工作原理分析(上)]: 2.那么本章节就继续分析Netty的服务端启动,分析Netty的源码版本为:netty-netty-4.1.22.Final: 二.三.四章节请看上一章节 四.源码分析Netty服务端启动 上一章节,我们主要分析了一下线程管理组对象是如何被实例化的,并且还了解到了每个线程管理组都有一个子线程数组来处理任务: 那么接下来我们就直接从4.6开始分析了:

Java(Android)线程池 总结

一种是使用Executors工厂生产线程池:另一种是直接使用ThreadPoolExecutor自定义. Executors工厂生产线程池 Java(Android)线程池 Trinea 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new Thread吗? Java 1 2 3 4 5 6 7 newThread(newRunnable(){ @Ove

Java(Android)线程池---基础篇

1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? 1 newThread(newRunnable(){ 2 3 @Override 4 publicvoidrun(){ 5 // TODO Auto-generated method stub 6 } 7 }).start(); 那你就out太多了,new Thread的弊端如下: a. 每次new Thread新建对象性能差.b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致

spi协议及工作原理分析

转自----http://blog.csdn.net/skyflying2012/article/details/11710801 一.概述. SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允

Android(java)学习笔记267:Android线程池

1. 线程池 (1)线程池的优点: 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销. 能有效控制线程池的最大并发数,避免大量的线程之间因相互抢占系统资源而导致的阻塞现象. 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能. (2)Android中的线程池: Android中的线程池的概念来源于Java中的Executor,Executor是一个借口,真正的线程池实现为ThreadPoolExecutor. ThreadPoolExecutor提供了一系列参数来配置