【Android笔记】AsyncTask的使用和介绍(获取网络图片与进度条实例)

什么是异步

首先我们要先知道什么是异步、什么是同步:

同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程,同步是阻塞模式。

异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待,异步是非阻塞模式。

 

 

为什么要异步任务

然后我们开始介绍为什么我们需要异步任务这个功能,也就是AsyncTask

 

因为Android是单线程模型,只有UI线程也就是主线程才能对UI进行操作,而其他workerThread(工作线程)是不能直接操作UI的,这样做的好处是保证了UI的稳定性准确性,避免多线程对UI进行操作而造成混乱。

但是同时Android也是一个多线程的操作系统,我们不可能把所有的事情都放到主线程中去做,比如一些网络操作,和读取文件这些耗时的操作。如果你都放到主线程去执行,这就会造成后面任务的阻塞,Android系统会去检测这样一个阻塞,当阻塞时间太长的时候,系统就会抛出Application Not Responding的无反应提示框(ANR

 

所以我们要将耗时操作放到非主线程去执行,这样既保证了android的单线程模型,也避免了程序的一个ANR。

 

 

 

提到异步任务,我们最直接想到的是用线程和线程池去实现,Android给我们提供了一个主线程与其他线程的通讯机制,同时Android也给我们提供了一个封装好了组件AsyncTask,利用她我们可以很方便的实行异步任务

 

 

 

 

AsyncTask为何而生

 

 

总结下来就是两条,第一、它可以在子线程中去更新UI。第二、它封装简化了异步操作。

上边我们提到,我们要是想异步任务,通常我们可以通过线程和线程池去实现。但这里就涉及到了一个线程同步、和一个线程的管理,同时当线程结束的时候,我们还需要用handler去通知主线程去更新UI。而AsyncTask将这一切都封装了起来,我们可以非常方便的在子线程中去更新UI。

总之,什么是AsyncTask

AsyncTask就是一个封装好的线程池,比起Thread+Handler的方法,AsyncTask在操作UI线程上更方便,因为onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均运行在主线程中,这样就不用Handler发消息处理了。

 

AsyncTask的基本结构介绍

 

一、构建AsyncTask子类的参数

要传入的三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用Java.lang.Void类型代替。

 

二、构建AsyncTask子类的回调方法

 

doInBackground:必须重写的方法,所有耗时的操作都将在这个方法中进行,此方法将接收输入参数和返回计算结果。在执行过程中可以调publishProgress(Progress...values)来更新进度信息。

onPreExecute: 通常将一些初始化操作放在这里执行

 

onPostExecute: dolnBackground执行后,立即被调用,传入dolnBackground方法所返回的值,即展示我们处理完的结果,将结果显示到UI组件上。

 

onProgressUpdate: dolnBackground执行事,调用了publishProgress方法,自动触发该方法,直接将进度信息更新到UI组件上,通过这个方法,我们可以清楚的知道当前耗时操作的完成进度。

另外有个重点要说明的是,这四个函数中只有doInBackground是在其他线程执行的,需要异步处理,其他三个函数都是在主线程中调用的,也就是说只有doInBackground方法内部是不能出现更新UI的操作

 

除了这四个函数之外,当然我们还要一个方法也要说一下

Execute:执行一个异步任务,触发异步任务的执行。简而言之,这个函数就是执行的意思,就类似于Thread线程中的start方法,但这个方法只被允许在UI线程中调用。

 

执行顺序:

Execute->onPreExecute->dolnBackground->(onProgressUpdate)->onPostExecute

首先我们通过execute方法开启执行一个异步任务,然后我们通过onPreExecute方法去完成开始异步任务前的一些准备操作,比如初始化。然后再调用dolnBackground方法来执行真正的耗时操作,如果在该方法里执行了publishProgress方法,则接下里调用onProgressUpdate方法用于获取进度,更新进度条,然后再执行onPostExecute方法,更新UI

 

顺序验证:

 

新建一个MyAsyncTask.java,继承于AsyncTask,所有参数暂时使用Java.lang.Void类型代替,只有doInBackground方法是必须要重写的,同时我们在每个函数中都加入一个Log,方便我们知道他们之间执行的顺序。

 

然后我们在虚拟机跑一跑,执行顺序就出来了,严格执行,我们上面所说的顺序

 

 

AsyncTask实际操作(网络获取图片)

 

网络操作式一个不稳定的耗时操作,从Android4.0开始就被严禁放进主线程中。所以在加载网络图片的操作中,我们需要在异步处理下载图片,并且在UI线程中去设置图像

 

首先我们准备一张网络图片的链接,要是失效了就去百度搜索一张就行了

http://image.sinajs.cn/newchart/monthly/n/sh601003.gif

 

actvity_main.xml

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button
        android:id="@+id/btn_loadImage"     
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="46dp"
        android:layout_marginTop="80dp"
        android:text="Image Test" />

</RelativeLayout>

 

 

image.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ProgressBar        
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

</LinearLayout>

ImageTest.java

 

package bnuz.lwj.asynctaskteaching;

import java.net.URLConnection;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class ImageTest extends Activity{

	private ImageView mImageView;
	private ProgressBar mProgressBar;
	private String URL = "http://image.sinajs.cn/newchart/monthly/n/sh601003.gif";
	MyAsyncTask myAsyncTask;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.image);
		mImageView=(ImageView) findViewById(R.id.image);
		mProgressBar=(ProgressBar) findViewById(R.id.progressBar1);
		//设置传递进去的参数,是doInBackground获取的
		MyAsyncTask myAsyncTask=new MyAsyncTask(mProgressBar,mImageView);
		myAsyncTask.execute(URL);
		
	}		
	@Override
	protected void onPause() {
		//当一个Activity跳转到另一个activity是调用
		super.onPause();
		
		if(myAsyncTask!=null && myAsyncTask.getStatus()==AsyncTask.Status.RUNNING){
			//这并不是真正的关闭这个异步任务,关闭线程,只是给线程标记了一个关闭的信号
			//还需要在线程内部判断标记信号为什么是跳出线程
			//目的是让这个异步任务和ImageTest的Activity的周期同步,一起存货,以免出现bug
			myAsyncTask.cancel(true);
		}
		
		
	}
			
}

MyAsyncTask.java

 

package bnuz.lwj.asynctaskteaching;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;

//我们要传入一个URL,所以第一个参数我们定义为String类型
//第二个参数是进度,所以我们传进去一个integer,它就是publishProgress(value)的参数类型
//因为我们需要返回的一个参数类型是一张图片,所以设置为bitmap类型
//从这里我们可以知道,以后如果有返回复杂参数的时候,我们还需要定义javaBean类型
public class MyAsyncTask extends AsyncTask<String,Integer,Bitmap>{
	
	private ProgressBar mProgressBar;
	private ImageView mImageView;
			
	public MyAsyncTask(ProgressBar mProgressBar, ImageView mImageView) {
		super();
		this.mProgressBar = mProgressBar;
		this.mImageView = mImageView;
	}

	@Override
	protected void onPreExecute() {			
		super.onPreExecute();
		
		//将隐藏的mProgressBar显示出来
		mProgressBar.setVisibility(View.VISIBLE);
		
		
	}			
	//该方法传进来的是一个可变长的数组String... params
	//因为我们只传进来一个URL,所以只要取这个数组索引为0的值就行了
	@Override
	protected Bitmap doInBackground(String... params) {

		String url=params[0];
		Bitmap bitmap=null;			
		URLConnection connection;
		InputStream is;
		//模拟更新进度
		for(int i=0;i<100;i++){
			//判断
			if(isCancelled())
			{
				//跳出线程
				break;
			}			
			publishProgress(i);
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
		}
		//通过URL.openConnection方法获得一个URLConnection对象			
		try {
			
			URL myurl = new URL(url);
			connection=myurl.openConnection();
			connection.connect();
			is=connection.getInputStream();			
			//包装一下
			BufferedInputStream bis=new BufferedInputStream(is);
			//将一个输入流解析为一个BitMap对象
			bitmap=BitmapFactory.decodeStream(is);
			//关闭输入流
			is.close();
			bis.close();
		} catch (MalformedURLException e) {
			Log.i("info", "MalformedURLException");
			e.printStackTrace();
		} catch (IOException e) {
			Log.i("info", "IOException:"+e.toString());
			e.printStackTrace();
		}
					
		return bitmap;
	}
	
	//操作UI,获取doInBackground从返回的bitmap图像
	@Override
	protected void onPostExecute(Bitmap bitmap) {
		super.onPostExecute(bitmap);
		/*mProgressBar.setVisibility(View.GONE);*/
		mImageView.setImageBitmap(bitmap);
		this.cancel(true);
		
		
	}
	
	@Override
	protected void onProgressUpdate(Integer... values) {			
		super.onProgressUpdate(values);
		if(isCancelled()){
			return;
		}
		//从doInBackground的publishProgress方法获取到i值
		//为什么是value[0],原理跟doInBackground获取URL原理一样
		mProgressBar.setProgress(values[0]);
		
	}
	
}

MainActvity.java

 

package bnuz.lwj.asynctaskteaching;


import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;

import android.widget.ImageView;
import android.widget.ProgressBar;


public class ImageTest extends Activity{

	private ImageView mImageView;
	private ProgressBar mProgressBar;
	private String URL = "http://image.sinajs.cn/newchart/monthly/n/sh601003.gif";
	MyAsyncTask myAsyncTask;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.image);
		mImageView=(ImageView) findViewById(R.id.image);
		mProgressBar=(ProgressBar) findViewById(R.id.progressBar1);	
		//实例一个异步任务	
		MyAsyncTask myAsyncTask=new MyAsyncTask(mProgressBar,mImageView);
		//设置传递进去的参数,是doInBackground获取的
		myAsyncTask.execute(URL);
		
	}		
	@Override
	protected void onPause() {
		//当一个Activity跳转到另一个activity是调用
		super.onPause();
		
		if(myAsyncTask!=null && myAsyncTask.getStatus()==AsyncTask.Status.RUNNING){
			//这并不是真正的关闭这个异步任务,关闭线程,只是给线程标记了一个关闭的信号
			//还需要在线程内部判断标记信号为什么是跳出线程
			//目的是让这个异步任务和ImageTest的Activity的周期同步,一起存货,以免出现bug
			myAsyncTask.cancel(true);
		}
		
		
	}
			
}

 

注意:最后别忘记了在AndroidManifest.xml加入网络权限和给新建的ImageTest这个Activity注册奥

在<uses-sdk android:minSdkVersion="21"  android:targetSdkVersion="21" />下输入

 

	<uses-permission android:name="android.permission.INTERNET" />

在MainActivity的注册下面输入

 

 

<activity android:name=".ImageTest"></activity>


另外在写这个例子的时候,我因为网络问题报了IO异常,折腾了一几乎一天,大家可以注意一下

 

URLConnection解析URL的UnknownHostException异常解决办法

 

效果就是从MainActivity点击button跳转到ImageTest,然后进度条先执行到100,再显示图片

 

 

总结到此结束~

 

欢迎关注我的博客,一起学习讨论

要转载,请附上原文链接,作者:SnailMann

完整源码下载

 

传送门:【Android学习笔记系列】BaseAdapter适配器的介绍、使用及优化(详细)

 

大家也可以关注我的私人github: https://github.com/SnailMann,欢迎watch ,star, fork

关注我的私人GitHub

 

虽然现在暂时没有什么东西,但是总会有的大笑

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值