AsyncTask介绍
【同步和异步】
同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
【Android 为什么要引入异步任务】
我们之前学过了,Android UI 操作并不是线程安全的,并且这些操作都需要在UI线程中执行。 如果我们把耗时的操作都放在 UI 线程中的话,会发生 ANR 异常。假如我们在非 UI 线程中,比如在主线程中 new Thread() 另外开辟一个线程,然后直接在里面修改 UI 控件的值,此时会抛出下述异常: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views
我们在之前的文章中学了下述两种方法进行UI更新:
1、在 Handler 里写好 UI 更新,然后通过 sendMessage()
等的方法通知 UI 更新。
2、利用 Activity.runOnUiThread(Runnable)
把更新ui的代码创建在 Runnable 中,更新 UI 时,把Runnable 对象传进来即可
除了以上两种方法,Android 给我们提供了一个轻量级的用于处理异步任务的类:AsyncTask,我们一般是继承 AsyncTask,然后在类中实现异步操作,然后将异步执行的进度,反馈给 UI 主线程。使用 AsyncTask 可以更集中的管理某个任务的代码。
实际异步用的最多的地方就是网络操作,图片加载,数据传输等,AsyncTask 暂时可以满足初学者的需求,当然实际上我们使用更多的是第三发的框架,比如 Volley、OkHttp、android-async-http、XUtils等
【AsyncTask中的泛型】
Pramas
参数的数据泛型,即执行任务之前,是否需要参数做位前提条件,如果不需要,可以使用 void 类型进行标识,如果需要,参数应该是哪种数据类型
Progress
进度的数据类型,即执行任务过程中,是否需要提交进度,如果需要,应该使用哪种数据类型描述进度
Result
结果的数据类型,即执行任务完成之后,是否需要使用某种数据标识任务的操作结果,如果需要,则一共是哪种数据类型
【AsyncTask 中的方法】
doInBackground()
默认运行在子线程的方法,可以直接执行耗时操作,该方法的参数是 Params 类型的可变参数,返回值是 Result 类型
publishProgress()
用于提交进度的方法,注意:该方法是用于调用的方法,而不是重写的方法
onProgressUpdate()
更新进度的方法,该方法默认是运行在主线程的,所以可以直接更新 UI,每次调用publishProgress()
都会导致本方法被回调,且本方法的参数是publishProgress()
方法的参数
onPostExecute()
处理结果的方法,该方法默认是运行在主线程,且该方法会在 doInBackground()
方法执行完毕之后自动回调 1 次,该方法的参数是 doInBackground()
方法的返回值
【执行AsyncTask】
创建任务的对象,调用execute()
方法即可
【取消AsyncTask】
调用任务对象的 cancel(true)
即可取消任务
取消任务并不会使得子线程的任务直接终止,子线程中的任务依然执行完成,只是主线程的响应(包括 onProgressUpdate()
和onPostExecute()
)将不被执行
【Async的特点】
若干个 AsyncTask 任务即使同时调用 execute()
准备执行,也会按照顺序一次执行,即若干个 AsyncTask 任务表现为 "串行"的
注意:只要是使用 AsyncTask 实现的任务,都是串行的,即假设存在 Task1、Task2 都继承自 AsyncTask,且各自创建对象分别执行,这些任务也都会是串行的
用AsyncTask做一个进度条的栗子
用 AsyncTask 做一个和之前效果一样的进度条程序:传送门
xml 代码相同,不赘述
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ProgressBar progressBar;
private Button button;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
button = findViewById(R.id.button);
textView = findViewById(R.id.tv_show_progress);
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
InnerSyncTask task = new InnerSyncTask();
task.execute();
}
private class InnerSyncTask extends AsyncTask<String, Integer, Bitmap> {
@Override
protected Bitmap doInBackground(String... strings) {
Log.d("Async", "[Thread ID:" + Thread.currentThread() + "] InnerClassSyncTask->doInBackground");
for (int i = 0; i <= 100; i++) {
//提交进度
publishProgress(i);
Log.d("Async", "[Thread ID:" + Thread.currentThread() + "] InnerClassSyncTask->publishProgress"+i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
Log.d("Async", "[Thread ID:" + Thread.currentThread() + "] onProgressUpdate");
progressBar.setProgress(values[0]);
textView.setText(values[0] + "/100");
}
@Override
protected void onPostExecute(Bitmap bitmap) {
Log.d("Async", "[Thread ID:" + Thread.currentThread() + "] onPostExecute");
}
}
}
运行程序,查看日志
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->doInBackground
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->publishProgress0
D/Async: [Thread ID:Thread[main,5,main]] onProgressUpdate
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->publishProgress1
D/Async: [Thread ID:Thread[main,5,main]] onProgressUpdate
......
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->publishProgress98
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->publishProgress99
D/Async: [Thread ID:Thread[main,5,main]] onProgressUpdate
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->publishProgress100
D/Async: [Thread ID:Thread[main,5,main]] onProgressUpdate
D/Async: [Thread ID:Thread[main,5,main]] onPostExecute
取消AsyncTask
在刚才的案例中增加一个按钮,增加点击事件,取消 AsyncTask
public class AsyncActivity extends AppCompatActivity implements View.OnClickListener {
......
private Button buttonCancel;
private InnerSyncTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async);
......
buttonCancel = findViewById(R.id.buttonCancel);
buttonCancel.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.button:
task = new InnerSyncTask();
task.execute();
break;
case R.id.buttonCancel:
task.cancel(true);
break;
}
}
......
}
我们打印日志可以发现子线程依然在执行,只是主线程的响应没被执行
......
D/Async: [Thread ID:Thread[AsyncTask #1,5,main]] InnerClassSyncTask->publishProgress100