前言:
本文纯粹个人见解,如有错误或不正确的地方,请指出,多谢。AsyncTask是Android对 线程池 的一个轻量级封装。
介绍:
线程池:管理线程的地方。
核心线程:CORE_POOL_SIZE,
api19是 CPU核数+1
api27是 Math.max(2, Math.min(核数 - 1, 4)),即2到4。
任务队列:最大存放128个任务
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
最大线程数:核心线程+额外线程:MAXIMUM_POOL_SIZE = 核数*2+1
额外线程空闲后的存活时间:KEEP_ALIVE_SECONDS = 30; 即存活30秒。
现在假设新建AsyncTask,一直往里添加任务。
1.新建AsyncTask时,线程池为空,添加任务时,
会将任务交给任务队列,核心线程空闲就会从任务队列提取任务执行。
2.当全部核心线程都在工作,任务就会继续往任务队列放,
超过最大值128个任务时,就会新开额外线程来处理,直到最大线程数。
3.然后额外线程全部在工作的时候,还往里添加任务,
就会使用handler抛出拒绝任务的异常Exception。
自带的Executor:
控制AsyncTask是串行执行任务还是并行执行任务。
Handler
负责线程间的通讯和更新UI界面。可以看看我之前的 博文 或 查阅其他相关资料。
生命周期
onPreExecute → doInBackground → onPostExecute
其中通过手动调用publishProgress()来调用onProgressUpdate。
还可以调用onCancelled()来取消任务。
如何使用:
第一步:新建一个类继承AsyncTask<Parms,Progress,Result>,重写需要的方法。Parms:执行异步操作前输入的值。
Progress:执行中任务进度的值。
Result:执行后反馈结果的值。
三个都是泛类,可以任意改动类型。
第二步:实例化对象,在主线程上使用execute()即可。
举个简单的例子:模拟下载更新progressbar
class DownloadAsyncTask extends AsyncTask<Void, Integer, Boolean> {
//执行后台任务前,调用该方法
@Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setProgress(0);
mTextView.setText("0 %");
isDownload = true;
}
//顾名思义,就是后台子线程执行任务的方法
@Override
protected Boolean doInBackground(Void... voids) {
for (int i = 0; i < 101; i++) {
try {
//睡眠模拟网络下载//
Thread.sleep(80);
//调用这个才能更新UI进度
publishProgress(i);
} catch (InterruptedException e) {
e.printStackTrace();
//下载出错
return false;
}
}
//下载成功
return true;
}
//子线程更新UI进度的方法
//调用publishProgress会触发该方法
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
mTextView.setText(values[0] + " %");
}
//执行完后台任务后会执行的方法
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean) {
mTextView.setText("下载完成");
} else {
mTextView.setText("下载出错");
}
isDownload = false;
}
}
新建好内部类,就看看onCreate这边
public class SampleActivity extends AppCompatActivity {
private TextView mTextView;
private ProgressBar mProgressBar;
private Button mButton;
private boolean isDownload;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
isDolwoad = false;
mTextView = findViewById(R.id.sam_btn);
mProgressBar = findViewById(R.id.sam_progressBar);
mButton = findViewById(R.id.sam_btn);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isDownload) {
Toast.makeText(v.getContext(), "正在下载...", Toast.LENGTH_SHORT).show();
} else {
new DownloadAsyncTask().execute(); //这是执行AsyncTask,相当于添加任务
}
}
});
}
class DownloadAsyncTask extends AsyncTask<Void, Integer, Boolean> {/*请看上面的*/}
}
效果图:
从例子可以看出,内部类DownloadAsyncTask继承了AsyncTask<Void, Integer, Boolean>
Parms 就是 Void, 所以new DownloadAsyncTask().execute();没有传入参数。
Progress 就是 Integer ,用来更新progressBar 和 TextVIew
Results 就是Boolean ,最后执行完毕反馈的结果
例子中重写了的方法有
onPreExecute();
doInBackground(Void... void);
onProgressUpdate(Integer... values);
onPostExecute(Boolean aBoolean):
其中只有doInBackground是必须重写的,而且在后台子线程运行。
其他都是在主线程执行的。
细心的朋友会留意到方法里的参数,跟继承时的泛类值一样。
没错,doInBackground的参数就是传入的Parms,
onProgressUpdate的参数就是中间的Progress,
onPostExecute的参数就是最后的Results,即doInBackground执行完反馈的值。
优点:
简单,结构清晰。
方法已经给好,直接在里面写动作就好了。
缺点:一大堆...
1.生命周期
因为AsyncTask不会随Activity销毁而销毁。
doInBackground会一直执行完为止。然后执行onPostExecute或onCancelled.
2.内存泄漏
Activity中使用非静态匿名内部AsyncTask类,AsyncTask会保留一个对Activity的引用。
Activity被销毁,AsyncTask还有保留,系统无法收回Activity,导致内存泄漏。
3.结果丢失
Activity被销毁,AsyncTask还有保留的旧Activity的引用,
那么onProgressUpdate和onPostExecute将不起作用。
4.onCancel()不一定起作用
doInBackground()有不可打断的方法会失效,比如这个BitmapFactory.decodeStream() IO操作。
5.并行的隐患
因为所有AsyncTask都是用同一个线程池,如果并行的doInBackground访问到相同的资源,但没有处理同步问题,就会报错甚至崩溃。
6.局限性
AsyncTask对象必须在主线程中创建
AsyncTask对象的execute方法必须在主线程中调用
一个AsyncTask对象只能调用一次execute方法
.不要在你的程序中去直接调用onPreExecute(), onPostExecute,
doInBackground, onProgressUpdate方法
Thanks:
以上内容部分来自以下网站