AsyncTask的用法

Stella981
• 阅读 433

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程.

.为什么需要使用异步任务?

我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱.但Android是一个多线程的操作系统,我们总不能把所有的任务都放在主线程中进行实现,比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,就可能会造成后面任务的阻塞.Android会去检测这种阻塞,当阻塞时间太长的时候,就会抛出Application Not Responsed(ANR)错误.所以我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR.

.AsyncTask为何而生?

提到异步任务,我们能想到用线程,线程池去实现.确实,Android给我们提供了主线程与其他线程通讯的机制.但同时,Android也给我们提供了一个封装好的组件--AsyncTask.利用AsyncTask,我们可以很方便的实现异步任务处理.AsyncTask可以在子线程中更新UI,也封装简化了异步操作.使用线程,线程池处理异步任务涉及到了线程的同步,管理等问题.而且当线程结束的时候还需要使用Handler去通知主线程来更新UI.而AsyncTask封装了这一切,使得我们可以很方便的在子线程中更新UI.

.构建AsyncTask子类的泛型参数

AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:

Params:启动任务时输入的参数类型.

Progress:后台任务执行中返回进度值的类型.

Result:后台任务执行完成后返回结果的类型.

.构建AsyncTask子类的回调方法

AsyncTask主要有如下几个方法:

doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.

onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.

onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.

onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.

下面通过代码演示一个典型的异步处理的实例--加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片.

MainActivity.java

AsyncTask的用法

package com.example.caobotao.learnasynctask; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;

public class MainActivity extends Activity { private Button btn_image; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_image = (Button) findViewById(R.id.btn_image); btn_image.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,ImageActivity.class)); } }); } }

AsyncTask的用法

ImageActivity.java

AsyncTask的用法

package com.example.caobotao.learnasynctask;

import android.app.Activity; import android.graphics.*; import android.os.*; import android.view.View; import android.widget.*; import java.io.*; import java.net.*;

/** * Created by caobotao on 15/12/2. */ public class ImageActivity extends Activity { private ImageView imageView ; private ProgressBar progressBar ; private static String URL = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.image); imageView = (ImageView) findViewById(R.id.image); progressBar = (ProgressBar) findViewById(R.id.progressBar); //通过调用execute方法开始处理异步任务.相当于线程中的start方法. new MyAsyncTask().execute(URL); }

class MyAsyncTask extends AsyncTask<String,Void,Bitmap> {

    //onPreExecute用于异步处理前的操作
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //此处将progressBar设置为可见.
        progressBar.setVisibility(View.VISIBLE);
    }

    //在doInBackground方法中进行异步任务的处理.
    @Override
    protected Bitmap doInBackground(String... params) {
        //获取传进来的参数
        String url = params\[0\];
        Bitmap bitmap = null;
        URLConnection connection ;
        InputStream is ;
        try {
            connection = new URL(url).openConnection();
            is = connection.getInputStream();
            //为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
            Thread.sleep(3000);
            BufferedInputStream bis = new BufferedInputStream(is);
            //通过decodeStream方法解析输入流
            bitmap = BitmapFactory.decodeStream(bis);
            is.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    //onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        //隐藏progressBar
        progressBar.setVisibility(View.GONE);
        //更新imageView
        imageView.setImageBitmap(bitmap);
    }
}

}

AsyncTask的用法

activity_main.xml

AsyncTask的用法

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:gravity="center" android:layout_height="match_parent"> <Button android:id="@+id/btn_image" android:text="加载图片" android:layout_width="match_parent" android:layout_height="wrap_content"/>

AsyncTask的用法

progress.xml

AsyncTask的用法

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:gravity="center" android:layout_height="match_parent"> <ProgressBar style="?android:attr/progressBarStyleHorizontal" android:id="@+id/progress" android:layout_width="match_parent" android:layout_height="wrap_content"/>

AsyncTask的用法

由于涉及到网络操作,需要在AndroidManifest.xml中添加网络操作权限:

运行结果:

AsyncTask的用法   AsyncTask的用法   AsyncTask的用法   AsyncTask的用法   AsyncTask的用法

下面再演示一个模拟更新进度条的实例.

MainActivity.java

AsyncTask的用法

package com.example.caobotao.learnasynctask; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;

public class MainActivity extends Activity { private Button btn_progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_progress = (Button) findViewById(R.id.btn_progress); btn_progress.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,ProgressActivity.class)); } }); } }

AsyncTask的用法

ProgressActivity.java

AsyncTask的用法

package com.example.caobotao.learnasynctask;

import android.app.Activity; import android.os.AsyncTask; import android.os.AsyncTask.Status; import android.os.Bundle; import android.widget.ProgressBar;

import java.util.Scanner;

/** * Created by caobotao on 15/12/2. */ public class ProgressActivity extends Activity{ private ProgressBar progressBar; private MyAsyncTask myAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.progress); progressBar = (ProgressBar) findViewById(R.id.progress); myAsyncTask = new MyAsyncTask(); myAsyncTask.execute(); } }

class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        //通过publishProgress方法传过来的值进行进度条的更新.
        progressBar.setProgress(values\[0\]);
    }

    @Override
    protected Void doInBackground(Void... params) {
        //使用for循环来模拟进度条的进度.
        for (int i = 0;i < 100; i ++){
            //调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
            publishProgress(i);
            try {
                //通过线程休眠模拟耗时操作
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

}

AsyncTask的用法

activity_main.xml

AsyncTask的用法

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:gravity="center" android:layout_height="match_parent"> <Button android:id="@+id/btn_progress" android:text="加载进度条" android:layout_width="match_parent" android:layout_height="wrap_content"/>

AsyncTask的用法

progress.xml

AsyncTask的用法

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:gravity="center" android:layout_height="match_parent"> <ProgressBar style="?android:attr/progressBarStyleHorizontal" android:id="@+id/progress" android:layout_width="match_parent" android:layout_height="wrap_content"/>

AsyncTask的用法

同样需要在AndroidManifest.xml中添加网络操作权限:

运行结果:

AsyncTask的用法   AsyncTask的用法    AsyncTask的用法    AsyncTask的用法    AsyncTask的用法

点击'加载进度条'按钮后程序看起来运行正常.但是,正如上面图示,如果接着点击BACK键,紧接着再次点击'加载进度条'按钮,会发现进度条的进度一直是零,过了一会才开始更新.这是为什么呢?

根据上述的讲解,我们知道,AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.所以必须等到第一个task的for循环结束后,才能执行第二个task.我们知道,当点击BACK键时会调用Activity的onPause()方法.为了解决这个问题,我们需要在Activity的onPause()方法中将正在执行的task标记为cancel状态,在doInBackground方法中进行异步处理时判断是否是cancel状态来决定是否取消之前的task.

更改ProgressActivity.java如下:

AsyncTask的用法

package com.example.caobotao.learnasynctask;

import android.app.Activity; import android.os.AsyncTask; import android.os.AsyncTask.Status; import android.os.Bundle; import android.widget.ProgressBar;

import java.util.Scanner;

/** * Created by caobotao on 15/12/2. */ public class ProgressActivity extends Activity{ private ProgressBar progressBar; private MyAsyncTask myAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.progress); progressBar = (ProgressBar) findViewById(R.id.progress); myAsyncTask = new MyAsyncTask(); //启动异步任务的处理 myAsyncTask.execute(); }

//AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
@Override
protected void onPause() {
    super.onPause();
    if (myAsyncTask != null && myAsyncTask.getStatus() == Status.RUNNING) {
        //cancel方法只是将对应的AsyncTask标记为cancelt状态,并不是真正的取消线程的执行.
        myAsyncTask.cancel(true);
    }
}

class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        //通过publishProgress方法传过来的值进行进度条的更新.
        progressBar.setProgress(values\[0\]);
    }

    @Override
    protected Void doInBackground(Void... params) {
        //使用for循环来模拟进度条的进度.
        for (int i = 0;i < 100; i ++){
            //如果task是cancel状态,则终止for循环,以进行下个task的执行.
            if (isCancelled()){
                break;
            }
            //调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
            publishProgress(i);
            try {
                //通过线程休眠模拟耗时操作
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

}

AsyncTask的用法

.使用AsyncTask的注意事项

① 必须在UI线程中创建AsyncTask的实例.

② 只能在UI线程中调用AsyncTask的execute方法.

③ AsyncTask被重写的四个方法是系统自动调用的,不应手动调用.

④ 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.

⑤ AsyncTask的四个方法,只有doInBackground方法是运行在其他线程中,其他三个方法都运行在UI线程中,也就说其他三个方法都可以进行UI的更新操作.

点赞
收藏
评论区
推荐文章
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
光头强的博客 光头强的博客
5个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
5个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
小森森 小森森
5个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
5个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
5个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Stella981 Stella981
1年前
Android AsyncTask实现
AsyncTask是android中一个非常好用的异步执行工具类。AsyncTask的应用AsyncTaskenablesproperandeasyuseoftheUIthread.这个类允许执行后台操作并在UI线程中发布结果,而却不需要管理threads和/或handlers。Asy
Stella981 Stella981
1年前
Android异步操作总结
Android中经常会有一些操作比如网络请求,文件读写,数据库操作,比较耗时,我们需要将其放在非UI线程去处理,此时,我们需要处理任务前后UI的变化和交互。我们需要通过类似js中异步请求处理,这里总结我所了解到的,方便自己记忆,也方便别人的浏览。1.AsyncTasknewAysncTask().execute();AsyncTask会
Wesley13 Wesley13
1年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
helloworld_28799839 helloworld_28799839
5个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue