Android之AsyncTask异步任务

1      AsyncTask异步任务

1.1    ANR的概念

Application Not Responding,应用程序无响应

Android系统规定只有UI线程(主线程)能够修改UI界面,但是如果在UI线程中执行耗时操作,则会导致线程阻塞,影响用户体验,如果耗时操作导致阻塞时间过长,则有可能会引起系统ANR

产生的原因

用户在界面上的操作是由主线程处理,如果主线程被阻塞,那么就没有办法响应用户的操作,这时候可以看做是程序就“卡住了”,如果阻塞超过5秒,就会导致ANR。

如何避免ANR?

我们只需要将会造成主线程阻塞的操作(耗时操作)放在子线程中就可以了。

注意事项

1、超过5秒没有响应用户操作才会导致ANR,那么是不是5秒之内的耗时操作就可以在主线程中执行?

不是的,即使没有导致ANR也会影响用户的体验,一般200ms以上的操作都属于耗时操作应该放在子线程中执行

1、  ANR的产生不只是因为主线程被阻塞,根本的原因是程序没有响应用户的操作超过5秒,才会导致ANR。

但是,Android系统规定,和界面UI更新相关的操作必须在主线程中执行,如果在子线程中对UI界面进行更新操作,就会抛出异常,导致程序奔溃。

异常信息:android.view.ViewRootImpl$CalledFromWrongThreadException: Only theoriginal thread that created a view hierarchy can touch its views.

问题:执行耗时操作需要在子线程中完成,但是子线程不能更新UI,怎么办?

答:Android提供了消息机制可能解决这个问题,原理就是子线程中如果需要更新UI,就“通知”主线程更新UI

Android提供了以下方式解决上面的问题:

1、Handler消息机制(后面讲)

2、AsyncTask异步任务

1.2    Android系统中线程的概念

MainThread 主线程(UI线程):应用启动时创建,处理与UI相关事情,如点击事件、数据更新

WorkThread 子线程(工作线程):Android 4.0之后UI线程不能访问网络资源或执行耗时操作,必须开启子线程

1.3    AsyncTask类的基本使用

Android提供的异步任务类,主要作用是可以在子线程中执行耗时操作,并通知主线程更新UI

功能

在类中实现异步操作,并提供回调方法反馈当前异步执行的程度,最后将反馈的结果提供给UI主线程

回调方法

1.onPreExecute: 线程任务开始执行前的准备工作(运行在线程中)

2.doInBackground:在线程中执行耗时操作, 在线程中执行的代码(后台开启了线程并执行这部分代码)

3.onProgressUpdate: 在线程的执行中需要执行的界面操作(运行在线程中)

4.onPostExecute:在子线程直线完毕之后调用(在线程中执行)

其它回调方法:

5.onCancelled()是当调用cancel方法取消任务并doInBackground执行完之后才会调用

常用方法

execute:执行任务

cancel(boolean):取消任务

isCancelled():判断当前任务是否被取消

publishProgress(int progressValue):发布任务进度

三个泛型参数

第一个泛型参数:(定义)是传递给doInBackground方法的参数的类型(引用类型),由execute(…..)方法初传入

第二个泛型参数:(定义)是传递给onProgressUpdate方法的参数的类型,由publishProgress(。。。。。)方法传入

第三个泛型参数:是传递给onPostExecute方法的参数的类型,接收的是doInBackground方法的返回值

基本使用步骤

1、定义一个类,继承自AsyncTask,并指定三个泛型

2、重写回调方法

3、在UI线程中实例化AsyncTask对象

4、在UI线程中,执行AsyncTask对象的execute(..)方法,开始执行异步任务

5、在执行过程中,可以通过调用cancel(true)方法,停止异步任务

使用AsyncTask必须遵守的准则

三个泛型使用位置

第一个泛型:在doInBackground()方法中使用

第二个泛型:在onProgressUpdate()方法中使用

第三个泛型:在onPostExecute()方法中使用

AsyncTask的实例必须在UIthread中创建

execute方法必须在UI线程中被调用

一个AsyncTask实例只能被执行一次,否则多次调用时将会出现异常

1.4    Guithub开源框架简介

MarkdownPad --:用于看.md文件格式的文档工具。

Volley、XUtils、Universal-Image-Loader、okhttp、SlidingMenu、android-async-http、fresco

ButterKnife、EventBus

1.4.1  运用xutils框架下载图片

publicclassMainActivityextendsActivity {

@ViewInject(R.id.iv)ImageViewiv;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

publicvoiddownloadImage(View v){

//下载图片

HttpUtilshttpUtils =newHttpUtils();

Stringurl ="http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json";

httpUtils.send(HttpMethod.GET, url,newRequestCallBack() {

@Override

publicvoidonFailure(HttpException arg0, String arg1) {

}

@Override

publicvoidonSuccess(ResponseInfo arg0) {

Toast.makeText(MainActivity.this, arg0.result,0).show();

}

});

}

}

1.5    拓展

当有多个异步任务被同时启动时,那么是多个同时执行还是依次排队执行??

当有多个异步任务通过execute方法被同时启动时,是依次排队执行的,即

只有当第一个启动被启动的异步任务执行完毕后才会去执行下一个

如何实现让多条异步任务同时执行??

1.当多个异步对象通过execute方法启动时,

* 特点:不管异步任务对象是否属于同类别,使用都保持依次排队执行的特点

2. 如果需要让多个异步任务同时执行的话,可以选择使用

*executeOnExecutor方法启动异步任务

* 参数:

* 1:Executor线程池对象,可以通过此对象指定同时可以有多少个异步任务一起运行

* 2:作用与execute方法中的参数作用完全一致

* 注意:此处的数字不要添加的太多,否则影响速度

指定同时执行3条异步任务,代码如:

Executorexec= Executors.newFixedThreadPool(3);

newMyTask().executeOnExecutor(exec,pb);

newMyTask().executeOnExecutor(exec,pb2);

newMyTask().executeOnExecutor(exec,pb3);

1.5.1  示例多个异步同时执行

publicclassMainActivityextendsActivity {

privateProgressBarpb,pb2,pb3;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//初始化控件

initView();

}

privatevoidinitView() {

pb= (ProgressBar)this.findViewById(R.id.progressBar1);

pb2= (ProgressBar)this.findViewById(R.id.progressBar2);

pb3= (ProgressBar)this.findViewById(R.id.progressBar3);

}

publicvoidbtnClick(View v){

Executor exec = Executors.newFixedThreadPool(3);//同时执行的线程数

new MyTask().executeOnExecutor(exec,pb);

new MyTask().executeOnExecutor(exec,pb2);

new MyTask().executeOnExecutor(exec,pb3);

}

//异步任务

classMyTaskextendsAsyncTask{

ProgressBarcurrentPb;

@Override

protectedVoid doInBackground(ProgressBar...params) {

//TODOAuto-generated method stub

currentPb= params[0];

for(inti=0;i<=100;i++){

try{

Thread.sleep(100);

publishProgress(i);

}catch(InterruptedException e) {

//TODOAuto-generated catch block

e.printStackTrace();

}

}

returnnull;

}

@Override

protectedvoidonProgressUpdate(Integer... values) {

//TODOAuto-generated method stub

super.onProgressUpdate(values);

currentPb.setProgress(values[0]);

}

}

}

1.5.2  同时异步多图片下载

publicclassTwoActivityextendsActivity {

/*

*实现多张图片同时下载并且在图片下载过程中时刻显示图片的下载进度

*/

ImageViewiv,iv2,iv3;

ProgressBarpb,pb2,pb3;

String[]urls= {

"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",

"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",

"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png"};

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

//TODOAuto-generated method stub

super.onCreate(savedInstanceState);

setContentView(R.layout.two);

initView();

}

privatevoidinitView() {

iv= (ImageView) findViewById(R.id.imageView1);

iv2= (ImageView) findViewById(R.id.imageView2);

iv3= (ImageView) findViewById(R.id.imageView3);

pb= (ProgressBar) findViewById(R.id.progressBar1);

pb2= (ProgressBar) findViewById(R.id.progressBar2);

pb3= (ProgressBar) findViewById(R.id.progressBar3);

}

publicvoidclick(View v) {

Executor exec = Executors.newFixedThreadPool(2);

newImageTask().executeOnExecutor(exec,urls[0],iv,pb);

newImageTask().executeOnExecutor(exec,urls[1],iv2,pb2);

newImageTask().executeOnExecutor(exec,urls[2],iv3,pb3);

}

classImageTaskextendsAsyncTask {

ImageViewcurrentIv;

ProgressBarcurrentPb;

@Override

protectedBitmap doInBackground(Object...params) {

//TODOAuto-generated method stub

Stringurl = (String) params[0];

currentIv= (ImageView) params[1];

currentPb= (ProgressBar) params[2];

try{

HttpURLConnectionconn = (HttpURLConnection)newURL(url)

.openConnection();

conn.setRequestMethod("GET");

conn.connect();

if(conn.getResponseCode() == 200) {

InputStreamis = conn.getInputStream();

byte[] b =newbyte[1024];

intnum = -1;

ByteArrayOutputStreambos =newByteArrayOutputStream();

intcurrent = 0;

inttotal = conn.getContentLength();

while((num = is.read(b)) != -1) {

bos.write(b,0, num);

SystemClock.sleep(100);

current+= num;

publishProgress(total, current);

}

byte[]bitm = bos.toByteArray();

returnBitmapFactory.decodeByteArray(bitm,0, bitm.length);

}

}catch(MalformedURLException e) {

//TODOAuto-generated catch block

e.printStackTrace();

}catch(IOException e) {

//TODOAuto-generated catch block

e.printStackTrace();

}

returnnull;

}

@Override

protectedvoidonProgressUpdate(Integer... values) {

//TODOAuto-generated method stub

super.onProgressUpdate(values);

currentPb.setMax(values[0]);

currentPb.setProgress(values[1]);

}

@Override

protectedvoidonPostExecute(Bitmap result) {

//TODOAuto-generated method stub

super.onPostExecute(result);

if(result !=null){

currentIv.setImageBitmap(result);

}

}

}

}

1.6    案例

1.6.1    案例:简易计时器

publicclassMainActivityextendsActivity {

privateTextViewtv_count;

privateintcount= 0;

privateCountTaskcountTask;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tv_count= (TextView)this.findViewById(R.id.tv_count);

}

//开始计数

publicvoidstartCount(View v) {

if(countTask==null) {

countTask=newCountTask();

countTask.execute();

}

}

//暂停

publicvoidpauseCount(View v) {

if(countTask!=null) {

countTask.cancel(true);

countTask=null;

}

}

//重置

publicvoidreset(View v) {

pauseCount(null);

count= 0;

tv_count.setText("00:00:00");

}

//开启异步任务

privateclassCountTaskextendsAsyncTask {

@Override

protectedVoid doInBackground(Void... params){

while(!isCancelled()) {

try{

Thread.sleep(10);

//通知主线程更新显示

publishProgress(++count);

}catch(InterruptedException e) {

//TODOAuto-generated catch block

e.printStackTrace();

break;

}

}

returnnull;

}

@Override

protectedvoidonProgressUpdate(Integer... values) {

if(isCancelled()) {

return;

}

intss = values[0];

intmm = values[0] / 60;

inthh = values[0] / 60 / 60;

// tv_count.setText(values[0]+"");

tv_count.setText(hh +":"+ mm +":"+ ss);

}

//当子线程成功执行之后调用

@Override

protectedvoidonPostExecute(Void result) {

Log.i("mtag","onPostExecute");

Toast.makeText(MainActivity.this,"onPostExecute",0).show();

}

//当被中断取消时调用

@Override

protectedvoidonCancelled() {

Log.i("mtag","onCancelled");

}

}

}

1.6.2    案例:下载一张图片

Android4.0开始,Android系统规定不能在UI线程中执行访问网络资源的操作,相关操作必须在子线程中完成

Android网络访问需要的权限:android.permission.INTERNET

/**

* 网络访问工具类

*

*@authorEvanYu

*@date2016.01.08

*/

publicclassHttpUtils {

/**

* 网络访问超时时间

*/

publicstaticfinalintTIMEOUT= 10000;

/**

* 通过get方式实现网络请求

*

*@paramurl

*           访问的url地址

*@return请求的结果,null代表请求失败

*/

publicstaticbyte[] doGet(String url) {

HttpURLConnection conn =null;

try{

URL mUrl =newURL(url);

conn = (HttpURLConnection)mUrl.openConnection();

conn.setRequestMethod("GET");

conn.setConnectTimeout(TIMEOUT);

conn.setReadTimeout(TIMEOUT);

conn.connect();

intcode= conn.getResponseCode();

if(code== 200) {

returnreadStream(conn.getInputStream());

}else{

thrownewRuntimeException("网络访问失败:"+ code);

}

}catch(Exception e) {

e.printStackTrace();

returnnull;

}finally{

if(conn!=null) {

conn.disconnect();

conn =null;

}

}

}

publicstaticbyte[] doPost(String url, String params) {

HttpURLConnection conn =null;

try{

URL mUrl =newURL(url);

conn = (HttpURLConnection)mUrl.openConnection();

conn.setRequestMethod("POST");

conn.setConnectTimeout(TIMEOUT);

conn.setReadTimeout(TIMEOUT);

//设置请求属性

conn.setRequestProperty("Content-Type",

"application/x-www-form-urlencoded");

conn.setRequestProperty("Content-Length", params.length() +"");

// Post请求必须要写以下两行代码

conn.setDoInput(true);

conn.setDoOutput(true);

//将请求参数写到请求体中

conn.getOutputStream().write(params.getBytes());

;

conn.connect();

intcode= conn.getResponseCode();

if(code== 200) {

returnreadStream(conn.getInputStream());

}else{

thrownewRuntimeException("网络访问失败:"+ code);

}

}catch(Exception e) {

e.printStackTrace();

returnnull;

}finally{

if(conn!=null) {

conn.disconnect();

conn =null;

}

}

}

privatestaticbyte[] readStream(InputStream is)throwsIOException {

ByteArrayOutputStream baos =newByteArrayOutputStream();

byte[] buf =newbyte[1024];

intlen = 0;

while((len = is.read(buf)) != -1) {

baos.write(buf, 0, len);

}

returnbaos.toByteArray();

}

}

1.6.3    案例:下载文件时显示进度

使用ProgressBar控件显示下载进度

使用ProgressDialog显示下载进度

构造方法:ProgressDialog(Contextcontext)

设置进度条样式:setProgressStyle(intstyle)

设置标题:setTitle(CharSequence)

设置标题图标:setIcon(intresId)

设置显示内容:setMessage(CharSequencemessage)

设置进度值:setProgress(intvalue)

显示对话框:show()

关闭对话框:cancel()

publicclassMainActivityextendsActivity {

privateImageViewimg_view;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

img_view= (ImageView)this.findViewById(R.id.img_view);

}

publicvoiddownloadImg(View v) {

// Stringurl=

//"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";

Stringurl ="http://img1.imgtn.bdimg.com/it/u=335355609,381250936&fm=21&gp=0.jpg";

newMyAsynTask().execute(url);

}

privateclassMyAsynTaskextendsAsyncTask {

ProgressDialog pDialog;

@Override

protectedvoidonPreExecute() {

//开始下载之前先显示一个对话框

pDialog=newProgressDialog(MainActivity.this);

pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

pDialog.setMax(100);

pDialog.setTitle("下载图片");

pDialog.setMessage("正在下载图片.....");

pDialog.show();

}

@Override

protectedBitmap doInBackground(String...params) {

/*

* Stringurl=params[0]; byte[]data = HttpUtils.doGet(url);

* //将下载到的数据解析成一张图片Bitmapbm=BitmapFactory.decodeByteArray(data,

* 0, data.length); SystemClock.sleep(2000);returnbm;

*/

HttpURLConnectionconn =null;

try{

Stringurl = params[0];

URLmurl =newURL(url);

conn= (HttpURLConnection) murl.openConnection();

conn.setRequestMethod("GET");

intcode = conn.getResponseCode();

if(code == 200) {

//获取数据的总大小

inttatalSize = conn.getContentLength();

floatcurSize = 0;//记录当前已经下载的大小

InputStreamis = conn.getInputStream();

ByteArrayOutputStreambaos =newByteArrayOutputStream();

byte[] buf =newbyte[10];

intlength = 0;

while((length = is.read(buf)) != -1) {

baos.write(buf,0, length);

curSize+= length;

publishProgress((int) ((curSize / tatalSize) * 100));

//模拟耗时操作

SystemClock.sleep(20);

}

//将下载到的数据解析成一张图片

byte[]data = baos.toByteArray();

Bitmapbm = BitmapFactory.decodeByteArray(data, 0,

data.length);

returnbm;

}else{

thrownewRuntimeException("网路访问失败"+ code);

}

}catch(Exception e) {

e.printStackTrace();

}finally{

if(conn !=null){

conn.disconnect();

conn=null;

}

}

returnnull;

}

@Override

protectedvoidonProgressUpdate(Integer... values) {

pDialog.setProgress(values[0]);

}

@Override

protectedvoidonPostExecute(Bitmap result) {

img_view.setImageBitmap(result);

//取消显示

// pDialog.dismiss();

pDialog.cancel();

}

}

}

1.6.4    案例:下载并显示天气信息

数据网:K780数据网、聚合数据等

K780测试账号

Appkey:15250

Secret:2bbebb3e480a850df6daca0c04a954e1

Sign:f88a5cecc3cbd37129bc090c0ae29943

网络访问工具类的封装

HttpUtils类

K780数据访问工具类的封装

K780Utils类

public classWeather {

privateStringdays;

privateStringtemperature;

publicWeather() {

}

publicWeather(String day, String temperature) {

this.days= day;

this.temperature= temperature;

}

publicString getDay() {

returndays;

}

public voidsetDay(String day) {

this.days= day;

}

publicString getTemperature() {

returntemperature;

}

public voidsetTemperature(String temperature) {

this.temperature= temperature;

}

@Override

publicString toString() {

return"Weather[day="+days+", temperature="+temperature+"]";

}

}

引入网络访问工具类

publicclassMainActivityextendsActivity {

privateTextViewtv_show;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tv_show= (TextView) findViewById(R.id.tv_show);

}

publicvoiddownload(View v) {

Stringurl ="http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=15250&sign=f88a5cecc3cbd37129bc090c0ae29943&format=json";

newMyTask().execute(url);

}

privateclassMyTaskextendsAsyncTask> {

@Override

protectedListdoInBackground(String... params) {

Stringdata;

try{

Listlist =newArrayList();

data=newString(HttpUtils.doGet(params[0]));

JSONObjectjsonObject =newJSONObject(data);

JSONArrayresult = jsonObject.getJSONArray("result");

for(inti = 0; i < result.length(); i++) {

JSONObjecttemp = result.getJSONObject(i);

Stringdays = temp.getString("days");

Stringtemperature = temp.getString("temperature");

list.add(newWeather(days, temperature));

}

returnlist;

}catch(JSONException e) {

//TODOAuto-generated catch block

e.printStackTrace();

}

returnnull;

}

@Override

protectedvoidonPostExecute(List result) {

// tv_show.setText(result);

for(Weather weather : result) {

tv_show.append(weather.toString() +"\n");

}

}

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容