Android必学之AsyncTask
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
- 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));
- }
- });
- }
- }
ImageActivity.java
- 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);
- }
- }
- }
activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <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"/>
- </LinearLayout>
progress.xml
- <?xml version="1.0" encoding="utf-8"?>
- <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"/>
- </LinearLayout>
由于涉及到网络操作,需要在AndroidManifest.xml中添加网络操作权限:<uses-permission android:name="android.permission.INTERNET"/>
运行结果:
下面再演示一个模拟更新进度条的实例.
MainActivity.java
- 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));
- }
- });
- }
- }
ProgressActivity.java
- 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;
- }
- }
- }
activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <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"/>
- </LinearLayout>
progress.xml
- <?xml version="1.0" encoding="utf-8"?>
- <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"/>
- </LinearLayout>
同样需要在AndroidManifest.xml中添加网络操作权限:<uses-permission android:name="android.permission.INTERNET"/>
运行结果:
点击'加载进度条'按钮后程序看起来运行正常.但是,正如上面图示,如果接着点击BACK键,紧接着再次点击'加载进度条'按钮,会发现进度条的进度一直是零,过了一会才开始更新.这是为什么呢?
根据上述的讲解,我们知道,AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.所以必须等到第一个task的for循环结束后,才能执行第二个task.我们知道,当点击BACK键时会调用Activity的onPause()方法.为了解决这个问题,我们需要在Activity的onPause()方法中将正在执行的task标记为cancel状态,在doInBackground方法中进行异步处理时判断是否是cancel状态来决定是否取消之前的task.
更改ProgressActivity.java如下:
- 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的注意事项
① 必须在UI线程中创建AsyncTask的实例.
② 只能在UI线程中调用AsyncTask的execute方法.
③ AsyncTask被重写的四个方法是系统自动调用的,不应手动调用.
④ 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.
⑤ AsyncTask的四个方法,只有doInBackground方法是运行在其他线程中,其他三个方法都运行在UI线程中,也就说其他三个方法都可以进行UI的更新操作.
Android必学之AsyncTask的更多相关文章
- Android必学-异步加载+Android自定义View源码【申明:来源于网络】
Android必学-异步加载+Android自定义View源码[申明:来源于网络] 异步加载地址:http://download.csdn.net/detail/u013792369/8867609 ...
- Android必学——AsyncTask
第一章 AsyncTask的基本构成 为是么要异步任务 1)Android单线程模型 2)耗时操作放在非主线程中执行 AsyncTask为何而生 1)子线程中跟新UI 2)封装.简化异步操作 pub ...
- Android必学之数据适配器BaseAdapter
什么是数据适配器? 下图展示了数据源.适配器.ListView等数据展示控件之间的关系.我们知道,数据源是各种各样的,而ListView所展示数据的格式则是有一定的要求的.数据适配器正是建立了数据源与 ...
- 2019 年软件开发人员必学的编程语言 Top 3
AI 前线导读:这篇文章将探讨编程语言世界的现在和未来,这些语言让新一代软件开发者成为这个数字世界的关键参与者,他们让这个世界变得更健壮.连接更加紧密和更有意义.开发者要想在 2019 年脱颖而出,这 ...
- Python爬虫工程师必学——App数据抓取实战 ✌✌
Python爬虫工程师必学——App数据抓取实战 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 爬虫分为几大方向,WEB网页数据抓取.APP数据抓取.软件系统 ...
- Android线程管理之AsyncTask异步任务
前言: 前面几篇文章主要学习了线程以及线程池的创建与使用,今天来学习一下AsyncTask异步任务,学习下AsyncTask到底解决了什么问题?然而它有什么弊端?正所谓知己知彼百战百胜嘛! 线程管理相 ...
- Linux必学的60个命令
inux必学的60个命令Linux提供了大量的命令,利用它可以有效地完成大量的工作,如磁盘操作.文件存取.目录操作.进程管理.文件权限设定等.所以,在Linux系统上工作离不开使用系统提供的命令.要想 ...
- Java基础知识强化之网络编程笔记15:Android网络通信之 Android异步任务处理(AsyncTask使用)
AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的 ...
- Linux必学的命令
Linux必学的命令Linux提供了大量的命令,利用它可以有效地完成大量的工作,如磁盘操作.文件存取.目录操作.进程管理.文件权限设定等.所以,在Linux系统上工作离不开使用系统提供的命令.要想真正 ...
随机推荐
- 34、iOS App图标和启动画面尺寸
注意:iOS所有图标的圆角效果由系统生成,给到的图标本身不能是圆角的. 1. 桌面图标 (app icon) for iPhone6 plus(@3x) : 180 x 180 for iPhone ...
- Linux 第三天
2.文件处理命令 1)touch 创建空文件 语法:touch文件名 2)cat 显示文件内容 英文原意:concatenate 语法:cat 文件名 常用选项: -n:number,显示行号 3)t ...
- Java基础语法学习知识
基础概念 标识符1.由字母,数字,下划线,美元符组成2.首字母不能是数字3.不能是关键字和保留字4.能反映其作用 关键字1.有特定含义2.用于特定地方3.用来命名标识符 常量1.程序执行时值不变的量2 ...
- 在平台中使用JNDI 数据源
有些情况下为了系统的安全性考虑,可以将数据源配置成JNDI,在程序中只需要使用 容器的JNDI路径就可以了. 配置方法 1.在容器中配置数据源 <Context path="/&quo ...
- latex字体
强调 方式:声明:\em 或者 命令\emph,后者是latex2e的命令 区别:声明与命令的作用范围不同:\em改变当前字体直到被其他相应的声明取消(也可以是\em本身),或者当前的环境结束为止,当 ...
- Mybatis-Plus 实战完整学习笔记(八)------delete测试
1.根据ID删除一个员工deleteById /** * 删除客户 * * @throws SQLException */ @Test public void deletedMethod() thro ...
- 第31章:MongoDB-索引--复合索引
①复合索引 对多个字段创建索引被称为复合索引或者组合索引 ②创建组合索引 创建组合索引(以后台模式创建) db.user.ensureIndex({"username": 1, & ...
- [小结]了解innodb锁
原创文章,会不定时更新,转发请标明出处:http://www.cnblogs.com/janehoo/p/5603983.html 背景介绍: innodb的锁分两类:lock和latch. 其中la ...
- Html5与Css3知识点拾遗(一)
1.元素 空元素: 可选的空格空格和斜杠 <img src="x.jpg" width="300" alt="pic" /> & ...
- PHP与Python哪个做网站产品好?
虽然python现在比较火,但在传统的LAMP组合里Linux+apache/tomcat+MySql+PHP里是PHP做网站的脚本语言,但现在已经变了:https://baike.baidu.com ...