【读书笔记】【Android 开发艺术探索】第11章Android 的线程和线程池

  在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不肯能无限制地产生,并且线程的创建和销毁都会有相应的开销。

       Android 的线程分主线程和子线程。主线程主要处理和界面相关的工作,子线程,也被称为工作线程,执行耗时工作。

       Android 中的线程形态除了 Thread 外,还包含 AsyncTask、Handler 以及 IntentService, 它们在底层实现也是线程。

     

      一、AsyncTask

         1、   AsyncTask 是轻量级的异步任务,是一个抽象的泛型类,内部使用封装了 Thread 和 Handler ,使用了线程池执行了后台任务,然后把执行的进度和最终的结果传递会主线程,在主线程中更新 UI.

           AsyncTask 不适合进行特别耗时的后台任务,对于特别耗时的任务尽量使用线程池。      

    /**
     *   AsyncTask 是一个泛型的抽象类
     * @param <Params>  输入的参数类型
     * @param <Progress>  后台任务的执行进度类型
     * @param <Result>      后台任务的返回结果类型
     */
    public abstract class AsyncTask<Params, Progress, Result>

      2、AsyncTask 的四个核心方法    

    /**
     *   在主线程中执行,在异步任务执行之前,此方法会被调用,用于做一些准备工作
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    /**
     *   在线程池中执行,此方法用于执行异步任务,在此方法中通过 publishProgress 方法来更新任务的进度,
     *    publishProgress 方法会调用 onProgressUpdate 方法。
     *    同时此方法返回结算结果给 onPostExecute 方法
     * @param params 表示输入的参数
     * @return
     */
    @Override
    protected Params  doInBackground(Params... params) {

    }

    /**
     *   在主线程中执行,当后台任务的执行进度发生变化时,此方法会被调用
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    /**
     *  在主线程中执行,在异步任务执行之后,此方法会执行。
     *   如果 onCancelled() 方法调用之后,该方法则不会执行
     * @param result  后台任务的返回值,即 doInBackground 的返回值。
     */
    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
    }

             

     3、AsyncTask 在具体使用中一些条件限制

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

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

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

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

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

         (6) 在 Android 1.6 之前,AsyncTask 是串行执行任务, Android 1.6 的时候 AsyncTask 采用线程池处理并行任务。在 Android 3.0 开发,AsyncTask 又采用了一个线程来串行执行任务。 如果现在 Android 3.0 以后并行执行任务,可通过 executeOnExecutor 方法来实现。


     4、AsyncTask 的工作原理

      以 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 "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        //  这里调用了 AsyncTask 的 onPreExecute 方法, onPreExecute 方法 是一个空方法,需要我们自己在
        // 实现自己的 AsyncTask 中重写改方法,做一些准备工作
        //  在主线程中执行
        onPreExecute();

        mWorker.mParams = params;

        // 把任务交给线程池 sDefaultExecutor 执行
        //  mFuture 是 FutureTask 对象,FutureTask 是一个并发类, 在这里充当 Runnable 作用
        exec.execute(mFuture);

        return this;
    }
      

        sDefaultExecutor 是一个串行的线程池,一个进程中所有的AsyncTask 全部在这个串行的线程池中排队执行。

    AsyncTask 中有 SerialExecutor 和 THREAD_POOL_EXECUTOR 两个线程池,SerialExecutor 用于任务的排队,

THREAD_POOL_EXECUTOR 用于真正执行任务。

  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) {
            // 将mFuture 即 r  插入到任务队列 mTasks 中
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            // 如果没有没有正在活动的任务,则会执行下一个任务, 这样保证了所有的任务时一个一个执行
            // 所以,在默认情况下,AsyncTask 是串行执行的
            if (mActive == null) {
                scheduleNext();
            }
        }

        // 从任务队列中取出一个任务,如果这个任务不为空,则交给线程池执行
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
             // 真正执行任务
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

       THREAD_POOL_EXECUTOR 线程池执行的任务,是传进来的mFuture. mFuture 是 FutureTask 的一个对象,FutureTask 是一个并发类,在这里充当 Runnable 的作用。当 FutureTask 执行的时候会调用自身的 run() 方法,在run() 方法中调用了传进来的 Callable 对象的 call() 方法,即在这里的 mWoker.call() 方法        

 // mFuture 和 mWoker 是在 AsyncTask 创建时就已经创建了
    mFuture = new FutureTask<Result>(mWorker);

    public void run() {
      ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {

                  // 执行传进来的 Callable 对象的 call 方法
                    //  这里我们传进去的是 mWorker
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
    ...
    }

    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            //  这里执行 AsyncTask 的 doInBackground 方法, 返回执行的结果;
            // 这是一个空方法,需要我们在实现自己的 AsyncTask 对象时重新的;
            // 注意,因为我们把 mWoker 传进了 mFuture 中,而 mFuture 是在线程池中执行的,所以,
            //  这里是在线程中,而不是在主线程中。
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            // 把 doInBackground 执行后返回的结果,传进 postResult() 方法中
            return postResult(result);
        }
    };
       在mWoker 的 call 方法中,将mTaskInvoked 设置为 true 表示当前任务已经被调用过,然后执行 AsyncTask 的 doInBackgroud 方法,接着将返回的值传递给 postResult(...) 方法

       

       因为 postResult(...) 方法也是在线程池中执行的,所以,通过 Handler 发送消息

 // postResult 方法通过 Handler 携带结果 result ,发送一个 MESSAG_POST_RESULT 消息
        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,  new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
      

               在 InternalHandler 的 handleMessage(...) 方法中处理消息

 //  InternalHandler 是一个静态内部类 ,因为静态成员会在加载类的时候会进行初始化话,  为了能够将执行环  境切换到主线程中,
        //  这就要求AsyncTask 的对象必须在主线程中加载。
        private static class InternalHandler extends Handler {

            public void handleMessage(Message msg) {
                AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
                switch (msg.what) {
                    case MESSAGE_POST_RESULT:
                        // There is only one result
                        result.mTask.finish(result.mData[0]);
                        break;
                    case MESSAGE_POST_PROGRESS:
                        // 会调用 AsyncTask 的 onProgressUpdate(...) 方法,主线程中调用
                        result.mTask.onProgressUpdate(result.mData);
                        break;
                }
            }
        }

        
      //  如果任务已经被取消,则执行 onCancelled(result) 方法,这亦是一个空方法,需要自己重写实现
       //  没有取消,则调用 onPostExecute(result) 方法, 整个过程结束。
        private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
               // 调用 AsyncTask 的 onPostExecute 方法,主线程中调用
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
         

             到此,AsyncTask 整个运行结束。

       

           5、AsyncTask 的使用例子          

/**  
 * 使用 AsyncTask 的一个例子,由于后台下载
 * Created by Administrator on 2016/1/23.
 */
public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    @Override
    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 null;
    }

    // 更新进程
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    // 最后在主线程中执行
    @Override
    protected void onPostExecute(Long aLong) {
        super.onPostExecute(aLong);
    }
}
     

    为了让 AsyncTask 在 Android3.0及版本以上并行,可以采用 AsyncTaskd 的 executeOnExecutor 方法

 

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
            new MyAsyncTask("AsyncTask#2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
            new MyAsyncTask("AsyncTask#3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
            new MyAsyncTask("AsyncTask#4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
        }

          

      二、HandlerThread        

HandlerThread 的定义,Handy class for starting a new thread that has a looper. The looper can then used to create handler classes.
           HandlerThread 本质上就是一个 Thread, 它继承 Thread.
           
           我们可以在主线程中直接使用 Handler ,因为主线程中已经有了默认的 Looper, 但是如果我们在子线程中使用必须要创建一个 Looper, 还要通过 Looper.loop() 开启消息循环。

new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "in startNewThread Thread id is " + Thread.currentThread().getId());
                Looper.prepare();
                Handler handler = new Handler();
                Looper.loop();
            }
        }).start();
否则会抛出异常

 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


    那有没有更简单的方法呢,那就用到 HandlerThread .
     HandlerThread 继承 Thread , 可以看它的 run 方法。
public void run() {
        mTid = Process.myTid();
        // 创建消息队列
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        // 开启消息循环
        Looper.loop();
        mTid = -1;
    }
         HandlerThread 在run() 方法中已经为我们提供了 Looper, 并开始消息队列。
         由于 HandlerThread 的 run 方法是一个无限循环,当不需要再使用 HandlerThread 时,可以通过使用它的 quit 或者 quitSafely 方法终止线程的执行。
         使用例子  

        mHandlerThread = new HandlerThread("new  handler");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                Log.i(TAG, " handle massage  msg " +  msg.what + "  in thread  " + Thread.currentThread().getId() );
            }
        };
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 释放资源
        mHandlerThread.quit();
    }


三、IntentService

          IntentService 是一种特殊的 Service , 它继承 Service 并且还是一个抽象类,必须创建它的子类才能使用IntentSercie.

IntentService 适用于非并发顺序处理后台任务,如果想要后台并发处理任务,还是得用 Service.

 
创建HandlerThread

<span style="font-size:18px;">    </span><span style="font-size:14px;">/**
     *  使用 HandlerThread 
     */
    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);
    }</span>

 

          发送消息

<span style="font-size:18px;">   </span><span style="font-size:14px;"> /**
     *   使用 mServiceHandler 发送消息,Handler 中 Looper 是顺序处理消息的。
     *   这里显示了为什么 IntentService 会顺序执行后台任务
     * @param intent  和 外界 startService(intent) 中的 intent 是完全一致的。
     * @param startId
     */
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }</span>
         

处理消息 

        当 onHandleIntent(...) 方法执行后,使用 stopSelf(int startId) 方法来尝试停止服务,而不是stopSelf() 方法。是因为 stopSelf() 会立即停止服务,而如果这时候可能还有消息未处理,stopSelf(int startid) 则会等待所有的消息处理完毕后才终止服务

  <span style="font-size:18px;">   </span><span style="font-size:14px;">/**
     *  处理发送过来的消息,然后停止 service
     *   onHandleIntent 是个抽象方法,子类自己实现。
     */
    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);
        }
    }</span>
 

         

         四、 Android 中的线程池

           使用线程池的优点

            。重用线程池中的线程,避免因线程的创建和销毁所带来的性能开销;

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


                  Android 线程池来源于 Java 中的 Executor,  Executor 只是接口,真正线程池的实现是 ThreadPoolExecutor


        1、关于 ThreadPoolExecutor

<span style="font-size:18px;">   </span><span style="font-size:14px;"> /**
     *   线程池的真正实现
     * @param corePoolSize  线程池的核心线程数
     *                      默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut
     *                      属性设置为 true, 那么闲置的核心线程在等待任务时会有超时策略,这个时间间隔由 keepAliveTime 所指定,等待时间超过 keepAliveTime
     *                      所指定的时长后,核心线程就会被终止。
     * @param maximumPoolSize   线程池所能容纳的最大线程数,当这个活动线程数达到这个数值后,后续的新任务将会被阻塞。
     * @param keepAliveTime 非核心线程闲置时的超时时长,超过这个时长,费核心线程就会被回收。
     *                      当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true 时, keepAliveTime 同样会作用于核心线程。
     * @param unit  指定 keepAliveTime 参数的时间单位
     *              常用有 TimeUnit.MILLISECONDS , TimeUnit.SECONDS 以及 TimeUnit.MINUTES
     * @param workQueue 线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个参数中。
     * @param threadFactory     线程工厂,为线程提供创建新线程的功能。 ThreadFactory 是一个接口,它只有一个方法 Thread newThread(Runnable r).
     */
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                              BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) 
</span>

             ThreadPoolExecutor 执行任务时的大致规则:

。1、如果线程池中的线程数量未达到核心线程的数量,会直接启动一个核心线程来执行任务;

。2、如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行;

。3、如果在 2 中因为任务队里已满,无法将任务插入到任务队列中,而此时如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程执行任务;

。4、如果步骤 3 中线程数量已经达到线程池规定的最大值,那么就会拒绝执行此任务,ThreadPoolExecutor 会调用 RejectExecutionHandler 的 rejectedExecution 方法来通知调用者, 抛出 RejectedExecution.


2、线程池的分类

A. Executors.newFixedThreadPool 

线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非关闭线程池。如果提交的任务多于线程数,就把任务插入到任务队列中。

B. Executors.newSingleThreadExecutor 

是一个退化了的大小为 1 的线程池。只有一个核心线程,它确保所有的任务都在同一线程中按顺序执行。

C. Executors.newCachedThreadPool

是一种线程数量不定的线程池,它只有非核心线程,最大线程数可以达到Integer.MAX_VALUE. 线程池中的空闲线程都有超时机制,时长为 60 秒,超过 60 秒闲置线程就会被回收。

适用于执行大量的耗时较少的任务。

D. Executors.newScheduledThreadPool 

核心线程固定,非核心线程数没有限制,当非核心线程闲置时会立即被回收。

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








你可能感兴趣的:(android,android,线程,读书笔记,开发艺术探索)