Android3.0之后的版本不允许在UI主线程当中访问网络,为了UI显示流畅,就需要开辟一个子线程来完成对网络的访问。UI主线程和这个子线程是异步的,子线程完成网络下载之后,可以把下载的内容显示在UI界面上。
在Android中实现异步任务机制有两种方式,Handler和AsyncTask。
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。
下面这部分摘自:http://blog.csdn.net/wykwdy007/article/details/7847637AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程和UI线程交互的辅助类。它的实现是通过一个内部的线程池,每个后台任务装载到线程池中的一个线程,然后在执行该线程。AsyncTask抽象出后台线程运行的五个状态:1,准备运行。2,正在后台运行。3,进度更新。4,完成后台任务。5,取消任务。
AsyncTask是一个泛型类,原型为:android.os.AsyncTask<Params, Progress, Result>
Params:启动任务执行的输入参数,比如Http请求的URL。
Progress:后台任务执行的百分比。
Result:后台执行任务的最终返回结果,如String。
要实现的方法:
onPreExecute()------->准备阶段:该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
doInBackground(Params...)------>正在后台运行:该回调函数由后台线程在onPreExecute()方法执行结束后立即调用。通常在这里执行耗时的后台计算。计算的结果必须由该函数返回,并被传递到onPostExecute()中。在该函数内也可以使用publishProgress(Progress...)来发布一个或多个进度单位(unitsof progress)。这些值将会在onProgressUpdate(Progress...)中被发布到UI线程。
onProgressUpdate(Progress...)--------->进度更新:该函数由UI线程在publishProgress(Progress...)方法调用完后被调用。一般用于动态地显示一个进度条。
onPostExcute(Result)-------->完成后台任务:当后台计算结束后调用。后台计算的结果会被作为参数传递给这一函数。
onCancelled()-------------->取消任务:在调用AsyncTask的cancel()方法时调用
为了正确的使用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传入的参数。
AsyncTask的本质是一个线程池,然后提交的异步任务将装载在一个线程中然后执行,当工作的线程需要更UI线程交互时,
实例1:
public class MainActivity extends Activity {
private Button button;
private ImageView imageview;
private String image_path="http://e.hiphotos.baidu.com/image/" +
"h%3D1050%3Bcrop%3D0%2C0%2C1680%2C1050/sign=a7bb27240db30f242a" +
"9ae803fda5ea20/95eef01f3a292df53ccc2050be315c6034a8736c.jpg";
private ProgressDialog dialog;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button)this.findViewById(R.id.button1);
imageview=(ImageView)this.findViewById(R.id.imageView1);
dialog=new ProgressDialog(this);
dialog.setTitle("提示信息");
dialog.setMessage("正在下载,请稍后...");
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//执行异步任务的操作
new MyTask().execute(image_path);
}
});
}
/**使用异步任务的规则:
* 1. 声明一个类继承AsyncTask标注三个参数的类型
* 2. 第一个参数表示要执行的任务通常是网络的路径
* 第二个参数表示的是进度的刻度,第三个参数表示执行的返回结果
*/
public class MyTask extends AsyncTask<String, Void, Bitmap>{
//表示完成耗时操作
protected Bitmap doInBackground(String... params) {
//使用网络链接类HttpClient类完成对网络数据的提取。
HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(params[0]);
Bitmap bitmap=null;
try {
HttpResponse httpResponse=httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode()==200){
HttpEntity httpEntity=httpResponse.getEntity();
byte[] data=EntityUtils.toByteArray(httpEntity);
bitmap=BitmapFactory.decodeByteArray(data, 0, data.length);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
protected void onPreExecute() {//表示任务执行之前的操作
super.onPreExecute();
dialog.show();
}
//主要是更新UI操作
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
imageview.setImageBitmap(result);
dialog.dismiss();
}
}
}
以上就是一个简单的异步操作的例子,在AsyncTask中进行耗时操作,在onPostExecute中更新UI,它是明确的先后执行顺序。以上实例实现了从网络上下载一张图片,并显示的功能。实例2:加入了进度条显示下载的进度
public class MainActivity extends Activity {
......
private String image_path="http://d.hiphotos.baidu.com/image/pic/item/d788d43f8794a4c2a63b262f0cf41bd5ad6e399e.jpg";
private ProgressDialog dialog;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button)this.findViewById(R.id.button1);
imageview=(ImageView)this.findViewById(R.id.imageView1);
dialog=new ProgressDialog(this);
dialog.setTitle("提示信息");
dialog.setMessage("正在下载,请稍后...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setCancelable(false);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//执行异步任务的操作
new MyTask().execute(image_path);
}
});
}
/**使用异步任务的规则:
* 1. 声明一个类继承AsyncTask标注三个参数的类型
* 2. 第一个参数表示要执行的任务通常是网络的路径
* 第二个参数表示的是进度的刻度,第三个参数表示执行的返回结果 */
public class MyTask extends AsyncTask<String, Integer, Bitmap>{
//表示完成耗时操作
protected Bitmap doInBackground(String... params) {
//使用网络链接类HttpClient类完成对网络数据的提取。
HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(params[0]);
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
InputStream inputStream=null;
Bitmap bitmap=null;
try {
HttpResponse httpResponse=httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode()==200){
inputStream=httpResponse.getEntity().getContent();
//先要获得文件的总长度
long file_length=httpResponse.getEntity().getContentLength();
int len=0;
byte[] data=new byte[1024];
int total_length=0;
while((len=inputStream.read(data))!=-1){
total_length+=len;
int values=(int)((total_length/(float)file_length)*100);
publishProgress(values);
outputStream.write(data,0,len);
}
byte[] result=outputStream.toByteArray();
bitmap=BitmapFactory.decodeByteArray(result, 0, result.length);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(inputStream!=null){
try {
inputStream.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
return bitmap;
}
protected void onPreExecute() {//表示任务执行之前的操作
super.onPreExecute();
dialog.show();
}
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
dialog.setProgress(values[0]);
}
//主要是更新UI操作
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
imageview.setImageBitmap(result);
dialog.dismiss();
}
}
}
上面介绍了AsyncTask的基本应用,有些朋友也许会有疑惑,AsyncTask内部是怎么执行的呢,它执行的过程跟我们使用Handler又有什么区别呢?答案是:AsyncTask是对Thread+Handler良好的封装,在android.os.AsyncTask代码里仍然可以看到Thread和Handler的踪迹。
总结:Handler和AsyncTask都是为了主线程不被阻塞UI线程,但是UI的更新只能在UI主线程这个完成这是因为Android的单线程模型,所以要异步的完成。
•线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。
•另外,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。
概括来说,当我们调用execute(Params... params)方法后,execute方法会调用onPreExecute()方法,然后由ThreadPoolExecutor实例sExecutor执行一个FutureTask任务,这个过程中doInBackground(Params... params)将被调用,如果被开发者覆写的doInBackground(Params... params)方法中调用了publishProgress(Progress... values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress... values)方法将被调用;如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。
进阶版:
Android实战技巧:深入解析AsyncTask