Android:异步调用详解

来源:互联网 发布:grub2手动引导ubuntu 编辑:程序博客网 时间:2024/05/01 07:31

在实际应用中经常会遇到比较耗时任务的处理,比如网络连接,数据库操作等情况时,如果这些操作都是放在主线程(UI线程)中,则会造成UI的假死现象,Android中可以使用AsyncTask和Handler两种异步方式来解决这种问题。

AsyncTask:

android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.
使用的优点:
简单,快捷,过程可控
使用的缺点:
在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

在使用AsyncTask时处理类需要继承AsyncTask,提供三个泛型参数,并且重载AsyncTask的四个方法(至少重载一个)。

三个泛型参数:
1.Param 任务执行器需要的数据类型
2.Progress 后台计算中使用的进度单位数据类型
3.Result 后台计算返回结果的数据类型
在设置参数时通常是这样的:String... params,这表示方法可以有0个或多个此类型参数;有时参数可以设置为不使用,用Void...即可。

四个方法:
1.onPreExecute() 执行预处理,它运行于UI线程,可以为后台任务做一些准备工作,比如绘制一个进度条控件。
2.doInBackground(Params...) 后台进程执行的具体计算在这里实现,doInBackground(Params...)是AsyncTask的关键,此方法必须重载。在这个方法内可以使用publishProgress(Progress...)改变当前的进度值。
3.onProgressUpdate(Progress...) 运行于UI线程。如果在doInBackground(Params...) 中使用了publishProgress(Progress...),就会触发这个方法。在这里可以对进度条控件根据进度值做出具体的响应。
4.onPostExecute(Result) 运行于UI线程,可以对后台任务的结果做出处理,结果就是doInBackground(Params...)的返回值。此方法也要经常重载,如果Result为null表明后台任务没有完成(被取消或者出现异常)。

异步任务类代码:

[java] view plaincopyprint?
  1. // AsyncTask异步方式下载图片   
  2. class DownImageTask extends AsyncTask<String, Integer, Bitmap> {  
  3.     // 执行预处理   
  4.     @Override  
  5.     protected void onPreExecute() {  
  6.         super.onPreExecute();  
  7.         // 显示进度条   
  8.         progressBar.setVisibility(View.VISIBLE);  
  9.         progressBar.setMax(100);  
  10.     }  
  11.     // 后台进程的执行   
  12.     @Override  
  13.     protected Bitmap doInBackground(String... params) {  
  14.         try {  
  15.             URL url = new URL(params[0]);  
  16.             HttpURLConnection conn = (HttpURLConnection) url  
  17.                     .openConnection();  
  18.             InputStream inputStream = conn.getInputStream();  
  19.             bitmap = BitmapFactory.decodeStream(inputStream);  
  20.             // 进度条的更新,我这边只是用一个循环来示范,在实际应用中要使用已下载文件的大小和文件总大小的比例来更新  
  21.             for (int i = 1; i <= 10; i++) {  
  22.                 publishProgress(i * 10);  
  23.                 Thread.sleep(200);  
  24.             }  
  25.             inputStream.close();  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         }  
  29.         return bitmap;  
  30.     }  
  31.     // 运行于UI线程,对后台任务的结果做出处理,doInBackground方法执行的结果作为此方法的参数  
  32.     @Override  
  33.     protected void onPostExecute(Bitmap result) {  
  34.         super.onPostExecute(result);  
  35.         ImageView imageView = (ImageView) findViewById(R.id.image);  
  36.         imageView.setImageBitmap(result);  
  37.         progressBar.setVisibility(View.GONE);  
  38.     }  
  39.     // 运行于UI线程,如果在doInBackground(Params...)中使用了publishProgress(Progress...),就会触发此方法  
  40.     @Override  
  41.     protected void onProgressUpdate(Integer... values) {  
  42.         super.onProgressUpdate(values);  
  43.         progressBar.setProgress(values[0]);  
  44.     }  
  45. }  
// AsyncTask异步方式下载图片class DownImageTask extends AsyncTask<String, Integer, Bitmap> {// 执行预处理@Overrideprotected void onPreExecute() {super.onPreExecute();// 显示进度条progressBar.setVisibility(View.VISIBLE);progressBar.setMax(100);}// 后台进程的执行@Overrideprotected Bitmap doInBackground(String... params) {try {URL url = new URL(params[0]);HttpURLConnection conn = (HttpURLConnection) url.openConnection();InputStream inputStream = conn.getInputStream();bitmap = BitmapFactory.decodeStream(inputStream);// 进度条的更新,我这边只是用一个循环来示范,在实际应用中要使用已下载文件的大小和文件总大小的比例来更新for (int i = 1; i <= 10; i++) {publishProgress(i * 10);Thread.sleep(200);}inputStream.close();} catch (Exception e) {e.printStackTrace();}return bitmap;}// 运行于UI线程,对后台任务的结果做出处理,doInBackground方法执行的结果作为此方法的参数@Overrideprotected void onPostExecute(Bitmap result) {super.onPostExecute(result);ImageView imageView = (ImageView) findViewById(R.id.image);imageView.setImageBitmap(result);progressBar.setVisibility(View.GONE);}// 运行于UI线程,如果在doInBackground(Params...)中使用了publishProgress(Progress...),就会触发此方法@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);progressBar.setProgress(values[0]);}}

调用代码:

[java] view plaincopyprint?
  1. public class MainActivity extends Activity {    
  2.     private Button button;  
  3.     private ProgressBar progressBar;   
  4.         
  5.     @Override    
  6.     public void onCreate(Bundle savedInstanceState) {    
  7.         super.onCreate(savedInstanceState);    
  8.         setContentView(R.layout.main);    
  9.             
  10.         button = (Button)findViewById(R.id.button);  
  11.         progressBar = (ProgressBar)findViewById(R.id.progressBar);  
  12.             
  13.         button.setOnClickListener(new OnClickListener() {    
  14.                 
  15.             @Override    
  16.             public void onClick(View v) {    
  17.                 ProgressBarAsyncTask asyncTask = new DownImageTask();    
  18.                 asyncTask.execute("http://www.baidu.com/img/baidu_jgylogo3.gif");  
  19.             }    
  20.         });  
  21.     }  
public class MainActivity extends Activity {      private Button button;    private ProgressBar progressBar;           @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);                    button = (Button)findViewById(R.id.button);        progressBar = (ProgressBar)findViewById(R.id.progressBar);                  button.setOnClickListener(new OnClickListener() {                            @Override              public void onClick(View v) {                  ProgressBarAsyncTask asyncTask = new DownImageTask();                  asyncTask.execute("http://www.baidu.com/img/baidu_jgylogo3.gif");            }          });    }

Handler:
Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程中)
两个作用: 
安排消息或Runnable 在某个主线程中某个地方执行
安排一个动作在不同的线程中执行

Handler中分发消息的方法:
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable,long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
*以上post开头的方法在主线程中调用。
*以上send开头的方法在其它线程中调用。

示例代码:

[java] view plaincopyprint?
  1. public class HandlerTestActivity extends Activity {  
  2.   
  3.     private Button start;  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         // TODO Auto-generated method stub   
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.handlertest);  
  10.         start = (Button) findViewById(R.id.start);  
  11.         start.setOnClickListener(new startListener());  
  12.   
  13.         System.out.println("Activity Thread:" + Thread.currentThread().getId());  
  14.     }  
  15.   
  16.     Handler  handler = new Handler(){  
  17.   
  18.         @Override  
  19.         public void handleMessage(Message msg) {  
  20.             // TODO Auto-generated method stub   
  21.             if (msg.what == 1) {  
  22.                 //成功,做一些操作   
  23.             } else {  
  24.                 //失败,..   
  25.             }  
  26.         }  
  27.           
  28.     };  
  29.     Runnable thread  = new Runnable() {  
  30.   
  31.                          @Override  
  32.                          public void run() {  
  33.                              // TODO Auto-generated method stub   
  34.                              System.out.println("HandlerThread:" + Thread.currentThread().getId());  
  35.                              //Message message = new Message();  
  36.                              //message.what = 1;   
  37.                              //handler.sendMessage(message);  
  38.   
  39.                          }  
  40.                      };  
  41.   
  42.     class startListener implements OnClickListener {  
  43.   
  44.         @Override  
  45.         public void onClick(View v) {  
  46.             // TODO Auto-generated method stub    
  47.             handler.post(thread);  
  48.             //Thread t = new Thread(thread);   
  49.             //t.start();   
  50.         }  
  51.     }  
  52.   
  53. }  
public class HandlerTestActivity extends Activity {    private Button start;    @Override    protected void onCreate(Bundle savedInstanceState) {        // TODO Auto-generated method stub         super.onCreate(savedInstanceState);        setContentView(R.layout.handlertest);        start = (Button) findViewById(R.id.start);        start.setOnClickListener(new startListener());        System.out.println("Activity Thread:" + Thread.currentThread().getId());    }    Handler  handler = new Handler(){        @Override        public void handleMessage(Message msg) {            // TODO Auto-generated method stub            if (msg.what == 1) {                //成功,做一些操作            } else {                //失败,..            }        }            };    Runnable thread  = new Runnable() {                         @Override                         public void run() {                             // TODO Auto-generated method stub                              System.out.println("HandlerThread:" + Thread.currentThread().getId());                             //Message message = new Message();                             //message.what = 1;                             //handler.sendMessage(message);                         }                     };    class startListener implements OnClickListener {        @Override        public void onClick(View v) {            // TODO Auto-generated method stub             handler.post(thread);            //Thread t = new Thread(thread);            //t.start();        }    }}


这个程序看上去似乎实现了Handler的异步机制, handler.post(thread)似乎实现了新启线程的作用,不过通过执行我们发现,两个线程的ID相同!也就是说,实际上thread还是原来 的主线程,由此可见,handler.post()方法并未真正新建线程,只是在原线程上执行而已,我们并未实现异步机制。将startListener改写成如下代码就可以实现异步:

[java] view plaincopyprint?
  1. class startListener implements OnClickListener {  
  2.   
  3.         @Override  
  4.         public void onClick(View v) {  
  5.             // TODO Auto-generated method stub   
  6.             //handler.post(thread);   
  7.             Thread t = new Thread(thread);  
  8.             t.start();  
  9.         }  
  10.     }  
class startListener implements OnClickListener {        @Override        public void onClick(View v) {            // TODO Auto-generated method stub             //handler.post(thread);            Thread t = new Thread(thread);            t.start();        }    }

将runnable放到新创建的线程中执行,这样就实现了异步调用,如果需要在调用完成后通知或修改主线程,需要在Runnable类的run方法中调用handler.sendMessage(message)

[java] view plaincopyprint?
  1. @Override  
  2.  public void run() {  
  3.      // TODO Auto-generated method stub    
  4.      System.out.println("HandlerThread:" + Thread.currentThread().getId());  
  5.      Message message = new Message();  
  6.      message.what = 1;  
  7.      handler.sendMessage(message);  
  8. }  
@Override public void run() {     // TODO Auto-generated method stub      System.out.println("HandlerThread:" + Thread.currentThread().getId());     Message message = new Message();     message.what = 1;     handler.sendMessage(message);}