AsyncTask 总结


介绍:

中文API http://www.cnblogs.com/over140/archive/2011/02/17/1956634.html

一、结构

public abstract class AsyncTask extends Object

java.lang.Object

android.os.AsyncTask<Params, Progress, Result>

  二、类概述

  AsyncTask能够适当地、简单地用于 UI线程。 这个类不需要操作线程(Thread)就可以完成后台操作将结果返回UI

  异步任务的定义是一个在后台线程上运行,其结果是在 UI线程上发布的计算。 异步任务被定义成三种泛型类型: ParamsProgress和 Result;和四个步骤: begin ,doInBackgroundprocessProgress 和end


       用法:


在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。在单线程模型中始终要记住两条法则: 
1. 不要阻塞UI线程 
2. 确保只在UI线程中访问Android UI工具包 
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。 
比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后,是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员 。

android提供了几种在其他线程中访问UI线程的方法。 
Activity.runOnUiThread( Runnable ) 
View.post( Runnable ) 
View.postDelayed( Runnable, long ) 
Hanlder 
这些类或方法同样会使你的代码很复杂很难理解。然而当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕。 

为了解决这个问题,Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。 
AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。 
  Params 启动任务执行的输入参数,比如HTTP请求的URL。 
  Progress 后台任务执行的百分比。 
  Result 后台执行任务最终返回的结果,比如String。 

AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。 
  1) 子类化AsyncTask 
  2) 实现AsyncTask中定义的下面一个或几个方法 
     onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。 
    doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。 
    onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。 
    onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread. 

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


doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

案例:阅读 http://www.cnblogs.com/dawei/archive/2011/04/18/2019903.html


实际应用

AsyncTask cancel    见:  http://apps.hi.baidu.com/share/detail/42189753

Android1.5版本后,提供了AsyncTask这个类用来实现非阻塞的操作,比如下载列表数据等。AsyncTask虽然解决了UI主线程阻塞的问题,但是也有一个众所周知的问题:就是一旦运行了,就没办法停止下来。
如下图所示,系统正在获取当前目录下的子目录和图片的时候,数据还没完全加载完毕。这个时候,用户等不耐烦了点击某个子目录(比如DCIM这个目录),系 统会启动一个新的AsyncTask去获取该子目录下的数据。但是由于之前的Task没法停止,我们常常会发现一个比较恼火的问题,就是原本属于上级目录 的数据也会显示到当前子目录下来。

   

查看了一下开源项目 PhotoStream的代码,终于有了一个好的解决办法。为了演示,我们定义两个类:PhotoTask 和 PhotoGridView。PhotoTask 接收到目录路径后,获取指定目录下的子目录和图片文件。PhotoGridView主要是显示目录和图片。

public class PhotoTask extends AsyncTask<String, File, Void>{
    
    @Override
    protected Void doInBackground(String... path) {
        File parent = new File(path[0]);
        if (parent.isDirectory()) {
            // 获取当前目录下的目录和文件
            File[] files = parent.listFiles();
            for (int j = 0; j < files.length; j++) {
                if(isCancelled()) return null;// Task被取消了,马上退出循环
                
                File file = files[j];  
                publishProgress(file );
            }
        }
    }

    @Override
    public void onProgressUpdate(File... files) {
        if(isCancelled()) return;   // Task被取消了,不再继续执行后面的代码
        .........
    }
}


public class PhotoGridView extends GridView implements OnItemClickListener{
    private PhotoTask task;  // 保持对Task的引用
    
    @Override
    public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
        if (task != null && task.getStatus() == AsyncTask.Status.RUNNING) {
            task.cancel(true);  //  如果Task还在运行,则先取消它
        }
        ......
        // 启动新的任务
        task = new PhotoTask();
        task.execute(path);
        
    }
}

经过上面的改造,终于不再出现上级目录的数据出现在子目录下面的情况了


     我的方法:

if (!(myTask != null && myTask.getStatus()==AsyncTask.Status.RUNNING)) {
			myTask = new PageTask();
			myTask.execute(itemMap.get(viewHolder.position),viewHolder);
			Log.w("测试", "新线程");
        }else if(myTask != null && myTask.getStatus()!=AsyncTask.Status.RUNNING){
			myTask.cancel(true);
			myTask=null;
			myTask = new PageTask();
			myTask.execute(itemMap.get(viewHolder.position),viewHolder);
			Log.w("测试", "关闭旧线程,开启新线程");
		}else {
			Log.w("测试", "线程正在运行");
		}
 点击关闭线程

progressDialog.setButton("取消", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				myTask.cancel(true);
				myTask=null;
			}
		});


 线程断点下载的全部代码

/**
	 * 线程 下载
	 *
	 */
	private class PageTask extends AsyncTask<Object, Integer, String> {
        private CityListItem mitem;
        private ViewHolder mViewHolder;
        private static final int GET_PACK_INFO = 0;// 最新包信息
        private static final int DOWNLOAD_FINISH = 1;// 下载完成
        private static final int UNPACK_FINISH = 2;// 解压完成
        private static final int UPDATE_FINISH = 3;// 更新完成
        private static final int UNPACK_ERROR = 4;// 解压有误
        private static final int DELETE_TEM_DIR = 5;// 删除零时目录
        private static final int ALL_SUCCESS = 6;// 跟新成功
        private static final int NO_PACK_INFO = 7;// 无包信息
        private static final int UPDATE_BAR = 8;// 更新进度
        @Override  
        protected String doInBackground(Object... params) {  
        	String website;
        	try {
        		mitem=(CityListItem)params[0];
        		mViewHolder=(ViewHolder)params[1];
				// 获取包信息....
        		publishProgress(GET_PACK_INFO);
				String packName = PackDataApi.getPack(mitem.city.getId(),
						LeziyouConstant.PACK_TYPE_3);
				if (packName.equals("")) {
					publishProgress(NO_PACK_INFO);
				} else {
					publishProgress(DOWNLOAD_FINISH);
				}
				// 下载包...
				website = CityDownloadActivity.this.getResources().getString(R.string.webSite)
							+ File.separator + "api" + File.separator + packName;
				
				Log.w("Url", website);
				Long nPos = 0l;
				long localSize = 0;
				File file = new File(mitem.packPath);
				if (!file.getParentFile().exists()) {
					file.getParentFile().mkdirs();
				}
				if (!file.exists()) {
					file.createNewFile();
				} else {
					localSize = file.length();
				}
				int totalSize = FileUtil.getRemoteFileSize(website);
				
				if (localSize < totalSize && localSize != 0) {
					Log.w("downLoad", "文件續傳");
					nPos = localSize;
				}
				URL url = new URL(website);
				HttpURLConnection httpConnection = (HttpURLConnection) url
						.openConnection();
				// 设置User-Agent
				httpConnection.setRequestProperty("User-Agent", "NetFox");
				// 设置断点续传的开始位置
				httpConnection.setRequestProperty("RANGE", "bytes=" + nPos + "-");
				// 获得输入流
				InputStream input = httpConnection.getInputStream();
				RandomAccessFile oSavedFile = new RandomAccessFile(mitem.packPath,"rw");
				// 定位文件指针到nPos位置
				oSavedFile.seek(nPos);
				byte[] b = new byte[1024];
				int nRead;
				int con = (int) localSize;
				// 从输入流中读入字节流,然后写到文件中
				int i = 0;
				int temp = 0;
				while ((nRead = input.read(b, 0, 1024)) > 0
						&& AppCache.get("isStop") == null) {
					if(isCancelled()) return null;// Task被取消了,马上退出循环
					(oSavedFile).write(b, 0, nRead);
					con += nRead;
					i++;
					if (i % 10 == 0) {
						temp = (con * 100) / totalSize + 1;
						if(temp%10==0){
							Log.w("测试 temp", temp + "");
						}
						publishProgress(UPDATE_BAR,(int)temp);
					}
				}
				input.close();
				oSavedFile.close();
				
				if (AppCache.get("isStop") != null
						&& AppCache.get("isStop").equals("true")) {
					Log.w("isStop", "true");
					AppCache.remove("isStop");
				}
				
				publishProgress(UNPACK_FINISH);
				// 解压
				if (PackDataApi.unPack(mitem.packPath)) {
					// 安装数据
					publishProgress(UPDATE_FINISH);
					PackDataApi
							.updateData(LeziyouConstant.PACK_TYPE_3,
									mitem.packPath.substring(0, mitem.packPath
											.lastIndexOf(".")), mitem.city
											.getId());
					publishProgress(DELETE_TEM_DIR);
					PackDataApi.delTemDir(mitem.packPath);
					publishProgress(ALL_SUCCESS);
				} else {
					publishProgress(GET_PACK_INFO);
					PackDataApi.delTemDir(mitem.packPath);
				}
				return "success";
			} catch (Exception e) {
				e.printStackTrace();
				if(null!=mitem.packPath){
					PackDataApi.delTemDir(mitem.packPath);
				}
				return null;
			}
		}  
  
        @Override  
        protected void onCancelled() {   
            super.onCancelled();
            mitem.tvCityName=mitem.city.getName() + "[下载未完成]";
            mitem.btDownload="继  续";
            mViewHolder.btDownload.setClickable(true);
			AppCache.put("isStop", "true");
			if(mitem.isFlag==true){//下载完成后解压安装中,则删除包重置
				mitem.tvCityName=mitem.city.getName();
				mitem.btDownload="下  载";
			}
			mViewHolder.updataview(mitem);
        }   
  
        @Override  
        protected void onPostExecute(String result) {
        	if(null!=result){
        		mitem.updateDownload("更  新", "[已更新]");
				progressDialog.setMessage("[已更新]");
				mViewHolder.btDownload.setClickable(true);
				mitem.tvCityName=mitem.tvCityName;
				mitem.btDownload=mitem.btDownload;
        	}
        	mViewHolder.updataview(mitem);
            progressDialog.dismiss();
        }   
  
        @Override  
        protected void onPreExecute() {   
            // 任务启动,可以在这里显示一个对话框,这里简单处理   
        	progressDialog.show();
        }   
  
        @Override  
        protected void onProgressUpdate(Integer... values) {
        	if(isCancelled()) return;// Task被取消了,不再继续执行后面的代码
        	int i=values[0];
        	switch (i) {
			case GET_PACK_INFO:// 最新包信息
				mViewHolder.btDownload.setClickable(false);
				progressDialog.setMessage("[请求中...]");
				mitem.updateDownload("", "[请求中...]");
				break;
			case DOWNLOAD_FINISH:// 下载完成
				progressDialog.setMessage("[正在下载...]");
				mitem.updateDownload("", "[正在下载...]");
				break;
			case UNPACK_FINISH:// 解压完成
				mitem.updateDownload("", "[解压中...]");
				progressDialog.setMessage("[解压中...]");
				break;
			case UPDATE_FINISH:// 更新完成
				progressDialog.setMessage("[更新中...]");
				mitem.updateDownload("", "[更新中...]");
				break;
			case UNPACK_ERROR:// 解压有误
				progressDialog.setMessage("[解压有误...]");
				mitem.updateDownload("", "[解压有误...]");
				break;
			case DELETE_TEM_DIR:// 删除零时目录
				mitem.updateDownload("", "[删除临时目录...]");
				progressDialog.setMessage("[删除临时目录...]");
				break;
			case NO_PACK_INFO:// 无包信息
				mitem.updateDownload("", "[暂无]");
				mViewHolder.btDownload.setClickable(false);
				break;
			case ALL_SUCCESS:// 跟新成功!
				mitem.updateDownload("更  新", "[已更新]");
				progressDialog.setMessage("[已更新]");
				progressDialog.dismiss();
				mViewHolder.btDownload.setClickable(true);
				break;
			case UPDATE_BAR:// 更新进度   
				progressDialog.setProgress(values[1]);
				break;
			default:
				break;
			}
        	mViewHolder.updataview(mitem);
        }   
        
	}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值