android基础学习之AsyncTask


AsyncTask是Android用来实现异步操作的一个类,因为被Android创造时加入了android的平台特性,因此,AsyncTask的异步操作相对于Thread来说,更安全、方便和实用。但其实质上也是对Thread的一个封装。与Handler很相似,都是为了使一些耗时操作不会堵塞android主线程并解决android的非主线程不能进行UI操作,而诞生的。

AsyncTask可以很方便的执行异步操作(doinBackground()),又能很方便的与主线程进行通讯,而且其又有很好的封装性,所以能够进行取消( cancel())操作。

下面给出示例代码:通过网络下载一张图片并显示。

1package com.cao.asynctasktest; 2 3import java.io.IOException; 4import java.io.InputStream; 5import java.io.OutputStream; 6import java.net.HttpURLConnection; 7import java.net.MalformedURLException; 8import java.net.URL; 9 10import android.app.Activity; 11import android.content.Context; 12import android.graphics.Bitmap; 13import android.graphics.BitmapFactory; 14import android.os.AsyncTask; 15import android.os.Bundle; 16import android.os.SystemClock; 17import android.util.Log; 18import android.view.View; 19import android.widget.Button; 20import android.widget.ImageView; 21import android.widget.ProgressBar; 22 23/** 24 * @author caohuize 25 * AysncTaskDemo 通过网络下载一个图片的状态来学习AysncTask 26 */ 27public class MainActivity extends Activity { 28 private static final String ImageUrl = "http://i1.cqnews.net/sports/attachement/jpg/site82/2011-10-01/2960950278670008721.jpg"; 29 private ProgressBar mProgressBar; 30 private ImageView mImageView; 31 private Button mGetImage; 32 private Button mAbort; 33 34 @Override 35 public void onCreate(Bundle icicle) { 36 super.onCreate(icicle); 37 setContentView(R.layout.activity_main); 38 mProgressBar = (ProgressBar) findViewById(R.id.async_task_progress); 39 mImageView = (ImageView) findViewById(R.id.async_task_displayer); 40 final ImageLoader loader = new ImageLoader(); 41 mGetImage = (Button) findViewById(R.id.async_task_get_image); 42 mGetImage.setOnClickListener(new View.OnClickListener() { 43 public void onClick(View v) { 44 /*开启异步线程操作*/ 45 Log.i("cao", "user call execute!"); 46 loader.execute(ImageUrl); 47 } 48 }); 49 mAbort = (Button) findViewById(R.id.asyc_task_abort); 50 mAbort.setOnClickListener(new View.OnClickListener() { 51 public void onClick(View v) { 52 /*取消这个异步线程操作,销毁线程*/ 53 loader.cancel(true); 54 } 55 }); 56 mAbort.setEnabled(false); 57 } 58 59 private class ImageLoader extends AsyncTask<String, Integer, Bitmap> { 60 private static final String TAG = "ImageLoader"; 61 62 /** 63 *这是用户调用execute()方法之后最先调用的方法 64 **/ 65 @Override 66 protected void onPreExecute() { 67 // Initialize progress and image 68 Log.i("cao", "call onPreExecute begin!"); 69 mGetImage.setEnabled(false); 70 mAbort.setEnabled(true); 71 mProgressBar.setVisibility(View.VISIBLE); 72 mProgressBar.setProgress(0); 73 mImageView.setImageResource(R.drawable.ic_launcher); 74 } 75 /** 76 *经过 onPreExecute 方法的准备之后,进入耗时操作方法 77 **/ 78 @Override 79 protected Bitmap doInBackground(String... url) { 80 Log.i("cao", "call doInBackground begin!"); 81 //一些网络操作代码,通过网络下载图片保存后并转化为Bitmap 82 try { 83 URL u; 84 HttpURLConnection conn = null; 85 InputStream in = null; 86 OutputStream out = null; 87 final String filename = "local_temp_image"; 88 try { 89 u = new URL(url[0]); 90 conn = (HttpURLConnection) u.openConnection(); 91 conn.setDoInput(true); 92 conn.setDoOutput(false); 93 conn.setConnectTimeout(20 * 1000); 94 in = conn.getInputStream(); 95 out = openFileOutput(filename, Context.MODE_PRIVATE); 96 byte[] buf = new byte[8196]; 97 int seg = 0; 98 final long total = conn.getContentLength(); 99 long current = 0; 100 /* 101 * Without checking isCancelled(), the loop continues until reading whole image done, i.e. the progress 102 * continues go up to 100. But onPostExecute() will not be called. 103 * By checking isCancelled(), we can stop immediately, i.e. progress stops immediately when cancel() is called. 104 */ 105 while (!isCancelled() && (seg = in.read(buf)) != -1) { 106 out.write(buf, 0, seg); 107 current += seg; 108 int progress = (int) ((float) current / (float) total * 100f); 109 publishProgress(progress); 110 SystemClock.sleep(1000); 111 } 112 } finally { 113 if (conn != null) { 114 conn.disconnect(); 115 } 116 if (in != null) { 117 in.close(); 118 } 119 if (out != null) { 120 out.close(); 121 } 122 } 123 return BitmapFactory.decodeFile(getFileStreamPath(filename).getAbsolutePath()); 124 } catch (MalformedURLException e) { 125 e.printStackTrace(); 126 } catch (IOException e) { 127 e.printStackTrace(); 128 } 129 return null; 130 } 131 /** 132 *开始耗时操作之后,开始跟新用户进度的方法 133 **/ 134 @Override 135 protected void onProgressUpdate(Integer... progress) { 136 Log.i("cao", "call onProgressUpdate begin!"); 137 mProgressBar.setProgress(progress[0]); 138 } 139 /** 140 *当耗时操作方法完之后,调用修改UI的方法。 141 **/ 142 @Override 143 protected void onPostExecute(Bitmap image) { 144 Log.i("cao", "call onPostExecute begin!"); 145 if (image != null) { 146 mImageView.setImageBitmap(image); 147 } 148 mProgressBar.setProgress(100); 149 mProgressBar.setVisibility(View.GONE); 150 mAbort.setEnabled(false); 151 } 152 } 153} 154
1 这里是配置文件的代码: 2
1<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 tools:context=".MainActivity" > 7 <!-- 分别显示 下载进度的显示--> 8 <ProgressBar 9 android:id="@+id/async_task_progress" 10 android:layout_width="match_parent" 11 android:layout_height="20dp" 12 style="?android:attr/progressBarStyleHorizontal" 13 android:max="100" 14 /> 15 <!-- 分别显示 下载前 下载中 下载后的显示 --> 16 <ImageView 17 android:id="@+id/async_task_displayer" 18 android:layout_width="200dp" 19 android:layout_height="200dp" 20 android:layout_marginTop="20dp" 21 android:layout_gravity="center_horizontal" 22 android:background="@drawable/public_head_boy" 23 /> 24 <LinearLayout 25 android:layout_width="wrap_content" 26 android:layout_height="wrap_content" 27 android:orientation="horizontal" 28 android:layout_marginTop="20dp" 29 android:layout_gravity="center_horizontal" 30 > 31 <Button 32 android:id="@+id/async_task_get_image" 33 android:layout_width="wrap_content" 34 android:layout_height="wrap_content" 35 android:text="get the image"/> 36 <Button 37 android:id="@+id/asyc_task_abort" 38 android:layout_width="wrap_content" 39 android:layout_height="wrap_content" 40 android:text="abort"/> 41 </LinearLayout> 42</LinearLayout> 43 44

AsyncTask的方法

  必选方法:

         1.doinBackground( params) 后台操作,程序中耗时的操作可以放在这个方法里面,不能操作UI,  Params 是任务执行器需要的数据类型。其 Params 是可变参数。

       2. onpostexecute( results ) 相当于 handler 处理 UI 的方式,在这里可以使用在 doinbackground 得到的结果处理操作 UI ,在此异步操作线程。此方法在主线程执行,任务执行的结果作为此方法的参数返回。其 results 也是可变参数。

 

 可选方法:

         1 、   onprogressupdate(progress…) 可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。 progress 也是可变参数,可以用来查看异步线程的进度。        2、  onpreExecute()  这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。        3、  onCancelled()  用户调用取消时,要做的操作。

另外, Google 还给 AsyncTask 制定了一些限制:

       1. AsyncTask的实例 必须是在主线程即 UI 线程中创建

       2. AsyncTask 的实例的 Execute() 方法必须在主线程中调用。(一般是在需要进行异步线程操作时调用)

       3. 不要手动调用 AsyncTask 里面的几个方法。

       4. AsyncTask 对象不可重复使用,也就是说一个 AsyncTask 对象只能 execute() 一次,否则会有异常抛出 " java.lang.IllegalStateException:Cannot execute task: the task is already running "

       5. 在 doInBackground() 中要检查 isCancelled() 的返回值,如果你的异步任务是可以取消的话。

注: cancel() 仅仅是给 AsyncTask 对象设置了一个标识位,当调用了 cancel() 后,发生的事情只有 :AsyncTask 对象的标识位变了,和 doInBackground() 执行完成后, onPostExecute() 不会被回调了,而 doInBackground() 和 onProgressUpdate() 还是会继续执行直到 doInBackground() 结束。所以要在 doInBackground() 中不断的检查 isCancellled() 的返回值,当其返回 true 时就停止执行,特别是有循环的时候。如上面的例子,如果把读取数据的 isCancelled() 检查去掉,图片还是会下载,进度也一直会走,只是最后图片不会放到 UI 上 ( 因为 onPostExecute() 没被回调 )!

代码交流 2021