敞开成长之旅!这是我参加「日新计划 12 月更文应战」的第28天,点击检查活动概况

从用途上来说Android的线程首要分为主线程和子线程两类,主线程首要处理和界面相关的作业,子线程首要处理耗时操作。除Thread之外,Android中还有其他扮演线程的角色如AsyncTask、IntentService、HandleThread,其中AsyncTask的底层用到了线程池,IntentService和HandleThread的底层直接运用了线程。

AsyncTask内部封装了线程池和Handler首要是为了便利开发者在在线程中更新UI;HandlerThread是一个具有音讯循环的线程,它的内部能够运用Handler;IntentService是一个服务,体系对其进行了封装使其能够更便利的履行后台使命,IntentService内部选用HandleThread来履行使命,当使命履行结束后IntentService会主动退出。IntentService是一个服务但是它不容易被体系杀死因而它能够尽量的保证使命的履行。

1.主线程和子线程

主线程是指进程所拥有的的线程,在Java中默许情况下一个进程只能有一个线程,这个线程便是主线程。主线程首要处理界面交互的相关逻辑,因为界面随时都有可能更新因而在主线程不能做耗时操作,不然界面就会呈现卡顿的现象。主线程之外的线程都是子线程,也叫做作业线程。

Android沿用了Java的线程模型,也有主线程和子线程之分,主线程首要作业是运行四大组件及处理他们和用户的交互,子线程的首要作业便是处理耗时使命,例如网络恳求,I/O操作等。Android3.0开端体系要求网络拜访必须在子线程中进行不然就会报错,NetWorkOnMainThreadException

2.Android中的线程形状

2.1 AsyncTask

AsyncTask是一个轻量级的异步使命类,它能够在线程池中履行异步使命然后把履行进展和履行成果传递给主线程并在主线程更新UI。从完成上来说AsyncTask封装了Thread和Handler,经过AsyncTask能够很便利的履行后台使命以及主线程中拜访UI,但是AsyncTask不适合处理耗时使命,耗时使命还是要交给线程池履行。

AsyncTask的四个中心类如下:

    • onPreExecute():首要用于做一些准备作业,在主线程中履行异步使命履行之前
    • doInBackground(Params … params):在线程池履行,此办法用于履行异步使命,params表示输入的参数,在此办法中能够经过publishProgress办法来更新使命进展,publishProgress会调用onProgressUpdate
    • onProgressUpdate(Progress .. value):在主线程履行,当使命履行进展产生改动时会调用这个办法
    • onPostExecute(Result result):在主线程履行,异步使命之后履行这个办法,result参数是返回值,即doInBackground的返回值。

2.2 AsyncTask的作业原理

2.3 HandleThread

HandleThread承继自Thread,它是一种能够运用Handler的Thread,它的完成在run办法中调用Looper.prepare()来创立音讯行列然后经过Looper.loop()来敞开音讯循环,这样在实际运用中就能够在HandleThread中创立Handler了。

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

HandleThread和Thread的区别是什么?

    • Thread的run办法中首要是用来履行一个耗时使命;
    • HandleThread在内部创立了一个音讯行列需要经过Handler的音讯办法来告诉HandleThread履行一个详细的使命,HandlerThread的run办法是一个无限循环因而在不运用是调用quit或许quitSafely办法中止线程的履行。HandleTread的详细运用场景是IntentService。

2.4 IntentService

IntentService承继自Service并且是一个笼统的类因而运用它时就必须创立它的子类,IntentService可用于履行后台耗时的使命,当使命履行结束后就会主动中止。IntentService是一个服务因而它的优先级要比线程高并且不容易被体系杀死,因而能够使用这个特色履行一些高优先级的后台使命,它的完成首要是HandlerThread和Handler,这点能够从onCreate办法中了解。

//IntentService#onCreate
@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目标ServiceHandler,这样经过mServiceHandler把音讯发送到HandlerThread中履行。每次发动IntentService都会调用onStartCommand,IntentService在onStartCommand中会处理每个后台使命的Intent。

//IntentService#onStartCommand
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
//IntentService#onStart
@Override
public void onStart(@Nullable 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是如何处理外界的Intent的?

在onStartCommand办法中进入了onStart办法,在这个办法中IntentService经过mserviceHandler发送了一条音讯,然后这个音讯会在HandlerThread中被处理。mServiceHandler接收到音讯后会把intent传递给onHandlerIntent(),这个intent跟发动IntentService时的startService中的intent是相同的,因而能够经过这个intent解析出发动IntentService传递的参数是什么然后经过这些参数就能够区别详细的后台使命,这样onHandleIntent就能够对不同的后台使命做处理了。当onHandleIntent办法履行结束后IntentService就会经过stopSelf(int startId)办法来尝试中止服务,这里不必stopSelf()的原因是因为这个办法被调用之后会立即中止服务但是这个时分可能还有其他音讯未处理结束,而选用stopSelf(int startId)办法则会等候一切音讯都处理结束后才会中止服务。调用stopSelf(int startId)中止服务时会根据startId判别最近发动的服务的startId是否持平,持平则立即中止服务不然不中止服务。

每履行一个后台使命就会发动一次intentService,而IntentService内部则经过音讯的办法向HandlerThread恳求履行使命,Handler中的Looper是顺序处理音讯的,这就意味着IntentService也是顺序履行后台使命的,当有多个后台使命一起存在时这些后台使命会按照外界发起的顺序排队履行。

3.Android中的线程池

线程池的优点:

    • 线程池中的线程可重复运用,防止因为线程的创立和销毁带来的功能开支;
    • 能有效操控线程池中的最大并发数防止很多的线程之间因互相抢占体系资源导致的堵塞现象;
    • 能够对线程进行简单的管理并供给定时履行以及指定距离循环履行等功能。

Android的线程池的概念来自于Java中的Executor,Executor是一个接口,真正的线程的完成是ThreadPoolExecutor,它供给了一些列参数来装备线程池,经过不同的参数能够创立不同的线程池。

3.1 ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

ThreadPoolExecutor是线程池的真正完成,它的结构函数中供给了一系列参数,先看一下每个参数的意义:

    • corePoolSize:线程池的中心线程数,默许情况下中心线程会在线程池中一向存活即便他们处于搁置状况。假如将ThreadPoolExecutor的allowCoreThreadTimeOut置为true那么搁置的中心线程在等候新的使命到来时会有超时策略,超时时刻由keepAliveTime指定,当等候时刻超越keepAliveTime设置的时刻后中心线程就会被中止。
    • maxinumPoolSize:线程池中所能容纳的最大线程数,当活动线程到达做大数量时后续的新使命就会被堵塞。
    • keepAliveTime:非中心线程搁置时的超时时长,超越这个时长非中心线程就会被收回。
    • unit:用于指定超时时刻的单位,常用单位有毫秒、秒、分钟等。
    • workQueue:线程池中的使命行列,经过线程池中的execute办法提交的Runnable目标会存储在这个参数中。
    • threadFactory:线程工厂,为线程池供给创立新的线程的功能。
    • handler:这个参数不常用,当线程池无法履行新的使命时,这可能是因为使命行列已满或许无法成功履行使命,这个时分ThreadPoolExecutor会调用handler的rejectExecution办法来告诉调用者。

ThreadPoolExecutor履行使命时大致遵从如下规矩:

    1. 假如线程池中的线程数量没有到达中心线程的数量那么会直接发动一个中心线程来履行使命;
    2. 假如线程池中线程数量已经到达或许超越中心线程的数量那么会把后续的使命刺进到行列中等候履行;
    3. 假如使命行列也无法刺进那么在根本能够确定是行列已满这时假如线程池中的线程数量没有到达最大值就会马上创立非中心线程来履行使命;
    4. 假如非中心线程的创立已经到达或许超越线程池的最大数量那么就拒绝履行此使命,一起ThreadPoolExecutor会经过RejectedExecutionHandler抛出异常rejectedExecution。

3.2线程池的分类

  • FixedThreadPool:它是一种数量固定的线程池,当线程处于闲暇状况时也不会被收回,除非线程池被封闭。当一切的线程都处于活动状况时,新使命都会处于等候状况,直到有闲暇线程出来。FixedThreadPool只要中心线程并且不会被收回因而它能够更加快速的呼应外界的恳求。
  • CacheThreadPool:它是一种线程数量不定的线程池且只要非中心线程,线程的最大数量是Integer.MAX_VALUE,当线程池中的线程都处于活动状况时假如有新的使命进来就会创立一个新的线程去履行使命,一起它还有超时机制,当一个线程搁置超越60秒时就会被收回。
  • ScheduleThreadPool:它是一种拥有固定数量的中心线程和不固定数量的非中心线程的线程池,当非中心线程搁置时会立即被收回。
  • SignleThreadExecutor:它是一种只要一个中心线程的线程池,一切使命都在同一个线程中按顺序履行。