android 学习--异步任务类AsyncTask

本文详细解析了Android中的AsyncTask类,包括其内部结构、工作原理及使用方法。通过创建线程池实现线程复用,利用Handler机制更新UI,解决了耗时操作导致的ANR问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        android 是一个实时系统 ,手机开发对响应速度要求比较高,因为我们在activity或者其实组件里面一般不允许我们有耗时比较长的操作,这样比较容易出现anr,因此如果我们如果有耗时操作比较另起线程(Thread)来完成,线程执行完成后我们往往要刷新界面,但android 刷新UI必须要在主线程里面完成,所以我们必须要用Handler去通信完成。由此我们得出类似网络下载图片显示,我们一般采用的方式:Thread + Handler来实现。不过这样的一种方式有以下几个不好的地方:

    1.每次都要为一个任务创建Thread线程,如果任务比较多的话,会创建很多Thread造成资源的浪费。

    2.线程执行完成之后就释放没有达到重用的效果,如果我们能使用线程池就不会有这种情况。

    3.如果是多个activity的话,每个activity都创建Handler为免太麻烦。

 为了解决上面的问题,android还有一种更好的解决方法那就是AsyncTask。

  通常使用方式:

     MyTask extends AsyncTask<String, Integer, String> {

protected String doInBackground(String... params) {

//耗时操作

  }

      protected void onPostExecute(String result) {

       //完成之后  刷新UI

}

   }

这样子的使用方式是非常简单的,我们一起来看一下它的具体实现, 

先看一下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());
        }
    };
这是线程池创建线程的工厂类,每个线程的命名都是AsyncTask # + id号(自增)。

private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);
线程池阻塞队列,初始大小10
 public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
创建线程池。

这里还有另外一个线程池:

 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

我们可以先来看一下它的实现:

  

 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);
            }
        }
    }
这个线程池相对比较简单,主要是把Runable添加到mTasks队列里面。真正执行还是放到THREAD_POOL_EXECUTOR里面去执行,SerialExecutor真正的作用:

1.维护每个Runable添加都是有序的。

2.也起到一个缓存Runable的作用。

我们继续回去到AsyncTask看数据结构:

 private static final InternalHandler sHandler = new InternalHandler();创建一个Handler用于刷新界面,这里使用的MQ是主线程的.

接着,看到:

 private final WorkerRunnable<Params, Result> mWorker;
 private final FutureTask<Result> mFuture;

WorkerRunnable我们可以叫做工作线程,实现Callable接口。

FutureTask用来获取返回值.


大概的数据结构我们已经看完成了, 再看AsyncTask的构造函数:

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

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
构造mWorker工作线程,同时带Result返回值,执行postResult函数的结果;再用mWorker构造一个任务Task.如果运行FutureTask就会执行 call()方法。

我们再来看一下运行方法:

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

直接调用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();

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

        return this;
    }
设置当前状态->运行:

 mStatus = Status.RUNNING;

onPreExecute //执行前做的操作,一般来初始化数据.

exec.execute(mFuture);添加到线程池中运行。注意这里的线程是第二个线程池也就是SerialExecutor,再看一下:SerialExecutor的execute方法,只是把runable封装成新的Runable,然后添加到队列中去,新Runable会在执行完成之后检测队列是否还有Runable,如果有的话,再添加到线程池中运行。第一次mActive是空的,所以就会所Runable添加到线程池中执行。


如果doInBackground运行完成,postResult就会用AsyncTaskResult来封装结果,再用Handler发送Message;

sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));


进入Handle的handleMessage方法:

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;
            }
调用finish方法,再根据运行结果调用不同的方法,如果运行成功,则调用  onPostExecute(result);刷新UI


到此 AsyncTask我们分析完成了,实现可以概括为:

 1.用线程池来对我们创建的任何进行封装,实用线程的重用。

 2.利用 Handle来更新UI


注意:以前AsyncTask线程池的运行大小只有5,不过新版本已经改成可以根据cpu的核心来调整。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值