Android实战技巧:多线程AsyncTask

标签: android 技巧 多线程 | 发表时间:2012-05-12 19:50 | 作者:hitlion2008
出处:http://blog.csdn.net

Understanding AsyncTask

AsyncTask是Android 1.5 Cubake加入的用于实现异步操作的一个类,在此之前只能用Java SE库中的Thread来实现多线程异步,AsyncTask是Android平台自己的异步工具,融入了Android平台的特性,让异步操作更加的安全,方便和实用。实质上它也是对Java SE库中Thread的一个封装,加上了平台相关的特性,所以对于所有的多线程异步都强烈推荐使用AsyncTask,因为它考虑,也融入了Android平台的特性,更加的安全和高效。

AsyncTask可以方便的执行异步操作(doInBackground),又能方便的与主线程进行通信,它本身又有良好的封装性,可以进行取消操作(cancel())。关于AsyncTask的使用,文档说的很明白,下面直接上实例。

实例

这个实例用AsyncTask到网络上下载图片,同时显示进度,下载完图片更新UI。

package com.hilton.effectiveandroid.concurrent;

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

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

import com.hilton.effectiveandroid.R;

/*
 * AsyncTask cannot be reused, i.e. if you have executed one AsyncTask, you must discard it, you cannot execute it again.
 * If you try to execute an executed AsyncTask, you will get "java.lang.IllegalStateException: Cannot execute task: the task is already running"
 * In this demo, if you click "get the image" button twice at any time, you will receive "IllegalStateException".
 * About cancellation:
 * You can call AsyncTask#cancel() at any time during AsyncTask executing, but the result is onPostExecute() is not called after
 * doInBackground() finishes, which means doInBackground() is not stopped. AsyncTask#isCancelled() returns true after cancel() getting
 * called, so if you want to really cancel the task, i.e. stop doInBackground(), you must check the return value of isCancelled() in
 * doInBackground, when there are loops in doInBackground in particular.
 * This is the same to Java threading, in which is no effective way to stop a running thread, only way to do is set a flag to thread, and check
 * the flag every time in Thread#run(), if flag is set, run() aborts.
 */
public class AsyncTaskDemoActivity extends Activity {
    private static final String ImageUrl = "http://i1.cqnews.net/sports/attachement/jpg/site82/2011-10-01/2960950278670008721.jpg";
    private ProgressBar mProgressBar;
    private ImageView mImageView;
    private Button mGetImage;
    private Button mAbort;
    
    @Override
    public void onCreate(Bundle icicle) {
	super.onCreate(icicle);
	setContentView(R.layout.async_task_demo_activity);
	mProgressBar = (ProgressBar) findViewById(R.id.async_task_progress);
	mImageView = (ImageView) findViewById(R.id.async_task_displayer);
	final ImageLoader loader = new ImageLoader();
	mGetImage = (Button) findViewById(R.id.async_task_get_image);
	mGetImage.setOnClickListener(new View.OnClickListener() {
	    public void onClick(View v) {
		loader.execute(ImageUrl);
	    }
	});
	mAbort = (Button) findViewById(R.id.asyc_task_abort);
	mAbort.setOnClickListener(new View.OnClickListener() {
	    public void onClick(View v) {
		loader.cancel(true);
	    }
	});
	mAbort.setEnabled(false);
    }
    
    private class ImageLoader extends AsyncTask<String, Integer, Bitmap> {
	private static final String TAG = "ImageLoader";

	@Override
	protected void onPreExecute() {
	    // Initialize progress and image
	    mGetImage.setEnabled(false);
	    mAbort.setEnabled(true);
	    mProgressBar.setVisibility(View.VISIBLE);
	    mProgressBar.setProgress(0);
	    mImageView.setImageResource(R.drawable.icon);
	}
	
	@Override
	protected Bitmap doInBackground(String... url) {
	    /*
	     * Fucking ridiculous thing happened here, to use any Internet connections, either via HttpURLConnection
	     * or HttpClient, you must declare INTERNET permission in AndroidManifest.xml. Otherwise you will get
	     * "UnknownHostException" when connecting or other tcp/ip/http exceptions rather than "SecurityException"
	     * which tells you need to declare INTERNET permission.
	     */
	    try {
		URL u;
		HttpURLConnection conn = null;
		InputStream in = null;
		OutputStream out = null;
		final String filename = "local_temp_image";
		try {
		    u = new URL(url[0]);
		    conn = (HttpURLConnection) u.openConnection();
		    conn.setDoInput(true);
		    conn.setDoOutput(false);
		    conn.setConnectTimeout(20 * 1000);
		    in = conn.getInputStream();
		    out = openFileOutput(filename, Context.MODE_PRIVATE);
		    byte[] buf = new byte[8196];
		    int seg = 0;
		    final long total = conn.getContentLength();
		    long current = 0;
		    /*
		     * Without checking isCancelled(), the loop continues until reading whole image done, i.e. the progress
		     * continues go up to 100. But onPostExecute() will not be called.
		     * By checking isCancelled(), we can stop immediately, i.e. progress stops immediately when cancel() is called.
		     */
		    while (!isCancelled() && (seg = in.read(buf)) != -1) {
			out.write(buf, 0, seg);
			current += seg;
			int progress = (int) ((float) current / (float) total * 100f);
			publishProgress(progress);
			SystemClock.sleep(1000);
		    }
		} finally {
		    if (conn != null) {
			conn.disconnect();
		    }
		    if (in != null) {
			in.close();
		    }
		    if (out != null) {
			out.close();
		    }
		}
		return BitmapFactory.decodeFile(getFileStreamPath(filename).getAbsolutePath());
	    } catch (MalformedURLException e) {
		e.printStackTrace();
	    } catch (IOException e) {
		e.printStackTrace();
	    }
	    return null;
	}
	
	@Override
	protected void onProgressUpdate(Integer... progress) {
	    mProgressBar.setProgress(progress[0]);
	}
	
	@Override
	protected void onPostExecute(Bitmap image) {
	    if (image != null) {
		mImageView.setImageBitmap(image);
	    }
	    mProgressBar.setProgress(100);
	    mProgressBar.setVisibility(View.GONE);
	    mAbort.setEnabled(false);
	}
    }
}
运行结果

先后顺序分别是下载前,下载中和下载后

总结

关于怎么使用看文档和这个例子就够了,下面说下,使用时的注意事项:

1. AsyncTask对象不可重复使用,也就是说一个AsyncTask对象只能execute()一次,否则会有异常抛出"java.lang.IllegalStateException: Cannot execute task: the task is already running"
2. 在doInBackground()中要检查isCancelled()的返回值,如果你的异步任务是可以取消的话。
cancel()仅仅是给AsyncTask对象设置了一个标识位,当调用了cancel()后,发生的事情只有:AsyncTask对象的标识位变了,和doInBackground()执行完成后,onPostExecute()不会被回调了,而doInBackground()和onProgressUpdate()还是会继续执行直到doInBackground()结束。所以要在doInBackground()中不断的检查isCancellled()的返回值,当其返回true时就停止执行,特别是有循环的时候。如上面的例子,如果把读取数据的isCancelled()检查去掉,图片还是会下载,进度也一直会走,只是最后图片不会放到UI上(因为onPostExecute()没被回调)!
这里的原因其实很好理解,想想Java SE的Thread吧,是没有方法将其直接Cacncel掉的,那些线程取消也无非就是给线程设置标识位,然后在run()方法中不断的检查标识而已。
3. 如果要在应用程序中使用网络,一定不要忘记在AndroidManifest中声明INTERNET权限,否则会报出很诡异的异常信息,比如上面的例子,如果把INTERNET权限拿掉会抛出"UnknownHostException"。刚开始很疑惑,因为模拟器是可以正常上网的,后来Google了下才发现原来是没权限,但是疑问还是没有消除,既然没有声明网络权限,为什么不直接提示无网络权限呢?
作者:hitlion2008 发表于2012-5-12 19:50:29 原文链接
阅读:7 评论:0 查看评论

相关 [android 技巧 多线程] 推荐:

Android实战技巧:多线程AsyncTask

- - CSDN博客推荐文章
AsyncTask是Android 1.5 Cubake加入的用于实现异步操作的一个类,在此之前只能用Java SE库中的Thread来实现多线程异步,AsyncTask是Android平台自己的异步工具,融入了Android平台的特性,让异步操作更加的安全,方便和实用. 实质上它也是对Java SE库中Thread的一个封装,加上了平台相关的特性,所以对于所有的多线程异步都强烈推荐使用AsyncTask,因为它考虑,也融入了Android平台的特性,更加的安全和高效.

Android多任务多线程下载

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 打算实现一个下载功能,当然理想的功能要支持多任务下载、多线程下载、断点续传的功能,我想一步一步来,首先困难摆在了多任务这里. 开始的思路是在一个Service中启动下载的流操作,然后通过Service中声明一个Activity中的Handler更新UI(比如进度条.

Android调试程序技巧

- - ITeye博客
在开发的过程中我们经常需要调试程序的执行路径,如我们想知道一个Activity的生命周期方法的调用顺序,我们可能会写如下代码. 这样每次都要输入两个参数,有没有更简便的方法呢. 我们可以通过Thread.currentThread().getStackTrace()获取当前堆栈调用信息,从堆栈信息中可以获取当前调用的java文件名,类名,方法名和代码行号.

Android开发--多线程下载加断点续传

- - CSDN博客推荐文章
        文件下载在App应用中也用到很多,一般版本更新时多要用的文件下载来进行处理,以前也有看过很多大神有过该方面的博客,今天我也自己来实践一下,写的一般,还请大家多提意见,共同进步.         1.多线程下载:.                首先通过下载总线程数来划分文件的下载区域:利用int range = fileSize / threadCount;得到每一段下载量;每一段的位置是i * range到(i + 1) * rang  - 1,注意最后一段的位置是到filesize - 1;.

Android通过HTTP协议实现多线程下载

- - 移动开发 - ITeye博客
     * 从路径中获取文件名称 .      * @param path 下载路径 .      * 下载文件 .      * @param path 下载路径 .      * @param threadsize 线程数 .         int filelength = conn.getContentLength();//获取要下载的文件的长度  .

21个Android手机使用小技巧

- - 译言-电脑/网络/数码科技
Android能让用户在桌面创建联系人图标. 长按桌面空白处,当弹出菜单后,选择:快捷方式>联系人,然后就可以在列表里选择想要添加的联系人. 对于冰激凌三明治(ICS)用户就更简单了,直接在首页使用插件菜单中的:”联系人“,”直接拨号“或者”直接发送信息“小插件. 你也可以将联系人分组到不同的文件夹中.

Android代码优化小技巧总结

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 这篇文章主要是介绍了一些小细节的优化技巧,当这些小技巧综合使用起来的时候,对于整个Android App的性能提升还是有作用的,只是不能较大幅度的提升性能而已. 选择合适的算法与数据结构才应该是你首要考虑的因素,在这篇文章中不会涉及这方面.

Android Java层的anti-hooking技巧

- - WooYun知识库
原文: http://d3adend.org/blog/?p=589. 一个最近关于检测native hook框架的方法让我开始思考一个Android应用如何在Java层检测Cydia Substrate或者Xposed框架. 下文所有的anti-hooking技巧很容易就可以被有经验的逆向人员绕过,这里只是展示几个检测的方法.

Google Nexus 7/Android 4.1新手入門技巧

- - 簡睿隨筆
整理了一下最近研讀與測試的Nexus 7小技巧,希望對剛買Nexus 7又沒用過Android系統的朋友們有幫助. 本文會不定期修訂,也請讀者們分享好用的工具以斆學相長. 聽說美國的Google Play Book會送一本Google Nexus 7 Guidebook電子書,但可惜台灣還沒有開放圖書購買也就找不到這本書.

【Android 开发技巧】布局优化利器<include/>和ViewStub

- - CSDN博客移动开发推荐文章
『原创作品,转载请注明出处. 〔文章原始地址  http://blog.csdn.net/manoel/article/details/39036507〕. 当创建复杂的布局的时候,有时候会发现添加了很多的ViewGroup和View. 随之而来的问题是View树的层次越来越深,应用也变的越来越慢,因为UI渲染是非常耗时的.