AsyncTask使用详解及源码分析

Android异步操作之AsyncTask


前言

本篇文章主要介绍的是AsyncTask,Android提供的一个异步执行操作的轻量级框架。首先先说一些废话吧,Android程序运行在一个进程中,在进程中存在一个主线程,ActivityThread、也就是我们耳熟能详的UI线程。所谓UI线程,顾名思义就是执行对UI操作的线程,UI也是和用户接触的直接窗口,如果发生掉帧,卡顿,对用户体验很不好,当操作UI线程的时间超过5秒钟,就会发生ANR。所以,对于一些耗时的任务就不能放在主线程中执行了,就要在子线程中开启执行耗时任务。这时AsyncTask这个靓仔就出现了。

AsyncTask是什么?怎么用?原理是啥?

ok,下一步,介绍一下这位靓仔!!

AsyncTask是什么?

前面也说了AsyncTask是Android原生提供的一个异步操作的组件。可以执行异步任务,并且更新UI,线程池处理异步任务涉及到了线程的同步,管理等问题,任务执行结束后,通过handler去更新UI,AsyncTask封装了这些,大大简化了异步任务。

public abstract class AsyncTask<Params, Progress, Result>{
    ....
}

由此可以看出AsyncTask是一个抽象类,所以需要子类去继承它,同时,可以看到有几个参数:

Params:执行任务时传递的参数
Progress:表示更新进度
Result:异步任务执行完成返回的结果

另外几个常用的接口,可根据实际需求进行重写:

//在异步任务开始之前执行,主要用于初始化工作,(可选)
@MainThread
protected void onPreExecute() {
}

//运行在子线程中,用于执行后台任务,必须重写
@WorkerThread
protected abstract Result doInBackground(Params... params);

//工作线程执行完毕之后会回调此方法并将结果返回
@MainThread
protected void onPostExecute(Result result) {
}

//执行后台任务时通过publishProgress(Progress... values)将执行进度同步更新
@MainThread
protected void onProgressUpdate(Progress... values) {
}

//取消任务时回调
@MainThread
protected void onCancelled() {
}

其中doInBackground()是必须要重写的,运行在工作线程中,其他可写可不写,根据当前业务决定。

isCancelled() 可以用来判断当前异步任务是否已经取消

cancel(boolean mayInterruptIfRunning)中断当前异步任务。

常用的方法及功能差不多已经介绍完了,该了解的了解了,会用才是硬道理。

AsyncTask如何使用?

下面以一个简单文件Copy示例来介绍一下AsyncTask的使用。

public class MainActivity extends Activity {

    private Button start;
    private TextView showStatus;
    private ProgressBar progressBar;
    private IAsyncTask transferTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        showStatus = findViewById(R.id.tv_show_status);
        progressBar = findViewById(R.id.pb_show_progress);
        start = findViewById(R.id.start);

        final String path = Environment.getExternalStorageDirectory().toString() + File.separator + "weixin673android1360.apk";
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                transferTask = new IAsyncTask(MainActivity.this);
                transferTask.execute(path);
            }
        }); 
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(transferTask != null && !transferTask.isCancelled())
            transferTask.cancel(true);
    }

    static class IAsyncTask extends AsyncTask<String, Integer, Void> {
        WeakReference<MainActivity> activityWeakReference;
        MainActivity activity;

        public IAsyncTask(MainActivity activity) {
            activityWeakReference = new WeakReference<MainActivity>(activity);
        }
        //执行之前进行初始化工作,在主线程中
        @Override
        protected void onPreExecute() {
            if (activity == null)
                activity = activityWeakReference.get();
            if (activity.progressBar != null) {
                activity.progressBar.setProgress(0);
            }

            activity.showStatus.setText(0 + "%");
        }

        //workthread 在子线程中执行耗时操作
        //执行完成之后会将结果返回,这里我们没有返回结果,所以设置返回类型为Void

        @Override
        protected Void doInBackground(String... paths) {
            File file = new File(paths[0]);
            File target = new File(Environment.getExternalStorageDirectory().toString()
                    + "/temp/weixin.apk");

            if (file.exists() && file.isFile()) {
                long totalSize = file.length();
                FileInputStream fis = null;
                FileOutputStream fos = null;

                try {
                    fis = new FileInputStream(file);
                    fos = new FileOutputStream(target);
                    byte[] buffer = new byte[1024];
                    int result = fis.read(buffer);

                    long temp = 0;
                    while (result != -1) {
                        fos.write(buffer, 0, buffer.length);
                        temp += buffer.length;
                        int percent = getPercentInt(temp, totalSize);
                        publishProgress(percent);
                        result = fis.read(buffer);
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {

                    try {
                        if (fis != null)
                            fis.close();
                        if (fos != null)
                            fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }
            return null;
        }

        public int getPercentInt(long used, long total) {
            double percent = used / (double) total;
            String numberPercent = NumberFormat.getPercentInstance().format(percent);
            String dealPercent = numberPercent.substring(0, numberPercent.indexOf("%"));
            return Integer.parseInt(dealPercent);
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            activity.progressBar.setProgress(values[0]);
            activity.showStatus.setText(values[0] + "%");
        }
    }
}

相信各位看官看到这里已经大概了解了AsyncTask是怎么一回事了,并且也会使用了。
了解了具体的使用方法,也要了解一下其中原理么,正所谓知其然,并且要知其所以然,那么下一步,来看看内部到底是如何实现的。

AsyncTask源码分析

以上面的为例吧。

前面已经提过了,它是一个抽象类,所以我们这里继承了它,并实现了内部的一些方法。

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

@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(WorkerRunnable)
    mWorker.mParams = params;

    exec.execute(mFuture);

    return this;
}

在这里先判断了一下当前状态,判断是否为阻塞,如果是则执行。

private volatile Status mStatus = Status.PENDING;

这里看到status的初始化状态为PENDING,并且使用了volatile关键字修饰,说明在此同步了status变量。也就是说当其他线程修改了当前状态时,此处的mStatus也会同步改变。
通过上面case可以看到,如果当前状态为RUNNING或者FINISHED,就抛出异常。
接下来执行

exec.execute(mFuture);
根据上面传入的参数来看,这里通过一个线程池来执行任务,即sDefaultExecutor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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);
        }
    }
}

通过上面代码可看到默认使用的线程池是串行线程池,对每个任务依次执行。上面执行的时候传入了mFuture,找到mFuture初始化的地方,在构造方法中。

public AsyncTask(@Nullable Looper callbackLooper) {
    //创建了handler对象
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
    //初始化mWorker变量
    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);
            }
        }
    };
}

前面通过线程池在子线程中执行了mFuture,所以这里doInBackground也是在子线程中执行的。最终将执行完成的结果返回,通过postResult发送出去。

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    //封装一个message,然后通过message.sendToTarget()用handler把message发送出去。
    message.sendToTarget();
    return result;
}
//获取异步任务执行完成之后的结果
public final Result get() throws InterruptedException, ExecutionException {
    return mFuture.get();
}  

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    //判断当前任务的标记是否被标记
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

关于Handler的消息处理机制可以参考Handler源码分析及使用

对发送出去的message进行处理:

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

接收到消息后执行了finish()方法,进入此方法标志异步任务已经结束,并且已经进入主线程,可以对UI数据进行更新。

private void finish(Result result) {
    //判断是否被取消,如果取消则回调onCancelled()方法,没有取消则执行onPostExecute()方法
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    //更新状态为finish
    mStatus = Status.FINISHED;
}

这里我们可以通过重写onCancelled()和onPostExecute()拿到对应的结果。

另外我们后台执行的过程中会通过onProgressUpdate()实时更新UI,在上面的示例中也用到了,接下来分析一下。
我们在上面的示例中使用了publishProgress()方法来更细当前进度。

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

由上可见publishProgress方法是在子线程中,也是通过handler发送消息MESSAGE_POST_PROGRESS来处理当前的任务。

case MESSAGE_POST_PROGRESS:
    result.mTask.onProgressUpdate(result.mData);
    break;

将message传递的内容通过onProgressUpdate()方法抛出提供给子类进行处理。

到这里基本就分析完了。

使用注意点

1. AsyncTask在使用过程中每个task对象只能执行一次,重复使用会报出异常:

Cannot execute task: the task has already been executed a task can be executed only once
当每个task执行完成后会将status变成finish

2. AsyncTask在使用过程中可能会引起内存泄漏,所以避免在task中使用Activity的强引用,使用上述例子的静态内部类来解决上述问题。

3. 经过上面的了解,我们知道AsyncTask中默认使用的是串行线程池,如果处理任务数量多的话时间会较长,为解决这一点,其内部也提供了线程池THREAD_POOL_EXECUTOR来并行执行任务。

执行并行任务时可以直接调用task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);

到这儿,基本功能介绍和源码分析基本结束了,觉得不错的同学,帮忙点个赞哦,非常感谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值