Android 中的线程

一、Android线程的基本介绍

       线程在Android中是一个很重要的概念,从用途上来说,Android中的线程可以分为主线程和子线程,主线程主要用来处理和界面相关的事,比如界面绘制和响应用户的操作,为了确保用户体验,主线程必须确保其响应速度,所有任何时候我们都不应该在主线程中处理非常耗时的任务,否则会造成界面卡顿甚至ANR;而子线程的作用就是完成耗时的操作,确保主线程的响应速度。主线程和子线程之间的通信是基于Handler机制,对于Handler机制可以参考:https://blog.csdn.net/xiao_nian/article/details/81011361。

1、Android中常见的线程形态基本介绍

        除了Thread本身,在Android中可以扮演线程角色的还有很多,比如AsyncTask、IntentSevice、HandlerThread等,尽管他们的表现形式有别于传统的线程,但他们的本质还是Thread,只不过结合了一些其他的功能,让它们适用于不同的应用场景。AsyncTask封装了线程池和Handler,它主要是为了开发者在使用子线程中能够方便的更新UI;HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。IntentService内部采用HanderThread来执行任务,当任务执行完毕后IntentService会自动退出。

2、Android线程池的基本介绍

       在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销。如果一个进程需要频繁的创建子线程来执行任务,而每执行一次任务都需要重新创建和销毁线程,这显然不是高效的做法。正确的做法是采用线程池,一个线程池中会缓存一定数量的线程,当我们创建线程后会将线程存入到线程池中,那么当下次在需要使用线程时,就不用重复创建线程,而是直接从线程池中取出,这样就节省了频繁创建线程和销毁线程的开销。

二、Android中常见的线程形态

1、AsyncTask

        AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把任务的执行进度和最终执行结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Hander,并且采用了线程池的机制。

1.1 AyncTask的基本用法

public class AsyncTaskTestActivity extends AppCompatActivity {
    private ImageView mImageView;
    private Button mButton;
    private ProgressDialog mProgressDialog;
    private DownloadImageAsyncTask mDownloadImageAsyncTask; // 不能声明为AsyncTask,否则会报类型转换错误

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task_test);
        initView();
    }

    private void initView() {
        mImageView = (ImageView) findViewById(R.id.imageView);
        mButton = (Button) findViewById(R.id.button);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String imagePath = "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1641460231,985790943&fm=27&gp=0.jpg";
                startAsyncTask(imagePath);
            }
        });
    }

    private void startAsyncTask(String imagePath) {
        if (mDownloadImageAsyncTask != null && !mDownloadImageAsyncTask.isCancelled()) {
            mDownloadImageAsyncTask.cancel(true);
        }
        mDownloadImageAsyncTask = new DownloadImageAsyncTask();
        mDownloadImageAsyncTask.execute(imagePath);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 在界面销毁后取消异步任务,以免发生内存泄露
        if (mDownloadImageAsyncTask != null && !mDownloadImageAsyncTask.isCancelled()) {
            mDownloadImageAsyncTask.cancel(true);
            mDownloadImageAsyncTask = null;
        }
    }

    public void showProgressDialog() {
        if (mProgressDialog == null) {
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setTitle("提示信息");
            mProgressDialog.setMessage("正在下载中,请稍后......");
            // 设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
            mProgressDialog.setCancelable(false);
            // 设置ProgressDialog样式为水平的样式
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        }
        mProgressDialog.show();
    }

    public void setLoadProgress(int progress) {
        if (mProgressDialog != null) {
            mProgressDialog.setProgress(progress);
        }
    }

    public void dimissProgressDialog() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
    }

    public void downloadImageSuccessCallBack(byte[] bytes) {
        if (bytes != null && mImageView != null) {
            Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
            mImageView.setImageBitmap(bitmap);
        }
        dimissProgressDialog();
    }

    /**
     * 下载任务
     */
    public class DownloadImageAsyncTask extends AsyncTask {
        // 在UI线程中执行,任务开始前需要进行的UI操作,比如弹出加载框
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            showProgressDialog();
        }

        // 在异步线程中执行耗时任务,比如请求网络数据
        @Override
        protected byte[] doInBackground(String... params) {
            //    通过Apache的HttpClient来访问请求网络中的一张图片
            HttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(params[0]);
            byte[] image = new byte[]{};
            try
            {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                InputStream inputStream = null;
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                if(httpEntity != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                    //    得到文件的总长度
                    long file_length = httpEntity.getContentLength();
                    //    每次读取后累加的长度
                    long total_length = 0;
                    int length = 0;
                    //    每次读取1024个字节
                    byte[] data = new byte[1024];
                    inputStream = httpEntity.getContent();
                    while(-1 != (length = inputStream.read(data)))
                    {
                        //    每读一次,就将total_length累加起来
                        total_length += length;
                        //    边读边写到ByteArrayOutputStream当中
                        byteArrayOutputStream.write(data, 0, length);
                        //    得到当前图片下载的进度
                        int progress = ((int)(total_length/(float)file_length) * 100);
                        //    时刻将当前进度更新给onProgressUpdate方法
                        publishProgress(progress);
                        if (isCancelled()) {
                            break;
                        }
                    }
                }
                image = byteArrayOutputStream.toByteArray();
                inputStream.close();
                byteArrayOutputStream.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                httpClient.getConnectionManager().shutdown();
            }
            return image;
        }

        // 任务取消时调用
        @Override
        protected void onCancelled() {
            super.onCancelled();
            dimissProgressDialog();
        }

        // 在UI线程中执行,在异步线程中调用publishProgress(Progress)后会立即回调onPregressUpdate方法,可以在该方法中更新UI界面上的任务执行进度
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            setLoadProgress(values[0]);
        }

        // 任务执行完毕调用该方法,在UI线程中执行,更新数据到UI界面
        @Override
        protected void onPostExecute(byte[] bytes) {
            super.onPostExecute(bytes);
            downloadImageSuccessCallBack(bytes);
        }
    }
}

上面的代码是AsyncTask典型的用法,通过AsyncTask下载了一张图片,下载完成后将图片显示在界面上;AsyncTask是一个抽象的泛型类,它提供了Params、Progress和Result三个泛型参数,分别表示传入参数的类型、任务执行进度的类型和任务返回结果的类型,如果不需要传递参数,那么上面三个泛型参数可以用void代替;AsyncTask提供了四个核心的方法,它们的含义如下:

onPreExecute():在主线程中执行,异步任务开始之前会执行该方法,一般用来做一些准备工作,比如界面上弹出加载框;

doInBackground(Params... params):在线程池中的线程中执行,该方法用来执行异步任务,params表示异步任务的输入参数,在该方法中可以通过调用publicProgress(Progress... values)来更新任务的执行进度,publicProgress(Progress... values)方法会触发调用onProgressUpdate(Progress... values)方法;

onProgressUpdate(Progress... values):在主线程中执行,用来更新任务的执行进度;

onPostExecute(Result result):在主线程中执行,该方法是异步任务执行完毕后的回调方法,result参数表示异步任务的返回结果。

除了上面四个方法外,还有一个比较重要的方法是onCancelled()方法,它同样是在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用,这个时候onPostExecute方法不会被调用。

1.2 AsyncTask源码分析

上面介绍了AsyncTask的基本用法,接下来我们分析一下AsyncTask的源码:

我们通过调用execute方法来让AsyncTask开始工作,代码如下:

    @MainThread
    public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    @MainThread
    public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) { // 每个AsyncTask只能执行一次
            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 "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute(); // 调用onPreExecute()方法

        mWorker.mParams = params;
        exec.execute(mFuture); // 执行异步任务

        return this;
    }

在上面的代码中,我们可以知道AsyncTask的execute必须在UI线程中执行,并且每个AsyncTask只能执行一次,在执行异步任务之前,首先会先调用onPreExecute()方法,然后将参数保存在“mWorker”的参数中,最终执行异步任务的代码是“exec.execute(mFuture);”,其中“exec”如果没有指定,默认是“mDefaultExecutor”,“mDefaultExecutor”的定义如下:

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    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) { // 从队列中取出任务并执行,如果队列为空,则mActive也会为空
                THREAD_POOL_EXECUTOR.execute(mActive); // 真正执行任务的线程池
            }
        }
    }

“exec.execute(mFuture);”最终会调用SerialExecutor的execute方法,将任务存入到队列中,然后会触发线程池执行任务,从上面也可以看出在一个进程中所有AysncTask的异步任务默认是串行执行的,当第一次调用execute方法时,mActive默认为空,所以会执行scheduleNext()方法,开始执行异步任务,这之后再次调用execute方法时,只要还有任务在执行,mActive就不为空了,也就不会调用scheduleNext,而只会将任务加入到队列中,当当前任务执行完毕后,会再次调用scheduleNext()方法执行下一个任务,如果此时队列中没有了任务,会将mActive置为空,当下一个任务来临后,也就会执行scheduleNext()方法继续执行任务。

那么有没有什么办法可以让AsyncTask的任务并行执行呢?答案是肯定的,AysncTask提供了executeOnExecutor(Executor exec, Params... params)接口,可以指定执行异步任务的Executor,并且AysncTask提供了一个public static类型的线程池对象,定义如下:


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

根据代码,我们知道THREAD_POOL_EXECUTOR是一个线程池对象,其根据CPU的个数配置了线程池的核心线程数和最大线程数,我们可以指定异步任务的执行线程池为THREAD_POOL_EXECUTOR,这样就能做到并行执行了(当然,如果任务很多,超过了线程池的最大线程数,任务还是会排队执行)。

我们再来看一下传入的参数“mFuture”,它是在AsyncTask的构造函数中初始化的:

        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); // 调用doInBackground方法执行异步任务
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result); // 将异步任务的返回结果发送给主线程
                }
                return result;
            }
        };

        mFuture = new FutureTask(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

FutureTask类是Future 的一个实现,并实现了Runnable,所以可通过Excutor(线程池) 来执行,下面看一下它的核心方法run方法:

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable c = callable; // 获得callable对象
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call(); // 调用callable对象的call方法执行任务并获得返回结果
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

可以看到FutureTask的run方法最终是调用了其Callable对象的call()方法来执行任务,由此也可以看出,AsyncTask的异步任务的执行是在其WorkerRunnable对象的call()方法中执行的,而call()方法中又调用了AsyncTask的doInBackground方法来执行异步任务,执行完毕后会调用postResult方法将返回结果发送给主线程,下面我们来看一下postResult的源码:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result)); // 构造一个Message,将消息发送给主线程
        message.sendToTarget();
        return result;
    }

上面的代码会构造一个Message,消息类型为MESSAGE_POST_RESULT,并且包含了一个AsyncTaskResult对象,这个对象中封装了当前AsyncTask对象和返回结果对象。继续看getHandler的代码:

    private static Handler getHandler() {
        synchronized (AsyncTask.class) { // 使用线程锁的单例模式构造Handler对象
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

getHandler使用线程锁的单例模式构造了一个InternalHander对象,InternalHander定义如下:

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper()); // 使用主线程的Looper来创建Handler,也就是说这个Hander的所有消息都会在主线程中处理
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult result = (AsyncTaskResult) msg.obj; // 取出AsyncTaskResult对象
            switch (msg.what) {
                case MESSAGE_POST_RESULT: // 返回结果的消息类型
                    // There is only one result
                    result.mTask.finish(result.mData[0]); // 调用AsyncTask的finish方法处理返回结果
                    break;
                case MESSAGE_POST_PROGRESS: // 更新进度的消息类型
                    result.mTask.onProgressUpdate(result.mData); // 调用AsyncTask的onProgressUpdate更新进度
                    break;
            }
        }
    }

InternalHandler使用主线程的Looper来创建Handler,这就确保了InternalHandler的所有消息都会在主线程中处理,在handlerMessage中处理了两种消息,一种是返回结果,一种是更新任务进度,而更新任务进度调用的是AsyncTask的onProgressUpdate方法,所有我们在自定义的AsyncTask中通过重写onProgressUpdate方法来告知用户任务进度;我们再来看一下AsyncTask的finish方法:

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result); // 如果任务已经取消,则调用onCancelled方法
        } else {
            onPostExecute(result); // 任务没取消,则调用onPostExecute方法处理任务返回结果
        }
        mStatus = Status.FINISHED;
    }

可以看到,如果任务取消了,AsyncTask会调用onCancelled方法处理任务;如果没有取消,则会调用onPostExecute方法来处理任务的返回结果。

之前我们说过,我们会在doInBackground方法中调用publishProgress方法来触发更新任务进度,我们可以猜测一下,publicProgress方法应该是利用InternalHandler向主线程发送一个MESSAGE_POST_PROGRESS的消息,然后在主线程中就会调用AsyncTask的onProgressUpdate方法来更新任务进度了,查看publicProgress的源码:

    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult(this, values)).sendToTarget();
        }
    }

上面的代码也验证了我们的猜想。

到此,AsyncTask的代码也就分析完了,总结一下,AsyncTask是一个用来执行异步任务的类,通过它可以轻松的在子线程中执行异步任务,执行完之后将处理返回结果的代码在主线程中执行;AsyncTask内部的原理主要使用到了Handler和线程池,Handler用来切换任务执行的线程,而线程池用来执行异步任务;其中还有线程池方面的知识,我们后面再分析。

2、 HandlerThread

HandlerThread继承了Thread,它是一种可以使用Handler的Thread,其实现原理很简单,主要是其内部维护了自己的Looper对象,这个Looper对象在run方法中会开启循环,下面来看一下其基本用法:

        HandlerThread handlerThread = new HandlerThread("HandlerThreadTest"); // 构造一个HandlerThread对象
        handlerThread.start(); // 启动线程
        Handler handler = new Handler(handlerThread.getLooper()) { // 用HandlerThread的Looper构造一个Handler对象,也就是说这个Handler的所有消息都会通过HandlerThread的Looper循环处理
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == 1) {
                    Log.i("aaaaaaa", "HandlerThread handlerMessage");
                }
            }
        };
        Message message = Message.obtain();
        message.what = 1;
        handler.sendMessage(message); // 发送一个消息

首先我们构造了一个HandlerThread的对象,并且调用其start方法启动线程,然后利用HandlerThread的Looper构造了一个Handler对象,之后就可以用这个Handler对象向HandlerThread发送消息了,消息的都会在HandlerThread线程中处理。HandlerThread的源码如下:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper; // HandlerThread内部维护的Looper对象

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() { // 正式开始Looper循环之前的回调方法,可以在该方法中做一些准备工作
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare(); // 为当前线程创建Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared(); // 在正式开始循环之前调用onLooperPrepared方法
        Looper.loop(); // 开启Looper循环
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() { // 获得当前线程的Looper对象
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * Quits the handler thread's looper.
     * 

* Causes the handler thread's looper to terminate without processing any * more messages in the message queue. *

* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

* Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. *

* * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */ public boolean quit() { // 调用Looper的quit来退出Looper循环,一旦Looper循环退出,run方法也就结束了,当前线程也就结束了 Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } /** * Quits the handler thread's looper safely. *

* Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. *

* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

* If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. *

* * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. */ public boolean quitSafely() {// 调用Looper的quitSafely来退出Looper循环,一旦Looper循环退出,run方法也就结束了,当前线程也就结束了 Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } /** * Returns the identifier of this thread. See Process.myTid(). */ public int getThreadId() { return mTid; } }

HandlerThread的源码很简单,相比于普通的线程,其内部维护了一个Looper对象,并且会在run方法中开启循环,其他线程可以通过它的Looper对象来构造Handler,通过Handler向HandlerThread发送消息,这样就将任务切换到了HandlerThread线程中处理;同时,HandlerThread也提供了一下退出的方法,其退出方法其实就是调用Looper对象的退出方法来结束Looper循环,一旦Looper循环结束,run方法也就结束了,线程退出。

3、 IntentService

IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用IntentService。IntentService可用于执行后台耗时的任务,当任务执行完后它会自动停止,同时由于IntentService是服务的原因,这就导致它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它高优先级不容易被系统杀死。IntentService相比于传统的Service,其任务的执行是在子线程中执行的,而传统的Service是运行在主线程中的,当然,我们也可以在Service中开启一个子线程来执行任务,但是如果使用IntentService,我们就省去了维护子线程的麻烦,而且IntentService会在任务结束后自动停止服务;从实现上来说,IntentService封装了HandlerThread和Handler。我们先来看一下它的基本用法:

和普通的Service一样,首先要在AndroidManifest文件中配置服务:

        

然后定义MyIntentService类:

public class MyIntentService extends IntentService {
    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) { // IntentService的任务处理方法
        Log.i("liunianprint:", "run thread " + Thread.currentThread().getId()); // 打印运行的这段代码的线程
    }
}

最后启动IntentService即可:

    Intent intent = new Intent(IntentServiceActivity.this, MyIntentService.class);
    startService(intent);

启动方式和启动普通Service一样。

运行程序,我们发现onHandleIntent方法的处理确实是在子线程中处理的。下面我们分析一下IntentService的源码实现:

public abstract class IntentService extends Service { // 继承与Service,这也确定了IntentService首先是一个Service
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

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

    public IntentService(String name) {
        super();
        mName = name;
    }


    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

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

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


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

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }


    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

代码不多,首先IntentService继承了Service,说明其是一个服务,在启动服务后,首先会调用服务的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 + "]"); // 创建一个HandlerThread线程
        thread.start(); // 启动HandlerThread线程,等待消息处理

        mServiceLooper = thread.getLooper(); // 获得HandlerThread的Looper对象
        mServiceHandler = new ServiceHandler(mServiceLooper); // 用HandlerThread的Looper对象来创建ServiceHandler对象
    }

在onCreate方法中,首先会创建一个HandlerThread线程并启动,然后在利用HandlerThread的Looper来构造ServiceHandler对象,从之前HandlerThread的源码中,我们知道HandlerThread线程一旦启动就会启动Looper循环,等待消息的到来并处理,我们再来看一下ServiceHandler的源码:

    private final class ServiceHandler extends Handler { // 继承Handler
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) { // 处理消息方法,该方法会在HandlerThread线程中调用
            onHandleIntent((Intent)msg.obj); // 调用onHandleIntent方法处理消息
            stopSelf(msg.arg1); // 停止服务,这里会根据传过来的startId来判断是否停止服务
        }
    }

ServiceHandler继承与Handler,由于它是使用上面HandlerThread的Looper对象构造的,所有其handleMessage方法也会在HandlerThread线程中调用,在handlerMessage方法中,首先会调用IntentService的onHandleIntent方法处理消息,消息处理完毕后会调用stopSelf来停止IntentService。我们知道,在onCreate方法调用完成后,Service会调用onStartCommand方法来运行服务,IntentService的onStartCommand定义如下:

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage(); // 构造一个消息
        msg.arg1 = startId; // 传入startId值
        msg.obj = intent; // 传入intent对象
        mServiceHandler.sendMessage(msg); // 发送消息
    }


    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId); // 调用onStart方法
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

从上面的代码可以看出,onStart方法其实就是构造了一个消息,消息里面包含了Intent对象和当前调用onStart的startId,然后利用ServiceHandler对象向之前创建的HandlerThread线程发送消息,这样,在HandlerThread线程中就可以出来消息了,也就是会调用到ServiceHandler的handleMessage方法。这里注意一点,在handleMessage中调用stopSelf方法停止服务时,传入了startId,每次调用startService时,系统都会调用onStart方法,但是startId会递增,调用stopSelf(int startId)方法来停止服务时,会以最近的请求的startId为标准,也就是说,系统在回调stopSelf(startId)时,会用传入的startId和最近的请求的startId相比较,如果相同,则退出Service,否则,Service不会被停止,这样确保了IntentService的最近任务是肯定能够执行的。

好了,IntentService的源码也分析完了,总结一下,IntentService本质上是一个Service,其目的是为了减去普通Service对子线程的维护;IntentService适合执行在后台的耗时任务,因为它本质上是Service,相比于普通线程,所有不容易被系统杀死。IntentService的内部实现原理是Service、HandlerThread和Handler,其中,继承Service让其有了Service的功能,HandlerThread用来执行异步的耗时任务,Handler用来发送消息给HandlerThread,触发HandlerThread执行任务。

三、线程池

这部分我们来了解一下线程池的知识,AsyncTask最终就是通过线程池来执行异步任务的,提到线程池,我们必须先说一下使用线程池的好处:

1、重用线程池中的线程,避免了频繁创建和销毁线程所带来的开销;

2、能够有效的控制线程的最大并发数,避免了大量的线程之间因为相互抢占系统资源而导致的阻塞;

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

总的来说,线程池是系统为我们提供的管理线程的模块,通过它我们可以方便的控制项目中的线程。Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池。通过配置参数我们可以获得不同功能的线程池。

3.1 ThreadPoolExecutor

ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,下面我们来看一下ThreadPoolExecutor的构造方法中各个参数的含义,这些参数将直接影响到线程池的功能特性:

    public ThreadPoolExecutor(int corePoolSize, // 线程池核心线程个数
                              int maximumPoolSize, // 线程池最大线程个数
                              long keepAliveTime, // 非核心线程闲置时的超时时长
                              TimeUnit unit, // 非核心线程闲置的超时时长的时间单位
                              BlockingQueue 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;
    }

corePoolSize:线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态;如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的线程在等待新任务到来时也会有超时策略,这个时间有kiipAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止。

maximumPoolSize:线程池所能容纳的最大线程个数,当任务队列放不下后,会立刻启动非核心线程执行任务,当线程数量已经达到了线程池规定的最大个数,那么就拒绝执行此任务,线程池会调用RejectedExecutionHandler的rejectedExecution方法来处理异常情况。

keepAliveTime:非核心线程闲置时的超时时长,超过这个时长后,非核心线程将会被回收;当allowCoreThreadTimeOut设置为true后,这个参数对核心线程也同样有效。

unit:用于指定keepAliveTime的时间单位。

workQueue:线程池中的任务队列,用来存储线程池将要执行的任务。

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

handler:任务异常处理,当线程池无法执行新任务时,就会调用这个参数来处理异常情况,RejectedExecutionHandler是一个接口,它只有一个方法:void rejectedExecution(Runnable r, ThreadPoolExecutor executor);导致无法处理新任务的额原因可能是由于任务队列已满并且线程数量也达到了线程池规定的最大个数,新的任务过来后无法存入到任务队列中,这个时候线程池会调用handler的rejectedExecution方法来通知调用者。

 

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

(1)如果线程池中的线程数量未达到核心线程的数量,那么会直接创建一个核心线程来执行任务;

(2)如果线程池中的线程数量已经超过核心线程的数量,那么会将任务插入到任务队列中排队等待执行;

(3)如果在步骤2中无法将任务插入到任务队列中,这往往是因为任务队列已满,这个时候如果线程数量没有达到线程池规定的最大个数,则会立即启动一个非核心线程来处理任务;

(4)如果在步骤3中线程数量已经达到了线程池规定的最大个数,那么就拒绝执行此任务,线程池会调用RejectedExecutionHandler的rejectedExecution方法来处理异常情况。

 

下面我们看一下AsyncTask中使用的线程池的配置

AsyncTask中异步任务的执行代码如下:

    THREAD_POOL_EXECUTOR.execute(mActive);

THREAD_POOL_EXECUTOR的定义:

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

可以看到,AsyncTask是在静态代码块初始化线程池的,其中参数的定义如下:

    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); // 核心线程个数和CPU的核数相关
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; // 最大线程数为CPU核数的2倍+1
    private static final int KEEP_ALIVE_SECONDS = 30; // 线程的超时时间为30秒
    private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue(128); // 线程池的任务队列,容量为128
    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());
        }
    };

3.2 线程池的分类

前面我们已经提到,根据对ThreadPoolExecutor配置不同的参数,其生成的线程池的功能特性也不一样。下面介绍Android中四类最常见的线程池,它们都直接或者间接的通过配置ThreadPoolExecutor的参数来实现自己的功能特性,这四类线程池分别是FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor。

FixedThreadPool

FixedThreadPool是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池关闭了。它可以通过Executors的newFixedThreadPool来创建:

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

通过观察上面的参数,我们可以知道,FixedThreadPool的最大核心线程数等于最大线程数,这也就意味着其没有非核心线程,任务队列没有设置最大容量,表示任务队列没有大小限制。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这就意味着它能够更加快的响应外界的请求,当核心线程都处于活动状态时,任务将会保存在任务队列中等待处理。

CachedThreadPool

CachedThreadPool是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为Integer.MAX_VALUE,也就是不限制线程的最大个数。它可以通过Executors的newCachedThreadPool来创建:

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

观察上面的参数,我们发现CachedThreadPool的最大核心线程数为0,并且不限制最大线程数,线程空闲超时时间为60秒,任务队列为SynchronousQueue,SynchronousQueue是一个非常特殊的队列,它其实是无法存储任务的,当然,CachedThreadPool的最大线程数是不限的,所以每个任务都会立即执行。从CachedThreadPool的特性来看,这类线程池比较适合执行大量的耗时较少的任务。

ScheduledThreadPool

ScheduledThreadPool的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会立刻被回收。它可以通过Executors的newScheduledThreadPool方法来创建:

    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue(), threadFactory);
    }

ScheduledThreadPool主要用于执行定时任务和具有固定周期的重复任务。

SingleThreadExecutor

SingleThreadExecutor内部只有一个核心线程,它确保了所有任务都在同一个线程中按顺序执行。它可以通过Executors的newSingleThreadExecutor来创建:

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

当有任务到来时,如果唯一的一个核心线程处于工作状态,则任务将会存入到队列中等待执行;SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,使这些任务之间不需要处理线程同步的问题。

除了上面的四种常见的线程池,我们还可以来配置符合自己需求的线程池。

你可能感兴趣的:(android)