本篇文章主要介绍的是AsyncTask,Android提供的一个异步执行操作的轻量级框架。首先先说一些废话吧,Android程序运行在一个进程中,在进程中存在一个主线程,ActivityThread、也就是我们耳熟能详的UI线程。所谓UI线程,顾名思义就是执行对UI操作的线程,UI也是和用户接触的直接窗口,如果发生掉帧,卡顿,对用户体验很不好,当操作UI线程的时间超过5秒钟,就会发生ANR。所以,对于一些耗时的任务就不能放在主线程中执行了,就要在子线程中开启执行耗时任务。这时AsyncTask这个靓仔就出现了。
AsyncTask是什么?怎么用?原理是啥?
ok,下一步,介绍一下这位靓仔!!
前面也说了AsyncTask是Android原生提供的一个异步操作的组件。可以执行异步任务,并且更新UI,线程池处理异步任务涉及到了线程的同步,管理等问题,任务执行结束后,通过handler去更新UI,AsyncTask封装了这些,大大简化了异步任务。
public abstract class AsyncTask{
....
}
由此可以看出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)中断当前异步任务。
常用的方法及功能差不多已经介绍完了,该了解的了解了,会用才是硬道理。
下面以一个简单文件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 {
WeakReference activityWeakReference;
MainActivity activity;
public IAsyncTask(MainActivity activity) {
activityWeakReference = new WeakReference(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是怎么一回事了,并且也会使用了。
了解了具体的使用方法,也要了解一下其中原理么,正所谓知其然,并且要知其所以然,那么下一步,来看看内部到底是如何实现的。
以上面的为例吧。
前面已经提过了,它是一个抽象类,所以我们这里继承了它,并实现了内部的一些方法。
transferTask.execute(path);
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask 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 mTasks = new ArrayDeque();
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() {
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(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(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
由上可见publishProgress方法是在子线程中,也是通过handler发送消息MESSAGE_POST_PROGRESS来处理当前的任务。
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
将message传递的内容通过onProgressUpdate()方法抛出提供给子类进行处理。
到这里基本就分析完了。
Cannot execute task: the task has already been executed a task can be executed only once
当每个task执行完成后会将status变成finish
执行并行任务时可以直接调用task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
到这儿,基本功能介绍和源码分析基本结束了,觉得不错的同学,帮忙点个赞哦,非常感谢!