AsnyncTask与handler(一)——AsyncTask异步处理

 前言:以前就遇到过AsnyncTask,但由于当时对其理解比较懵懂,所以就先放了一下,没写博客,时隔几月,当再次遇到此问题时,感觉难度更大,却有必要仔细研究一下,这里分两篇写,这篇先写AsncTask的异步处理的实现,第二篇对AsyncTask进行补充,讲述handler消息机制。

一、android开发中存在的问题 

当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。也就是说所有有关更新界面的操作必须在主线程中操作,除主线程外的其它自建线程都没有这个权力处理这些UI更新操作,否则将报错!在后面的讲解中会看到即便是最常用的Toast.makeText()也是不允许在非主线程中使用的!

比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后,是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员  

在单线程模型中始终要记住两条法则:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包 ,即更新UI

 二、AsyncTask

 但我们在处理程序时,常常是先做数据处理,然后再数据准备好后更新UI,如果按照上面的单线程模式,就出现了一个问题,更新UI必须写在OnCreate函数中,但我们数据处理操作一般是要自建线程的,如果全部写在OnCreate函数中,那么代码就难以规范。所以为了解决数据处理后更新UI的问题,就产生了AsyncTask!

AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。

 <一>子类化AsyncTask

 AsyncTask是抽象类 ,我们必须自己写一个类来继承AsyncTask 类

 <二>实现AsyncTask中定义及须重写的方法 

重写AsyncTask后可以或必须重写的函数如下:

  • onPreExecute() 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。
  • doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。
  • publishProgress 该方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
  • onProgressUpdate(Progress...), 在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,可以通过一个进度条进行展示。
  • onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

自己写一个类继承自AsyncTask后的形式如下所示:

class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>
{
	……………………
}

可以看到有三个参数:

AsyncTask的三个泛型参数说明(三个参数可以是任何类型)
第一个参数:传入doInBackground()方法的参数类型,这里是String
第二个参数:传入onProgressUpdate()方法的参数类型,这里是Integer
第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。这里是Bitmap

<三>AsyncTask遵守准则 

 为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
<1>Task的实例必须在UI thread中创建
<2>execute方法必须在UI thread中调用
<3>不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
<4> 该task只能被执行一次,否则多次调用时将会出现异常

 <四>实例

 效果:

            初始状态                                       加载中                                           完成

   

效果讲解:总体上来讲是从网上加载一张图片并贴到当前XML的指定位置

1、添加网络访问权限

在AndroidManifest.xml文件中,添加下面一行代码,获取互联网访问权限

<uses-permission android:name="android.permission.INTERNET"/>
2、XML布局
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/show"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="下一个" />

    <ProgressBar   
        android:id="@+id/processBar"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"
        android:visibility="gone"/>  
    <HorizontalScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none" >

        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </HorizontalScrollView>

</LinearLayout>

关于布局,难度不大,也不是本篇重点,不再多讲,这里仅说下作用。
progressBar初始化为不显示,仅当用户点击“下一个”按钮时,显示,图片加载完成后取消显示。
imageView用来显示加载后的图片。

3、JAVA代码 

 先贴出完整代码,然后再逐步讲解

package com.example.try_asynctask;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends Activity {

	private ImageView	image		= null;
	private Button		show;
	private ProgressBar	progressBar	= null;
	private int			number		= 0;
	List<String>		imageUrl	= null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		progressBar = (ProgressBar) findViewById(R.id.processBar);
		image = (ImageView) findViewById(R.id.image);
		show = (Button) findViewById(R.id.show);
		show.setOnClickListener(new showButtonListener());
		
		imageUrl = new ArrayList<String>(); // 图片地址List
		imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");
		imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");
		imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");
		imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");
		imageUrl.add("http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg");
	}

	public class showButtonListener implements OnClickListener
	{
		@Override
		public void onClick(View v)
		{
			number++;
			MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());
			myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));
		}
	}
	
	class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>
	{
		// 可变长的输入参数,与AsyncTask.exucute()对应
		public MyAsyncTask(Context context)
		{
			progressBar.setVisibility(View.VISIBLE);
			image.setVisibility(View.GONE);
		}
		@Override
		protected Bitmap doInBackground(String... params)
		{
			Bitmap bitmap = null;
			try
			{
				//根据URL取得图片并返回 
                URL url = new URL(params[0]);  
                
                URLConnection conn = url.openConnection();  
                conn.connect();  
                InputStream inputStream = conn.getInputStream();  
                bitmap = BitmapFactory.decodeStream(inputStream);  
                
				Toast.makeText(getApplicationContext(), "传回图片了", Toast.LENGTH_SHORT).show();
				inputStream.close();
			}
			catch (Exception e)
			{
				Log.e("msg", e.getMessage());
			}
			return bitmap;
		}

		/**
		 * 在doInBackground 执行完成后,onPostExecute方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
		 */
		@Override
		protected void onPostExecute(Bitmap bitmap)
		{
			progressBar.setVisibility(View.GONE);
			image.setVisibility(View.VISIBLE);
			if (bitmap != null)
			{
				image.setImageBitmap(bitmap);
			}
			else
			{
				Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();
			}
		}

		/**
		 * 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。
		 */
		@Override
		protected void onPreExecute()
		{
			// 任务启动
			Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();
		}
	}

}

1、先看OnCreate函数中操作

imageUrl = new ArrayList<String>(); // 图片地址List
imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");
imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");
imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");
imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");
imageUrl.add(http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg);

定义了一个StringArrayList,用来保存要访问的网址集合,点击“下一个”时,逐个循环显示 

progressBar = (ProgressBar) findViewById(R.id.processBar);
image = (ImageView) findViewById(R.id.image);
show = (Button) findViewById(R.id.show);
show.setOnClickListener(new showButtonListener());

 然后是初始化控件变量,并为“下一个”按钮设置监听函数showButtonListener。

2、“下一个”按钮监听函数showButtonListener

public class showButtonListener implements OnClickListener
{
	@Override
	public void onClick(View v)
	{
		number++;
		MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());
		myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));
	}
}

核心在于最后一句话,imageUrl.get(number % imageUrl.size())循环得到传进去的网址,然后传给myAsyncTask.execute(),所以这里往MyAsyncTask传的参数是String类型,所以在MyAsyncTask派生自AsyncTask的时候,第一个参数也应当是String类型!这就是上面所讲的三个参数中的第一个的意义。
下面就开始今天的核心问题MyAsyncTask的讲解:

3-1 MyAsyncTask构造函数(可省略)

public MyAsyncTask(Context context)
{
	progressBar.setVisibility(View.VISIBLE);
	image.setVisibility(View.GONE);
}

这里是对XML作初始化设置,其实完全可以在onPreExecute()函数中操作是一样的。
3-2 onPreExecute()处理前操作(可省略)

@Override
protected void onPreExecute()
{
	// 任务启动
	Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();
}

代码没什么好讲的,这里只是想说,这个函数对于类派生不是必须要重写的,如果需要在数据处理前要在UI上加以提示等等的预处理操作,都可以放在这里操作。
3-3 doInBackground()后台数据处理操作(必写)

 这里必须注意的一点是,在这个函数里只能做数据处理操作,不能涉及任何更新UI的操作,即便使用Toast.makeText()也会报错!

 代码:

@Override
protected Bitmap doInBackground(String... params)
{
	Bitmap bitmap = null;
	try
	{
		//根据URL取得图片并返回 
        URL url = new URL(params[0]);  
        
        URLConnection conn = url.openConnection();  
        conn.connect();  
        InputStream inputStream = conn.getInputStream();  
        bitmap = BitmapFactory.decodeStream(inputStream);  
        
        //注意!!!!这里写了个Toast.makeText!!!
		Toast.makeText(getApplicationContext(), "传回图片了", Toast.LENGTH_SHORT).show();
		inputStream.close();
	}
	catch (Exception e)
	{
		Log.e("msg", e.getMessage());
	}
	return bitmap;
}

这行的核心意思就是根据得到的URL获取BitMap对象,然后返回!几点注意:
1、获取传递过来值的方法:

URL url = new URL(params[0]);  

2、程序报错

在实际运行时,根本无法看到“传回图片了”消息提示,在看LogCat,已经报错了,错误信息如下:

 

 这里不影响程序运行是因为外面加着try-catch呢,如果去掉程序就会直接崩了。所以,在doInBackground中不准做任何的UI更新操作,即便是Toast也不行!!!!

 3-4 onPostExecute()对返回结果处理,更新UI(必写)

@Override
protected void onPostExecute(Bitmap bitmap)
{
	progressBar.setVisibility(View.GONE);
	image.setVisibility(View.VISIBLE);
	if (bitmap != null)
	{
		image.setImageBitmap(bitmap);
	}
	else
	{
		Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();
	}
}

这个函数中处理doInBackground的返回结果,更新UI

至此本篇关于AsyncTask异步处理的操作就全部讲完了,这里存在一个问题,如果我们想在doInBackground处理过程中更新UI怎么办?下篇讲述的handler消息机制,就可以解决这个问题。 

 

 参考文章:

《Android进阶2之AsyncTask实现异步处理任务》:http://www.cnblogs.com/snake-hand/archive/2012/03/30/2454368.html

 《AsyncTask<>的参数介绍》:http://my.eoe.cn/xuliangbo/archive/6063.html

 

源码地址:http://download.csdn.net/detail/harvic880925/7275089  不要分,仅供分享

 

 请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/24774883 ,不胜感激!

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值