关于android:android多线程AsyncTask二

49次阅读

共计 11737 个字符,预计需要花费 30 分钟才能阅读完成。

  上篇剖析 AsyncTask 的一些根本用法以及不同 android 版本下的区别,接着本篇咱们就来全面分析一下 AsyncTask 的工作原理。在开始之前咱们先来理解一个多线程的知识点——Callable<V>、Future<V> 和 FutureTask 类

一、了解 Callable<V>、Future<V> 以及 FutureTask 类

Callable<V>

Callable 的接口定义如下:

public interface Callable<V> {V   call()   throws Exception;   
}   

  Callable 接口申明了一个名称为 call()的办法,该办法能够有返回值 V,也能够抛出异样。Callable 也是一个线程接口,它与 Runnable 的次要区别就是 Callable 在线程执行实现后能够有返回值而 Runnable 没有返回值,Runnable 接口申明如下:

public interface Runnable {public abstract void run();
}

  那么 Callable 接口如何应用呢,Callable 须要和 ExcutorService 联合应用,其中 ExecutorService 也是一个线程池对象继承自 Executor 接口,这里就不深刻了,接着看看 ExecutorService 提供了那些办法供咱们应用:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
  • submit(Callable task),传递一个实现 Callable 接口的工作,并且返回封装了异步计算结果的 Future。
  • submit(Runnable task, T result),传递一个实现 Runnable 接口的工作,并且指定了在调用 Future 的 get 办法时返回的 result 对象。
  • submit(Runnable task),传递一个实现 Runnable 接口的工作,并且返回封装了异步计算结果的 Future。

  因而咱们只有创立好咱们的线程对象(实现 Callable 接口或者 Runnable 接口),而后通过下面 3 个办法提交给线程池去执行即可。Callable 接口介绍就先到这,再来看看 Future 时什么鬼。

Future<V>

  Future 接口是用来获取异步计算结果的,说白了就是对具体的 Runnable 或者 Callable 对象工作执行的后果进行获取(get()), 勾销(cancel()), 判断是否实现等操作。其办法如下:

public interface Future<V> {
    // 勾销工作
    boolean cancel(boolean mayInterruptIfRunning);

    // 如果工作实现前被勾销,则返回 true。boolean isCancelled();

    // 如果工作执行完结,无论是失常完结或是中途勾销还是产生异样,都返回 true。boolean isDone();

    // 获取异步执行的后果,如果没有后果可用,此办法会阻塞直到异步计算实现。V get() throws InterruptedException, ExecutionException;

    // 获取异步执行后果,如果没有后果可用,此办法会阻塞,然而会有工夫限度,// 如果阻塞工夫超过设定的 timeout 工夫,该办法将返回 null。V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

}

总得来说 Future 有以下 3 点作用:

  • 可能中断执行中的工作
  • 判断工作是否执行实现
  • 获取工作执行实现后额后果。

  然而 Future 只是接口,咱们根本无法将其创立为对象,于官网又给咱们提供了其实现类 FutureTask,这里咱们要晓得后面两个接口的介绍都只为此类做铺垫,毕竟 AsncyTask 中应用到的对象是 FutureTask。

FutureTask

先来看看 FutureTask 的实现:

public class FutureTask<V> implements RunnableFuture<V> {

显然 FutureTask 类实现了 RunnableFuture 接口,咱们再看一下 RunnableFuture 接口的实现:

public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
}

  从接口实现能够看出,FutureTask 除了实现了 Future 接口外还实现了 Runnable 接口,因而 FutureTask 既能够当做 Future 对象也可是 Runnable 对象,当然 FutureTask 也就能够间接提交给线程池来执行。接着咱们最关怀的是如何创立 FutureTask 对象,实际上能够通过如下两个构造方法来构建 FutureTask

public FutureTask(Callable<V> callable) { }  
public FutureTask(Runnable runnable, V result) {}  

  从构造方法看出,咱们能够把一个实现了 Callable 或者 Runnable 的接口的对象封装成一个 FutureTask 对象,而后通过线程池去执行,那么具体如何应用呢?简略案例,CallableDemo.java 代码如下:


package com.zejian.Executor;
import java.util.concurrent.Callable;
/**
 * Callable 接口实例 计算累加值大小并返回
 */
public class CallableDemo implements Callable<Integer> {

    private int sum;
    @Override
    public Integer call() throws Exception {System.out.println("Callable 子线程开始计算啦!");
        Thread.sleep(2000);

        for(int i=0 ;i<5000;i++){sum=sum+i;}
        System.out.println("Callable 子线程计算完结!");
        return sum;
    }
}

CallableTest.java 测试代码如下:

package com.zejian.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableTest {public static void main(String[] args) {
// 第一种应用形式
//      // 创立线程池
//      ExecutorService es = Executors.newSingleThreadExecutor();
//      // 创立 Callable 对象工作
//      CallableDemo calTask=new CallableDemo();
//      // 提交工作并获取执行后果
//      Future<Integer> future =es.submit(calTask);
//      // 敞开线程池
//      es.shutdown();

    // 第二中应用形式

    // 创立线程池
    ExecutorService es = Executors.newSingleThreadExecutor();
    // 创立 Callable 对象工作
    CallableDemo calTask=new CallableDemo();
    // 创立 FutureTask
    FutureTask<Integer> futureTask=new FutureTask<>(calTask);
    // 执行工作
    es.submit(futureTask);
    // 敞开线程池
    es.shutdown();
    try {Thread.sleep(2000);
    System.out.println("主线程在执行其余工作");

    if(futureTask.get()!=null){
        // 输入获取到的后果
        System.out.println("futureTask.get()-->"+futureTask.get());
    }else{
        // 输入获取到的后果
        System.out.println("futureTask.get()未获取到后果");
    }

    } catch (Exception e) {e.printStackTrace();
    }
    System.out.println("主线程在执行实现");
}
}

  代码非常简单,正文也很清朗,这里咱们剖析一下第 2 种执行形式,先前申明一个 CallableDemo 类,该类实现了 Callable 接口,接着通过 call 办法去计算 sum 总值并返回。而后在测试类 CallableTest 中,把 CallableDemo 实例类封装成 FutureTask 对象并交给线程池去执行,最终执行后果将封装在 FutureTask 中,通过 FutureTask#get()能够获取执行后果。第一种形式则是间接把 Callable 实现类丢给线程池执行,其后果封装在 Future 实例中,第 2 种形式执行后果如下:

Callable 子线程开始计算啦!主线程在执行其余工作
Callable 子线程计算完结!futureTask.get()-->12497500
主线程在执行实现

  ok~,到此咱们对 Callable、Future 和 FutureTask 就介绍到这,有了这个常识铺垫,咱们就能够欢快的撩开 AsyncTask 的外部工作原理了。

二、AsyncTask 的工作原理齐全解析

  在上篇中,应用了如下代码来执行 AsyncTask 的异步工作:

new AysnTaskDiff("AysnTaskDiff-1").execute("");

  从代码可知,入口是 execute 办法,那咱们就先看看 execute 的源码:

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

  很显著 execute 办法只是一个壳子,间接调用了executeOnExecutor(sDefaultExecutor, params),其中 sDefaultExecutor 是一个串行的线程池,接着看看 sDefaultExecutor 外部实现:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

// 串行线程池类,实现 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() { // 插入一个 Runnble 工作
            public void run() {
                try {r.run();
                } finally {scheduleNext();
                }
            }
        });
        // 判断是否有 Runnable 在执行, 没有就调用 scheduleNext 办法
        if (mActive == null) {scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
      // 从工作队列 mTasks 中取出工作并放到 THREAD_POOL_EXECUTOR 线程池中执行.
      // 由此也可见工作是串行进行的。if ((mActive = mTasks.poll()) != null) {THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

  从源码能够看出,ArrayDeque 是一个寄存工作队列的容器(mTasks),工作 Runnable 传递进来后交给 SerialExecutor 的 execute 办法解决,SerialExecutor 会把工作 Runnable 插入到工作队列 mTasks 尾部,接着会判断是否有 Runnable 在执行, 没有就调用 scheduleNext 办法去执行下一个工作,接着交给 THREAD\_POOL\_EXECUTOR 线程池中执行,由此可见 SerialExecutor 并不是真正的线程执行者,它只是是保障传递进来的工作 Runnable(实例是一个 FutureTask)串行执行,而真正执行工作的是 THREAD\_POOL\_EXECUTOR 线程池,当然该逻辑也体现 AsyncTask 外部的工作是默认串行进行的。顺便看一下 THREAD\_POOL\_EXECUTOR 线程池的申明:

//CUP 核数
private static final int CPU_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;
// 非核心线程的存活工夫 1s
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);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 * 创立线程池
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

  ok~,对于 sDefaultExecutor,咱们先理解到这,回到之前 execute 办法外部调用的 executeOnExecutor 办法的步骤,先来看看 executeOnExecutor 都做了些什么事?其源码如下:

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;
   //onPreExecute()在此执行了!!!onPreExecute();
   // 参数传递给了 mWorker.mParams
   mWorker.mParams = params;
   // 执行 mFuture 工作,其中 exec 就是传递进来的 sDefaultExecutor
   // 把 mFuture 交给线程池去执行工作
   exec.execute(mFuture);

   return this;
    }

  从 executeOnExecutor 办法的源码剖析得悉,执行工作前先会去判断以后 AsyncTask 的状态,如果处于 RUNNING 和 FINISHED 状态就不可再执行,间接抛出异样,只有处于 Status.PENDING 时,AsyncTask 才会去执行。而后 onPreExecute()被执行的,该办法能够用于线程开始前做一些筹备工作。接着会把咱们传递进来的参数赋值给 mWorker.mParams,并执行开始执行 mFuture 工作,那么 mWorker 和 mFuture 到底是什么?先看看 mWorker 即 WorkerRunnable 的申明源码:

// 抽象类
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {Params[] mParams;
}

  WorkerRunnable 抽象类实现了 Callable 接口,因而 WorkerRunnable 实质上也算一个 Callable 对象,其外部还封装了一个 mParams 的数组参数,因而咱们在内部执行 execute 办法时传递的可变参数最终会赋值给 WorkerRunnable 的外部数组 mParams,这些参数最初会传递给 doInBackground 办法解决,这时咱们发现 doInBackground 办法也是在 WorkerRunnable 的 call 办法中被调用的,看看其源码如下:

public AsyncTask() {
   // 创立 WorkerRunnable mWorker,实质上就是一个实现了 Callable 接口对象
    mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {
            // 设置标记
            mTaskInvoked.set(true);

         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            // 执行 doInBackground,并传递 mParams 参数
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            // 执行实现调用 postResult 办法更新后果
            return postResult(result);
        }
    };
// 把 mWorker(即 Callable 实现类)封装成 FutureTask 实例
// 最终执行后果也就封装在 FutureTask 中
    mFuture = new FutureTask<Result>(mWorker) {
        // 工作执行实现后被调用
        @Override
        protected void done() {
            try {
             // 如果还没更新后果告诉就执行 postResultIfNotInvoked
                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);
            }
        }
    };
}

   能够看到在初始化 AsyncTask 时,不仅创立了 mWorker(实质实现了 Callable 接口的实例类)而且也创立了 FutureTask 对象,并把 mWorker 对象封装在 FutureTask 对象中,最初 FutureTask 对象将在 executeOnExecutor 办法中通过线程池去执行。给出下图帮助了解:

  AsynTask 在初始化时会创立 mWorker 实例对象和 FutureTask 实例对象,mWorker 是一个实现了 Callable 线程接口并封装了传递参数的实例对象,而后 mWorker 实例会被封装成 FutureTask 实例中。在 AsynTask 创立后,咱们调用 execute 办法去执行异步线程,其外部又间接调用了 executeOnExecutor 办法,并传递了线程池 exec 对象和执行参数,该办法外部通过线程池 exec 对象去执行 mFuture 实例,这时 mWorker 外部的 call 办法将被执行并调用 doInBackground 办法,最终通过 postResult 去告诉更新后果。对于 postResult 办法, 其源码如下:

private Result postResult(Result result) {@SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

  显然是通过 Handler 去执行后果更新的,在执行后果成返回后,会把 result 封装到一个 AsyncTaskResult 对象中,最初把 MESSAGE\_POST\_RESULT 标示和 AsyncTaskResult 寄存到 Message 中并发送给 Handler 去解决,这里咱们先看看 AsyncTaskResult 的源码:

private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

  显然 AsyncTaskResult 封装了执行后果的数组以及 AsyncTask 自身,这个没什么好说的,接着看看 AsyncTaskResult 被发送到 handler 后如何解决的。

private static class InternalHandler extends Handler {public InternalHandler() {
        // 获取主线程的 Looper 传递给以后 Handler,这也是为什么 AsyncTask 只能在主线程创立并执行的起因
        super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
    // 获取 AsyncTaskResult
        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:
            // 执行 onProgressUpdate 办法,本人实现。result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

  从 Handler 的源码剖析可知,该 handler 绑定的线程为主线线程,这也就是为什么 AsyncTask 必须在主线程创立并执行的起因了。接着通过 handler 发送过去的不同标记去决定执行那种后果,如果标示为 MESSAGE\_POST\_RESULT 则执行 AsyncTask 的 finish 办法并传递执行后果给该办法,finish 办法源码如下:

private void finish(Result result) {if (isCancelled()) {// 判断工作是否被勾销
            onCancelled(result);
        } else {// 执行 onPostExecute(result)并传递 result 后果
            onPostExecute(result);
        }
        // 更改 AsyncTask 的状态为已实现
        mStatus = Status.FINISHED;
    }

  该办法先判断工作是否被勾销,如果没有被勾销则去执行 onPostExecute(result)办法,内部通过 onPostExecute 办法去更新相干信息,如 UI,音讯告诉等。最初更改 AsyncTask 的状态为已实现。到此 AsyncTask 的全副流程执行完。
  这里还有另一种标记 MESSAGE\_POST\_PROGRESS,该标记是咱们在 doInBackground 办法中调用 publishProgress 办法时收回的,该办法原型如下:

protected final void publishProgress(Progress... values) {if (!isCancelled()) {
    // 发送 MESSAGE_POST_PROGRESS,告诉更新进度条
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();}
}

  ok~,AsyncTask 的整体流程根本剖析完,最初来个总结吧:当咱们调用 execute(Params… params)办法后,其外部间接调用 executeOnExecutor 办法,接着 onPreExecute()被调用办法,执行异步工作的 WorkerRunnable 对象 (本质为 Callable 对象) 最终被封装成 FutureTask 实例,FutureTask 实例将由线程池 sExecutor 执行去执行,这个过程中 doInBackground(Params… params)将被调用(在 WorkerRunnable 对象的 call 办法中被调用),如果咱们覆写的 doInBackground(Params… params)办法中调用了 publishProgress(Progress… values)办法,则通过 InternalHandler 实例 sHandler 发送一条 MESSAGE\_POST\_PROGRESS 音讯,更新进度,sHandler 解决音讯时 onProgressUpdate(Progress… values)办法将被调用;最初如果 FutureTask 工作执行胜利并返回后果,则通过 postResult 办法发送一条 MESSAGE\_POST\_RESULT 的音讯去执行 AsyncTask 的 finish 办法,在 finish 办法外部 onPostExecute(Result result)办法被调用,在 onPostExecute 办法中咱们能够更新 UI 或者开释资源等。这既是 AsyncTask 外部的工作流程,能够说是
Callable+FutureTask+Executor+Handler 外部封装。结尾咱们献上一张执行流程,帮助大家了解整个流程:

本文转自 https://blog.csdn.net/javazejian/article/details/52464139,如有侵权,请分割删除。

正文完
 0