AsnyncTask与handler(一)——AsyncTask异步处理

来源:互联网 发布:网络盒子哪个好 编辑:程序博客网 时间:2024/05/29 13:40

前言:以前就遇到过AsnyncTask,但由于当时对其理解比较懵懂,所以就先放了一下,没写博客,时隔几月,当再次遇到此问题时,感觉难度更大,却有必要仔细研究一下,这里分两篇写,这篇先写AsncTask的异步处理的实现,第二篇对AsyncTask进行补充,讲述handler消息机制。

一、android开发中存在的问题 

当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。也就是说所有有关更新界面的操作必须在主线程中操作,除主线程外的其它自建线程都没有这个权力处理这些UI更新操作,否则将报错!在后面的讲解中会看到即便是最常用的Toast.makeText()也是不允许在非主线程中使用的!

比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后,是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员  

在单线程模型中始终要记住两条法则:
1. 不要阻塞UI线程 
2. 确保只在UI线程中访问Android UI工具包 ,即更新UI

 二、AsyncTask

 但我们在处理程序时,常常是先做数据处理,然后再数据准备好后更新UI,如果按照上面的单线程模式,就出现了一个问题,更新UI必须写在OnCreate函数中,但我们数据处理操作一般是要自建线程的,如果全部写在OnCreate函数中,那么代码就难以规范。所以为了解决数据处理后更新UI的问题,就产生了AsyncTask!

AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。

 <一>子类化AsyncTask

 AsyncTask是抽象类 ,我们必须自己写一个类来继承AsyncTask 类

 <二>实现AsyncTask中定义及须重写的方法 

重写AsyncTask后可以或必须重写的函数如下:

  • onPreExecute() 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。
  • doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。
  • publishProgress 该方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
  • onProgressUpdate(Progress...), 在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,可以通过一个进度条进行展示。
  • onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

自己写一个类继承自AsyncTask后的形式如下所示:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>  
  2. {  
  3.     ……………………  
  4. }  

可以看到有三个参数:

AsyncTask的三个泛型参数说明(三个参数可以是任何类型) 
第一个参数:传入doInBackground()方法的参数类型,这里是String 
第二个参数:传入onProgressUpdate()方法的参数类型,这里是Integer
第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。这里是Bitmap

<三>AsyncTask遵守准则 

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

 <四>实例

 效果:

            初始状态                                       加载中                                           完成

   

效果讲解:总体上来讲是从网上加载一张图片并贴到当前XML的指定位置

1、添加网络访问权限

在AndroidManifest.xml文件中,添加下面一行代码,获取互联网访问权限

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <uses-permission android:name="android.permission.INTERNET"/>  

2、XML布局

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical"  
  6.     tools:context=".MainActivity" >  
  7.   
  8.     <Button  
  9.         android:id="@+id/show"  
  10.         android:layout_width="fill_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="下一个" />  
  13.   
  14.     <ProgressBar     
  15.         android:id="@+id/processBar"    
  16.         android:layout_width="wrap_content"    
  17.         android:layout_height="wrap_content"  
  18.         android:visibility="gone"/>    
  19.     <HorizontalScrollView  
  20.         android:layout_width="fill_parent"  
  21.         android:layout_height="wrap_content"  
  22.         android:scrollbars="none" >  
  23.   
  24.         <ImageView  
  25.             android:id="@+id/image"  
  26.             android:layout_width="wrap_content"  
  27.             android:layout_height="wrap_content" />  
  28.     </HorizontalScrollView>  
  29.   
  30. </LinearLayout>  

关于布局,难度不大,也不是本篇重点,不再多讲,这里仅说下作用。
progressBar初始化为不显示,仅当用户点击“下一个”按钮时,显示,图片加载完成后取消显示。
imageView用来显示加载后的图片。

3、JAVA代码 

 先贴出完整代码,然后再逐步讲解

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.try_asynctask;  
  2.   
  3. import java.io.InputStream;  
  4. import java.net.URL;  
  5. import java.net.URLConnection;  
  6. import java.util.ArrayList;  
  7. import java.util.List;  
  8.   
  9. import android.os.AsyncTask;  
  10. import android.os.Bundle;  
  11. import android.app.Activity;  
  12. import android.content.Context;  
  13. import android.graphics.Bitmap;  
  14. import android.graphics.BitmapFactory;  
  15. import android.util.Log;  
  16. import android.view.View;  
  17. import android.view.View.OnClickListener;  
  18. import android.widget.Button;  
  19. import android.widget.ImageView;  
  20. import android.widget.ProgressBar;  
  21. import android.widget.Toast;  
  22.   
  23. public class MainActivity extends Activity {  
  24.   
  25.     private ImageView   image       = null;  
  26.     private Button      show;  
  27.     private ProgressBar progressBar = null;  
  28.     private int         number      = 0;  
  29.     List<String>      imageUrl    = null;  
  30.       
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.activity_main);  
  35.           
  36.         progressBar = (ProgressBar) findViewById(R.id.processBar);  
  37.         image = (ImageView) findViewById(R.id.image);  
  38.         show = (Button) findViewById(R.id.show);  
  39.         show.setOnClickListener(new showButtonListener());  
  40.           
  41.         imageUrl = new ArrayList<String>(); // 图片地址List  
  42.         imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");  
  43.         imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");  
  44.         imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");  
  45.         imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");  
  46.         imageUrl.add("http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg");  
  47.     }  
  48.   
  49.     public class showButtonListener implements OnClickListener  
  50.     {  
  51.         @Override  
  52.         public void onClick(View v)  
  53.         {  
  54.             number++;  
  55.             MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());  
  56.             myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));  
  57.         }  
  58.     }  
  59.       
  60.     class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>  
  61.     {  
  62.         // 可变长的输入参数,与AsyncTask.exucute()对应  
  63.         public MyAsyncTask(Context context)  
  64.         {  
  65.             progressBar.setVisibility(View.VISIBLE);  
  66.             image.setVisibility(View.GONE);  
  67.         }  
  68.         @Override  
  69.         protected Bitmap doInBackground(String... params)  
  70.         {  
  71.             Bitmap bitmap = null;  
  72.             try  
  73.             {  
  74.                 //根据URL取得图片并返回   
  75.                 URL url = new URL(params[0]);    
  76.                   
  77.                 URLConnection conn = url.openConnection();    
  78.                 conn.connect();    
  79.                 InputStream inputStream = conn.getInputStream();    
  80.                 bitmap = BitmapFactory.decodeStream(inputStream);    
  81.                   
  82.                 Toast.makeText(getApplicationContext(), "传回图片了", Toast.LENGTH_SHORT).show();  
  83.                 inputStream.close();  
  84.             }  
  85.             catch (Exception e)  
  86.             {  
  87.                 Log.e("msg", e.getMessage());  
  88.             }  
  89.             return bitmap;  
  90.         }  
  91.   
  92.         /** 
  93.          * 在doInBackground 执行完成后,onPostExecute方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread. 
  94.          */  
  95.         @Override  
  96.         protected void onPostExecute(Bitmap bitmap)  
  97.         {  
  98.             progressBar.setVisibility(View.GONE);  
  99.             image.setVisibility(View.VISIBLE);  
  100.             if (bitmap != null)  
  101.             {  
  102.                 image.setImageBitmap(bitmap);  
  103.             }  
  104.             else  
  105.             {  
  106.                 Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();  
  107.             }  
  108.         }  
  109.   
  110.         /** 
  111.          * 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。 
  112.          */  
  113.         @Override  
  114.         protected void onPreExecute()  
  115.         {  
  116.             // 任务启动  
  117.             Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();  
  118.         }  
  119.     }  
  120.   
  121. }  

1、先看OnCreate函数中操作

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. imageUrl = new ArrayList<String>(); // 图片地址List  
  2. imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");  
  3. imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");  
  4. imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");  
  5. imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");  
  6. imageUrl.add(<a target="_blank" href="http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg">http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg</a>);  

定义了一个StringArrayList,用来保存要访问的网址集合,点击“下一个”时,逐个循环显示 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. progressBar = (ProgressBar) findViewById(R.id.processBar);  
  2. image = (ImageView) findViewById(R.id.image);  
  3. show = (Button) findViewById(R.id.show);  
  4. show.setOnClickListener(new showButtonListener());  

 然后是初始化控件变量,并为“下一个”按钮设置监听函数showButtonListener。

2、“下一个”按钮监听函数showButtonListener

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class showButtonListener implements OnClickListener  
  2. {  
  3.     @Override  
  4.     public void onClick(View v)  
  5.     {  
  6.         number++;  
  7.         MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());  
  8.         myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));  
  9.     }  
  10. }  

核心在于最后一句话,imageUrl.get(number % imageUrl.size())循环得到传进去的网址,然后传给myAsyncTask.execute(),所以这里往MyAsyncTask传的参数是String类型,所以在MyAsyncTask派生自AsyncTask的时候,第一个参数也应当是String类型!这就是上面所讲的三个参数中的第一个的意义。
下面就开始今天的核心问题MyAsyncTask的讲解:

3-1 MyAsyncTask构造函数(可省略)

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public MyAsyncTask(Context context)  
  2. {  
  3.     progressBar.setVisibility(View.VISIBLE);  
  4.     image.setVisibility(View.GONE);  
  5. }  

这里是对XML作初始化设置,其实完全可以在onPreExecute()函数中操作是一样的。
3-2 onPreExecute()处理前操作(可省略)

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void onPreExecute()  
  3. {  
  4.     // 任务启动  
  5.     Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();  
  6. }  

代码没什么好讲的,这里只是想说,这个函数对于类派生不是必须要重写的,如果需要在数据处理前要在UI上加以提示等等的预处理操作,都可以放在这里操作。
3-3 doInBackground()后台数据处理操作(必写)

 这里必须注意的一点是,在这个函数里只能做数据处理操作,不能涉及任何更新UI的操作,即便使用Toast.makeText()也会报错!

 代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected Bitmap doInBackground(String... params)  
  3. {  
  4.     Bitmap bitmap = null;  
  5.     try  
  6.     {  
  7.         //根据URL取得图片并返回   
  8.         URL url = new URL(params[0]);    
  9.           
  10.         URLConnection conn = url.openConnection();    
  11.         conn.connect();    
  12.         InputStream inputStream = conn.getInputStream();    
  13.         bitmap = BitmapFactory.decodeStream(inputStream);    
  14.           
  15.         //注意!!!!这里写了个Toast.makeText!!!  
  16.         Toast.makeText(getApplicationContext(), "传回图片了", Toast.LENGTH_SHORT).show();  
  17.         inputStream.close();  
  18.     }  
  19.     catch (Exception e)  
  20.     {  
  21.         Log.e("msg", e.getMessage());  
  22.     }  
  23.     return bitmap;  
  24. }  

这行的核心意思就是根据得到的URL获取BitMap对象,然后返回!几点注意:
1、获取传递过来值的方法:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. URL url = new URL(params[0]);    

2、程序报错

在实际运行时,根本无法看到“传回图片了”消息提示,在看LogCat,已经报错了,错误信息如下:

 

 这里不影响程序运行是因为外面加着try-catch呢,如果去掉程序就会直接崩了。所以,在doInBackground中不准做任何的UI更新操作,即便是Toast也不行!!!!

 3-4 onPostExecute()对返回结果处理,更新UI(必写)

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void onPostExecute(Bitmap bitmap)  
  3. {  
  4.     progressBar.setVisibility(View.GONE);  
  5.     image.setVisibility(View.VISIBLE);  
  6.     if (bitmap != null)  
  7.     {  
  8.         image.setImageBitmap(bitmap);  
  9.     }  
  10.     else  
  11.     {  
  12.         Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();  
  13.     }  
  14. }  

这个函数中处理doInBackground的返回结果,更新UI

至此本篇关于AsyncTask异步处理的操作就全部讲完了,这里存在一个问题,如果我们想在doInBackground处理过程中更新UI怎么办?下篇讲述的handler消息机制,就可以解决这个问题。 

 

 参考文章:

《Android进阶2之AsyncTask实现异步处理任务》:http://www.cnblogs.com/snake-hand/archive/2012/03/30/2454368.html

 《AsyncTask<>的参数介绍》:http://my.eoe.cn/xuliangbo/archive/6063.html

 

源码地址:http://download.csdn.net/detail/harvic880925/7275089  不要分,仅供分享

0 0
原创粉丝点击