Android中AsyncTask的相关面试题

1.Android中几个重要的有工作线程的机制

  • IntentService
  • AsyncTask
  • HandlerThread

2.AsyncTask是对Handler和线程池的一种封装。

3.AsyncTask使用方法

<1>AsyncTask是个抽象类,它需要子类去继承。

<2>我们通过调用AsyncTask的execute方法传入参数,执行任务。

<3>onPreExecute方法

▲该方法为复写方法

▲执行在主线程

▲该方法在任务执行前运行,进行一些数据初始化操作

<4>doInBackground方法

▲该方法为复写方法

▲该方法为后台运行方法,它的代码将在子线程中运行

▲该方法内部可以进行耗时操作

▲doInBackground方法在onPreExecute方法之后立即执行。

<5>onProgressUpdate方法

▲该方法是当我们在doInBackground方法中调用publishProgress(i)方法时才会被调用。

▲该方法为复写方法

▲onProgressUpdate方法也是执行在主线程中

<6>onPostExecute方法

▲该方法为复写方法

▲该方法运行在主线程中

▲该方法胡会在doInBackground执行完后立即执行。

▲在该方法中,可以对UI进行最后的更新。

4.AsyncTask几点注意

<1>AsyncTask的实例必须在主线程中创建

<2>AsyncTask的execute方法必须在主线程中调用

<3>回调方法,Android会自动调用

<4>一个AsyncTask实例,只能执行一次execute方法。

5.AsyncTask源码

private static final int CORE_POOL_SIZE = 1;

AsyncTask所使用的线程池核心大小。

6.AsyncTask源码

private static final int MAXIMUM_POOL_SIZE = 20;

AsyncTask所使用线程池最大线程数大小。

7.AsyncTask源码

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

该工厂为之后创建线程池所用,需要注意以下两点:

<1>mCount为原子类型。之所以使用原子类型操作,是为了确保后面获得mCount这个变量的大小的时候,它的整个方法和数据是线程安全的。

<2>重写的newThread方法是为了新增线程的名字以AsyncTask #为标识。

8.AsyncTask源码

private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue;

它存放的是Runnable对象。

9.AsyncTask源码

@Deprecated
public static final Executor THREAD_POOL_EXECUTOR;

前面AsyncTask初始化了一些参数。然后到这里它就是为了利用这些参数实例化这个线程池。这个线程池是静态的,final,所以可以看到,在AsyncTask内部它是维护了一个静态的线程池。因此默认情况下,AsyncTask会将工作交给这个线程池来执行。

在一个静态代码块中会将这个线程池利用之前定义好的参数进行初始化。

10.AsyncTask源码

@Deprecated
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

这里实例化了一个SerialExecutor类型的线程池,它内部也是static,final类型的。

SerialExecutor是AsyncTask的一个内部类。

11.AsyncTask源码

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() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

<1>这个SerialExecutor实现了Executor接口当中的execute这个方法。

<2>这个类它类似去执行串行的一些任务。即一个接一个地执行任务。

<3>mTasks变量是维护一个Runnable的双端队列。ArrayDeque是没有容量限制的,就是说它的容量可以自增长。

<4>mActive表示正在执行的任务Runnable。

<5>execute方法

首先该方法会传入一个Runnable类型的变量,然后会实例化一个Runnable类型的匿名内部类,然后对这个Runnable进行封装。通过调用队列的offer方法,将封装后的Runnable添加到队尾。然后它会执行Runnable的run方法,开始执行任务。注意这个run方法是在线程池中执行的。当任务执行完毕之后,会执行scheduleNext()方法,来执行下一个任务的Runnable。

<6>在execute方法尾部,会对mActive对象判断是否为null,若为null,则同样调用scheduleNext()开启下一个任务。

<7>在scheduleNext()方法中

通过调用mTasks.pool()方法,进行任务的出队操作,删除任务,并返回新的队头的Runnable。同时对该任务判断是否为空。如果不为空,将该Runnable传递给默认的线程池,调用execute方法来进行执行。

<8>所以可以看出,AsyncTask内部最终它还是要通过那个默认的线程池THREAD_POOL_EXECUTOR来执行一些线程的操作。

12.AsyncTask源码

@UnsupportedAppUsage
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

即AsyncTask默认情况下使用的线程池就是SERIAL_EXECUTOR,而SERIAL_EXECUTOR就是使用THREAD_POOL_EXECUTOR串行执行任务的。

所以AsyncTask默认是串行执行任务的。当然我们也可以使用并行执行任务。

13.AsyncTask源码

private static InternalHandler sHandler;

InternalHandler是AsyncTask定义的静态内部类,它绑定了主线程Looper和消息队列。所以这个Handler就是用来处理子线程发送的消息,然后在主线程中回显的。

14. AsyncTask源码

@UnsupportedAppUsage
private final WorkerRunnable<Params, Result> mWorker;

它是实现Callable接口的一个对象,内部会实现接口的call方法。

15. AsyncTask源码

@UnsupportedAppUsage
private final FutureTask<Result> mFuture;

它是FutureTask的一个对象,它需要用mWorker作为参数实例化mFuture对象。

16. AsyncTask源码

@UnsupportedAppUsage
private volatile Status mStatus = Status.PENDING;

表示AsyncTask初始状态位为PENDING。PENDING表示还没有开始执行任务。

Status是个枚举内部类,它有三个枚举值:

<1>PENDING:还没有开始执行任务。

<2>RUNNING:已经开始执行任务了。

<3>FINISHED:任务已经执行完成了或调用了Cancel方法取消了。

17. AsyncTask源码

回顾AsyncTask中的WorkerRunnable,源码如下:

@UnsupportedAppUsage
private final WorkerRunnable<Params, Result> mWorker;

这个 WorkerRunnable又是AsyncTask的一个内部抽象类,实现了Callable接口,源码如下:

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

Callable接口又是什么呢?可以看一下源码:

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

可以看到Callable接口内部定义了 一个call方法 ,所以在call方法上可以做一些具体的业务逻辑。在这一点上Callable与熟悉的Runnable接口很类似,但两者有一个不同点,Runnable的run方法是没有返回值的,Callable的call方法是有返回值的,返回了一个泛型V。

18. AsyncTask源码

回顾AsyncTask中的FutureTask,源码如下:

@UnsupportedAppUsage
private final FutureTask<Result> mFuture;

FutureTask内部实现了Runnable接口,源码如下:

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

而RunnableFuture继承自Runnable,源码如下:

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

接下来我们看一下FutureTask的构造方法,源码如下:

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

可以看到,FutureTask的构造方法中,传入了一个Callable接口,它就完成了它的实例化。回忆之前,Executor的execute方法,也是接收一个Runnable对象,所以FutureTask继承了Runnable接口,我们可以直接把FutureTask这个对象传递给我们的线程池Executor,通过调用execute方法来进行线程池的执行。

FutureTask有一个done()方法,源码如下:

protected void done() { }

该方法由我们自己去实现。done方法中可以做一些操作,比如任务完成之后你可以做一些回显、数据保存等。

FutureTask中的get()方法,源码如下:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}

它是阻塞式地等待任务返回值。它是等待Callable中call方法的返回值。

FutureTask中的cancel方法,源码如下:

    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

当调用FutureTask时执行cancel方法,来表示取消任务。任务取消后,也会直接执行FutureTask的done方法。

18. AsyncTask源码

AsyncTask构造方法,源码如下

public AsyncTask(@Nullable Looper callbackLooper) {

    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler(): new Handler(callbackLooper);

    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(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);
            }
        }
    };
}

我们可以看到,构造方法中实例化了WorkerRunnable类,而WorkerRunnable实现Callable接口,所以这里实现了Callable接口的call方法。而在call方法中,try-catch结构的finally中最后会调用postResult方法。

postResult方法中会根据DoInBackground方法调用的返回值result来进行相应的判断。

19. AsyncTask源码

postResult源码如下:

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

首先该方法通过调用getHandler方法获取主线程的Handler,然后根据这个Handler来进行判断,接着调用sendToTarget方法将消息交给Handler来处理。

20. AsyncTask源码

接着看一下Handler的内容,源码如下:

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    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:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

在它的handleMessage中会做相应判断。这里的result.mTask就是绑定的AsyncTask;result.mData[0]就是doInBackground返回的处理结果。它将这个结果交给finish这个方法来进行处理。

21. AsyncTask源码

我们进入finish方法,源码如下:

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

首先在finish方法内部会判断,我们这个AsyncTask是否被取消了。如果被取消了,它就会执行相应的onCancelled这个方法;如果没有被取消,则调用onPostExecute这个方法。调用isCancelled方法可以判断当前任务是否已经被取消了。onPostExecute方法之前说过,做一些后台计算结果之后的数据显现工作。是在主线程工作的。而我们的Handler也是抛向主线程的。所以这个handleMessage方法都是在主线程中执行的。所以这个finish方法也是在主线程中执行。finish方法最后,它会将状态值设置为FINISH,就表示我们整个任务就已经执行完毕了,可以去执行下一个任务了。

22. AsyncTask源码分析我们知道,AsyncTask主要就是两部分,一个是线程池,一个是Handler。

23. AsyncTask源码

AsyncTask构造方法,源码如下

public AsyncTask(@Nullable Looper callbackLooper) {

    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler(): new Handler(callbackLooper);

    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(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);
            }
        }
    };
}

<1>它会传入一个Looper对象,主要用来判断主线程Handler的。mHandler就是我们主线程的Handler。

<2>实例化mWorker对象,即WorkRunnable类的实例。前面分析我们知道,WorkerRunnable实现了Callable接口的call方法。

<3>call方法是在线程池中的某个线程上执行的 。不是运行在主线程的。在call方法中,执行mTaskInvoked.set(true)表示我们这个任务已经开始执行了。

<4>在call方法中的 

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

它会将执行call方法的这个线程设置为后台线程级别。

<5>在call方法中

result = doInBackground(mParams);

在工作线程中执行了DoInBackground这个方法,并返回一个结果赋给result。

<6>在call方法中,finally中会调用postResult方法,即不管执行结果如何,会在最后把result发送出去。

<7>mFuture是FutureTask的一个类型对象。这里是用mWorker作为参数去实例化的mFuture对象。

<8>实例化mFuture时,重写了它的done方法。两种情况会执行done方法,一种是任务执行完,一种是任务被取消。

24. AsyncTask源码

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

<1>这里有一个@MainThread注解。这个注解就表示它是运行在主线程中的。

<2>该方法内部只是简单地调用executeOnExecutor这个方法,并将sDefaultExecutor作为参数传给了executeOnExecutor这个方法。

<3>所以sDefaultExecutor就是我们默认执行任务的那个线程池。

25. AsyncTask源码

    @MainThread
    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();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

<1>首先该方法会对状态进行一个判断。为什么要进行判断?之前说了,一个AsyncTask实例只能执行一次任务,当你再次执行时便会抛出异常。所以这个方法一开始就会先检查之前的状态,只有状态为PENDING才会继续向下执行。

<2>判断完成后,将状态改为RUNNING状态。

<3>调用onPreExecute()方法,该方法运行在主线程当中。所以可以在该方法中做一些UI的改变。

<4>之后调用了exec.execute这个方法,来接收一个参数,同时,这个mFuture其实是FutureTask的实例。而这个FutureTask同时实现了Callable和Runnable的接口。

<5>所以可以直接调用execute方法来执行我们的线程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值