学习 http://www.imooc.com/learn/377 笔记
异步任务:
由于Android单线程模型,只有主线程即UI线程可以对UI进行操作,然而一些耗时任务必须放在非主线程中进行。
AsyncTask是一个Android封装好的组件用于进行异步任务处理。
用处:在子线程中更新UI,封装简化异步操作
一个典型的AsyncTask使用:
public class MyAsyncTask extends AsyncTask<Void,Void,Void> {
/**第二执行,执行真正的后台耗时操作
* @param params
* @return
*/
@Override
protected Void doInBackground(Void... params) {
Log.d("Tony","doInBackground");
return null;
}
/**
* 首先执行,完成准备工作
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d("Tony", "onPreExecute");
}
/**
* 第三执行,用于展示处理的结果
* @param aVoid
*/
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Log.d("Tony", "onPostExecute");
}
/**
* 单独调用才能执行
* @param values
*/
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
Log.d("Tony", "onProgressUpdate");
}
}
具体介绍:
AsyncTask<Params, Progress, Result>
是一个抽象类,通常用于被继承,继承AsyncTask需要指定如下三个泛型参数:
Params:启动任务时输入参数的类型,比如HTTP请求的URL
Progress:后台任务执行中返回进度值的类型
Result:后台执行任务完成后返回结果的类型,比如String,Integer等
不一定非要使用参数,不使用时设为Void类型
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定
AsyncTask< doInBackground接受的参数 , 显示进度的参数 , doInBackground返回和onPostExecute传入的参数 >
AsyncTask的回调方法
onPreExecute():
前置调用,完成初始化任务,运行在主线程,可操作UI,比如显示进度条或者控件实例化
onPostExecute()
后置操作,承接doInBackground,运行在主线程,可操作UI,将结果传递给UI
doInBackground():
核心部分,必须重写,异步处理需要耗时的工作
onProgressUpdate():
进度条更新,如果doInBackground()调用了publishProgress()更新任务进度就会调用
运行顺序是onPreExecute->doInBackground->onPostExecute
onProgressUpdate()在加载进度条时被调用进行
Async注意事项
1:Async实例需要在:UI线程中创建
2:必须在UI线程中调用AsyncTask的execute()方法,这个方法是运行相应AsyncTAsk
3:重写的方法由系统自动调用
4:每个AsyncTask方法只能调用一次
AsyncTask异步加载网络图片实例:
main.xml 设置加载网络图片和进度条的按钮
<span style="font-size:10px;"><strong> </strong><Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/Button"
android:text="Image Test"
android:onClick="loadImage"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/ProgressBtn"
android:text="Progress Test"
android:onClick="loadProgress"/></span>
mainactivity 主要代码不多,用于响应按钮
public void loadImage(View view){
startActivity(new Intent(this, ImageTest.class));
}
public void loadProgress(View view){
startActivity(new Intent(this,ProgressBarTest.class));
}
image.xml 显示网络图片的UI界面
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
ImageTest 实现网络加载的主线程,按下MainActivity的按钮后会跳转到这个线程,内部类ImageAsyncTask实现异步加载
public class ImageTest extends Activity {
private ImageView mImageView;
private ProgressBar mProgressBar;
private static String URL=
"https://img-my.csdn.net/uploads/201504/12/1428806103_9476.png";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
mImageView= (ImageView) findViewById(R.id.image);
mProgressBar=(ProgressBar)findViewById(R.id.progressbar);
//开启异步线程操作,设置传递进去的操作
new ImageAsyncTask().execute(URL);
}
class ImageAsyncTask extends AsyncTask<String,Void,Bitmap>{
//进行顺序:onPreExecute->doInBackground->onPostExecute
/**
* 前置操作,显示进度条,操作UI
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);
}
/**
* 后置操作,承接doInBackground,操作UI
* @param bitmap
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mProgressBar.setVisibility(View.INVISIBLE);
mImageView.setImageBitmap(bitmap);
}
/**
* 真正的异步操作,以下都在子线程中进行。之前两个方法都在主线程中进行
* @param params
* @return
*/
//String... param是可变长数组
@Override
protected Bitmap doInBackground(String... params) {
//获取传递进来的url参数,因为只有一个所以在数组的0位
String url=params[0];
Bitmap bitmap=null;
URLConnection urlConnection;
InputStream inputStream;
try {
//异步操作图片加载和解析
// URl->InputStream->BufferedInputStream->Bitmap
urlConnection=new URL(url).openConnection();
inputStream=urlConnection.getInputStream();
BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
//将输入流解析成Bitmap图片
bitmap= BitmapFactory.decodeStream(bufferedInputStream);
//关闭输入流
inputStream.close();
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
//返回给onPostExecute()
return bitmap;
}
}
}
AsyncTask异步加载进度条实例:
progressBarTest 用于AsyncTask模拟进度条
因为在进度条运行时按返回键挂起,重新执行一个新的进度条ProgressAsyncTask时,必须等之前的ProgressAsyncTask执行完毕后才可以执行,导致界面出现进度条不运行的现象,所以实现了AsyncTask的关闭
public class ProgressBarTest extends Activity {
private ProgressBar mprogressBar;
private ProgressAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
mprogressBar=(ProgressBar)findViewById(R.id.pB);
mTask=new ProgressAsyncTask();
mTask.execute();
}
//目的是在AsyncTask挂起后结束线程,因为如果在进度条运行时按返回键挂起,重新执行一个新的
//进度条ProgressAsyncTask时,必须等之前的ProgressAsyncTask执行完毕后才可以执行,导致界面
//出现进度条不运行的现象,所以希望一旦ProgressAsyncTask挂起立刻结束线程
@Override
protected void onPause() {
super.onPause();
//如果当前进度条进程mTask不为空且正在异步加载
if (mTask!=null&&mTask.getStatus()==AsyncTask.Status.RUNNING){
//cancel()只是将对应的AsyncTask即mTask标记为cancel状态,
// 并不是真正的取消线程的执行,Java中没办法直接停止一个线程
//解决方法是在ProgressAsyncTask中加入判断cancel标记语句if(isCancelled())
mTask.cancel(true);
}
}
class ProgressAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
//模拟进度栏的更新
for (int i=0;i<100;i++){
if(isCancelled()){
break;
}
publishProgress(i);
//让线程睡眠300毫秒,延缓更新速度
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (isCancelled()){
return;
}
//得到进度更新值
mprogressBar.setProgress(values[0]);
}
}
}