android异步任务AsyncTask
一、为什么需要异步任务?
开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的,并且这些操作必须在UI线程中执行。
单线程模型中始终要记住两条法则:
1). 不要阻塞UI线程 ;
2). 确保只在UI线程中访问Android UI控件。
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
3、Android4.0以上版本中,主线程中不允许访问网络。涉及到网络操作的程序一般都是需要开一个新线程完成网络访问。但是在获得页面数据后,又不能将数据返回到UI界面中 。因为子线程(Worker Thread)不能直接访问UI线程中的成员,也就是说没有办法对UI界面上的内容进行操作,如果操作,将抛出异常:CalledFromWrongThreadException。
二、解决方案就是异步任务:AsyncTask
一般在异步任务中执行的是访问网络的代码,将访问到网络中的数据返回来传给UI线程,不阻塞主线程的同时更新UI。
下面解释下一下该类的使用,由于该类是抽象类因此子啊建立自己的异步任务的时候需要继承该抽象类,同需要修改继承时的三个泛型参数!
(1) AsyncTask定义了三种泛型类型 Params,Progress和Result。
使用异步任务的规则:
1.声明一个类继承AsyncTask,标注三个参数的类型
2. 第一个参数表示要执行的任务,通常是网络的路径URL;第二个参数任务进度的刻度;第三个参数任务执行的返回的结果。
- Params 启动任务执行的输入参数,比如HTTP请求的URL。 一般用String类型;
- Progress 后台任务执行的百分比。 一般用Integer类型;
- Result 后台执行任务最终返回的结果,一般用byte[]或者String,有的时候为了方便也可以直接将bitmap传递过去。当然最好的还是字节数组。
(2)需要继承的方法,且重写的方法(♥♥♥)重点理解!!!!!
1) onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条:dialog.show();
2) doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现: publishProgress(value); // 将该进度发布出去!这个进度最终将会发送至-->onProgressUpdate~
3) onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示:dialog.setProgress(values[0]);
4) 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,主要是更新UI操作,后台的计算结果将通过该方法传递到UI thread. 比如:imageView.setImageBitmap(result);
dialog.dismiss();
//--------------------------------------------------------------------代码分割线
//在这个demo里面我使用异步任务访问网络下载了一幅图片,然后将该图片更新至UI,当点击按钮后显示下载进度,然后下载完成后图片显示,下载进度自动消失。
1 import java.io.ByteArrayOutputStream; 2 import java.io.IOException; 3 import java.io.InputStream; 4 5 import org.apache.http.HttpEntity; 6 import org.apache.http.HttpResponse; 7 import org.apache.http.client.HttpClient; 8 import org.apache.http.client.methods.HttpGet; 9 import org.apache.http.impl.client.DefaultHttpClient; 10 import org.apache.http.util.EntityUtils; 11 12 import android.os.AsyncTask; 13 import android.os.Bundle; 14 import android.app.Activity; 15 import android.app.ProgressDialog; 16 import android.content.Entity; 17 import android.graphics.Bitmap; 18 import android.graphics.BitmapFactory; 19 import android.view.Menu; 20 import android.view.View; 21 import android.view.View.OnClickListener; 22 import android.widget.Button; 23 import android.widget.ImageView; 24 25 public class MainActivity extends Activity { 26 private Button button; 27 private ImageView imageView; 28 private String image_path = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png"; 29 private ProgressDialog dialog; 30 31 @Override 32 protected void onCreate(Bundle savedInstanceState) { 33 super.onCreate(savedInstanceState); 34 setContentView(R.layout.activity_main); 35 button = (Button) findViewById(R.id.button1); 36 imageView = (ImageView) findViewById(R.id.imageView1); 37 dialog = new ProgressDialog(this); 38 dialog.setTitle("提示"); 39 dialog.setMessage("正在下载,稍后....."); 40 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // 设置进度条是横向的, 41 dialog.setCancelable(false);// 不可被取消。不会失去焦点,直到下载结束后自动消失 42 43 button.setOnClickListener(new OnClickListener() { 44 @Override 45 public void onClick(View v) { 46 // TODO Auto-generated method stub 47 // 此处必须要进行执行!!execute 48 new MyTask().execute(image_path); 49 } 50 }); 51 } 52 53 //定义自己的MyTask 54 public class MyTask extends AsyncTask<String, Integer, Bitmap> { 55 // 56 57 @Override 58 protected void onPreExecute() { 59 // TODO Auto-generated method stub 60 super.onPreExecute(); 61 dialog.show(); 62 } 63 64 // 完成对图片的下载功能 65 @Override 66 protected Bitmap doInBackground(String... params) { 67 // TODO Auto-generated method stub 68 Bitmap bitmap = null; 69 ByteArrayOutputStream outputStream = null; 70 InputStream inputStream = null; 71 // 使用httpclient下载图片,这一次不再使用HttpEntity获取响应流中的实体,来读写图片而是采用IO操作。 72 try { 73 HttpClient httpClient = new DefaultHttpClient(); 74 // String... params可变参数 75 HttpGet httpGet = new HttpGet(params[0]); 76 HttpResponse httpResponse = httpClient.execute(httpGet); // 让httpclient执行get请求 77 // 判断是否有响应。 78 if (httpResponse.getStatusLine().getStatusCode() == 200) { 79 // 获取响应实体HttpEntity中的流对象! 80 inputStream = httpResponse.getEntity().getContent(); 81 // 获取文件的总长度,此长度用于更新进度条 82 long file_length = httpResponse.getEntity() 83 .getContentLength(); 84 outputStream = new ByteArrayOutputStream(); 85 byte[] data = new byte[1024]; 86 int len = 0; 87 int total_length = 0; 88 while ((len = inputStream.read(data)) != -1) { 89 total_length += len; 90 int value = (int) ((total_length / (float) file_length) * 100); // 计算进度值!!!这个方法是根据API中写的 91 publishProgress(value); // 将该进度发布出去!--->onProgressUpdate 92 outputStream.write(data, 0, len); // 最后一个参数不是len 93 } 94 // 将内存流ByteArrayOutputStream转换成字符数组--->再转成图片 95 byte[] result = outputStream.toByteArray(); 96 bitmap = BitmapFactory.decodeByteArray(result, 0, 97 result.length); 98 } 99 } catch (Exception e) { 100 // TODO: handle exception 101 e.printStackTrace(); 102 } finally { 103 if (inputStream != null) { 104 try { 105 inputStream.close(); 106 } catch (IOException e) { 107 // TODO Auto-generated catch block 108 e.printStackTrace(); 109 } 110 } 111 } 112 return bitmap; 113 } 114 115 @Override 116 protected void onProgressUpdate(Integer... values) { 117 // TODO Auto-generated method stub 118 super.onProgressUpdate(values); 119 // 设置进度条的进度是values[0] 120 dialog.setProgress(values[0]); 121 } 122 123 @Override 124 protected void onPostExecute(Bitmap result) { 125 // TODO Auto-generated method stub 126 super.onPostExecute(result); 127 dialog.dismiss(); 128 imageView.setImageBitmap(result); 129 } 130 } 131 132 } 133
// ======================布局文件十分简单,需要啥效果可以自己调整。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="下载图片" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="135dp"
android:src="@drawable/ic_launcher" />
</RelativeLayout>