Android开发笔记之:深入理解多线程AsyncTask
2023-06-13 09:14:54 时间
AsyncTask可以方便的执行异步操作(doInBackground),又能方便的与主线程进行通信,它本身又有良好的封装性,可以进行取消操作(cancel())。关于AsyncTask的使用,文档说的很明白,下面直接上实例。
实例
这个实例用AsyncTask到网络上下载图片,同时显示进度,下载完图片更新UI。
packagecom.hilton.effectiveandroid.concurrent;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.OutputStream;
importjava.net.HttpURLConnection;
importjava.net.MalformedURLException;
importjava.net.URL;
importandroid.app.Activity;
importandroid.content.Context;
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.os.AsyncTask;
importandroid.os.Bundle;
importandroid.os.SystemClock;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.ImageView;
importandroid.widget.ProgressBar;
importcom.hilton.effectiveandroid.R;
/*
*AsyncTaskcannotbereused,i.e.ifyouhaveexecutedoneAsyncTask,youmustdiscardit,youcannotexecuteitagain.
*IfyoutrytoexecuteanexecutedAsyncTask,youwillget"java.lang.IllegalStateException:Cannotexecutetask:thetaskisalreadyrunning"
*Inthisdemo,ifyouclick"gettheimage"buttontwiceatanytime,youwillreceive"IllegalStateException".
*Aboutcancellation:
*YoucancallAsyncTask#cancel()atanytimeduringAsyncTaskexecuting,buttheresultisonPostExecute()isnotcalledafter
*doInBackground()finishes,whichmeansdoInBackground()isnotstopped.AsyncTask#isCancelled()returnstrueaftercancel()getting
*called,soifyouwanttoreallycancelthetask,i.e.stopdoInBackground(),youmustcheckthereturnvalueofisCancelled()in
*doInBackground,whenthereareloopsindoInBackgroundinparticular.
*ThisisthesametoJavathreading,inwhichisnoeffectivewaytostoparunningthread,onlywaytodoissetaflagtothread,andcheck
*theflageverytimeinThread#run(),ifflagisset,run()aborts.
*/
publicclassAsyncTaskDemoActivityextendsActivity{
privatestaticfinalStringImageUrl="http://i1.cqnews.net/sports/attachement/jpg/site82/2011-10-01/2960950278670008721.jpg";
privateProgressBarmProgressBar;
privateImageViewmImageView;
privateButtonmGetImage;
privateButtonmAbort;
@Override
publicvoidonCreate(Bundleicicle){
super.onCreate(icicle);
setContentView(R.layout.async_task_demo_activity);
mProgressBar=(ProgressBar)findViewById(R.id.async_task_progress);
mImageView=(ImageView)findViewById(R.id.async_task_displayer);
finalImageLoaderloader=newImageLoader();
mGetImage=(Button)findViewById(R.id.async_task_get_image);
mGetImage.setOnClickListener(newView.OnClickListener(){
publicvoidonClick(Viewv){
loader.execute(ImageUrl);
}
});
mAbort=(Button)findViewById(R.id.asyc_task_abort);
mAbort.setOnClickListener(newView.OnClickListener(){
publicvoidonClick(Viewv){
loader.cancel(true);
}
});
mAbort.setEnabled(false);
}
privateclassImageLoaderextendsAsyncTask<String,Integer,Bitmap>{
privatestaticfinalStringTAG="ImageLoader";
@Override
protectedvoidonPreExecute(){
//Initializeprogressandimage
mGetImage.setEnabled(false);
mAbort.setEnabled(true);
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setProgress(0);
mImageView.setImageResource(R.drawable.icon);
}
@Override
protectedBitmapdoInBackground(String...url){
/*
*Fuckingridiculousthinghappenedhere,touseanyInternetconnections,eitherviaHttpURLConnection
*orHttpClient,youmustdeclareINTERNETpermissioninAndroidManifest.xml.Otherwiseyouwillget
*"UnknownHostException"whenconnectingorothertcp/ip/httpexceptionsratherthan"SecurityException"
*whichtellsyouneedtodeclareINTERNETpermission.
*/
try{
URLu;
HttpURLConnectionconn=null;
InputStreamin=null;
OutputStreamout=null;
finalStringfilename="local_temp_image";
try{
u=newURL(url[0]);
conn=(HttpURLConnection)u.openConnection();
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setConnectTimeout(20*1000);
in=conn.getInputStream();
out=openFileOutput(filename,Context.MODE_PRIVATE);
byte[]buf=newbyte[8196];
intseg=0;
finallongtotal=conn.getContentLength();
longcurrent=0;
/*
*WithoutcheckingisCancelled(),theloopcontinuesuntilreadingwholeimagedone,i.e.theprogress
*continuesgoupto100.ButonPostExecute()willnotbecalled.
*BycheckingisCancelled(),wecanstopimmediately,i.e.progressstopsimmediatelywhencancel()iscalled.
*/
while(!isCancelled()&&(seg=in.read(buf))!=-1){
out.write(buf,0,seg);
current+=seg;
intprogress=(int)((float)current/(float)total*100f);
publishProgress(progress);
SystemClock.sleep(1000);
}
}finally{
if(conn!=null){
conn.disconnect();
}
if(in!=null){
in.close();
}
if(out!=null){
out.close();
}
}
returnBitmapFactory.decodeFile(getFileStreamPath(filename).getAbsolutePath());
}catch(MalformedURLExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
returnnull;
}
@Override
protectedvoidonProgressUpdate(Integer...progress){
mProgressBar.setProgress(progress[0]);
}
@Override
protectedvoidonPostExecute(Bitmapimage){
if(image!=null){
mImageView.setImageBitmap(image);
}
mProgressBar.setProgress(100);
mProgressBar.setVisibility(View.GONE);
mAbort.setEnabled(false);
}
}
}
运行结果
1.AsyncTask对象不可重复使用,也就是说一个AsyncTask对象只能execute()一次,否则会有异常抛出"java.lang.IllegalStateException:Cannotexecutetask:thetaskisalreadyrunning"
2.在doInBackground()中要检查isCancelled()的返回值,如果你的异步任务是可以取消的话。
cancel()仅仅是给AsyncTask对象设置了一个标识位,当调用了cancel()后,发生的事情只有:AsyncTask对象的标识位变了,和doInBackground()执行完成后,onPostExecute()不会被回调了,而doInBackground()和onProgressUpdate()还是会继续执行直到doInBackground()结束。所以要在doInBackground()中不断的检查isCancellled()的返回值,当其返回true时就停止执行,特别是有循环的时候。如上面的例子,如果把读取数据的isCancelled()检查去掉,图片还是会下载,进度也一直会走,只是最后图片不会放到UI上(因为onPostExecute()没被回调)!
这里的原因其实很好理解,想想JavaSE的Thread吧,是没有方法将其直接Cacncel掉的,那些线程取消也无非就是给线程设置标识位,然后在run()方法中不断的检查标识而已。
3.如果要在应用程序中使用网络,一定不要忘记在AndroidManifest中声明INTERNET权限,否则会报出很诡异的异常信息,比如上面的例子,如果把INTERNET权限拿掉会抛出"UnknownHostException"。刚开始很疑惑,因为模拟器是可以正常上网的,后来Google了下才发现原来是没权限,但是疑问还是没有消除,既然没有声明网络权限,为什么不直接提示无网络权限呢?
对比JavaSE的Thread
Thread是非常原始的类,它只有一个run()方法,一旦开始,无法停止,它仅适合于一个非常独立的异步任务,也即不需要与主线程交互,对于其他情况,比如需要取消或与主线程交互,都需添加额外的代码来实现,并且还要注意同步的问题。
而AsyncTask是封装好了的,可以直接拿来用,如果你仅执行独立的异步任务,可以仅实现doInBackground()。
所以,当有一个非常独立的任务时,可以考虑使用Thread,其他时候,尽可能的用AsyncTask。
相关文章
- android scaleanimation动画,【Android动画九章】-RotateAnimation(旋转动画)和ScaleAnimation(尺寸动画)…[通俗易懂]
- android onresume函数,Android界面跳转时候onDestroy和onResume的调用顺序
- Android浏览器直接打开网页上的文档
- android 常用加密,分享一下Android各种类型的加密
- Android系统签名以及生成keystore秘钥
- android bindservice方法,Android bindservice方法返回false
- android 的hook技术,Android Native Hook技术(一)
- android触摸屏事件,Android Touch事件分析
- Android n_android 反编译
- android跳转到相册需要权限,Android打开相册获取图片路径[通俗易懂]
- eclipse中android开发_Android开发教程
- android 系统浏览器 源码-Android 最最最简单的浏览器代码
- 【Android 系统开发】 编译 Android文件系统 u-boot 内核 并烧写到 OK-6410A 开发板上
- 【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )
- 【Android 内存优化】libjpeg-turbo 函数库交叉编译与使用 ( 交叉编译脚本编写 | 函数库头文件拷贝 | 构建脚本配置 | Android Studio 测试函数库 )
- 【Android 逆向】Android 逆向基本概念 ( 软件运行时内存结构 | 文件与内存之间的联系 )
- 【Android Gradle 插件】LintOptions 配置 ③ ( LintOptions#error 方法配置 | Lint 问题 ID | 查询 Lint 问题 ID 列表 )
- 【错误记录】Android 应用安全检测漏洞修复 ( StrandHogg 漏洞 | 设置 Activity 组件 android:taskAffinity=““ )
- [android] 手机卫士设置向导页面详解手机开发
- android notification,notificationmanager详解手机开发
- 爱壁纸HD下载 爱壁纸HD安卓版 爱壁纸HD 3.7 Android去广告版
- ChangeLog有点长:Android 5.0 Lollipop被证实是个“大更新”
- 每16台Android手机中,就有一台受BadKernel漏洞的影响
- 解析android流量监测的实现原理
- android教程之使用popupwindow创建菜单示例