Android AsyncTask 的实现及 cancel 方式

最近做一个功能需要用到AsyncTask。实现的过程很容易,但是在cancel的时候遇到了一点麻烦。找了很多地方终于找到了比较好的方法,这里跟大家分享一下。


根据Android Developer的介绍http://developer.android.com/intl/zh-CN/reference/android/os/AsyncTask.html :

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask其实是Android给开发者提供的一个简单轻量级的多线程类,通过它我们可以很容易新建一个线程做一些耗时的操作,并在这个过程中更新UI。之所以说它轻量级,是因为缺少了直接使用Thread的灵活性。如果是很复杂的操作,还是建议通过Thread来操作,然后通过Broadcast的方式来更新UI。

使用AsyncTask的时候还需要注意,如果AsyncTask在运行过程中使用了某个activity,那么我们就需要在保证AsyncTask的运行过程中这个activity不被destory,或者是在activity的onDestory()方法里面cancel掉这个AsyncTask。但是,如果是一直不让activity被destory的话,相当于不让用户切换界面,这个体验其实时不好的。所以,我们最好使用ProgressDialog,让用户知道我们在努力完成他们的操作,为了让用户可以cancel这个操作,我们可以检测ProgressDialog有没有取消,如果取消了,那就cancel整个AsyncTask。

所以,我们总是会需要cancel AsyncTask。这里贴一段我写的代码,有点点改动。

AsyncTask
 1     private class OpenWebPortalTask extends AsyncTask<Void, Void, Void> {
 2         private static final String DEFAULT_URL = "https://www.google.com";
 3         private ProgressDialog mProgressDialog = null;
 4         private Activity mActivity;
 5 
 6         public OpenWebPortalTask(Activity activity){
 7             mActivity = activity;
 8             mProgressDialog = new ProgressDialog(mActivity);  
 9             mProgressDialog.setMessage(getString(R.string.open_aweb_portal));
10             mProgressDialog.setCancelable(true);
11             mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
12                 
13                 @Override
14                 public void onCancel(DialogInterface dialog) {
15                     cancel(true);
16                 }
17             });
18         }
19         
20         @Override
21         protected void onPreExecute(){
22             mProgressDialog.show();
23         }
24         
25         @Override
26         protected Void doInBackground(Void... params) {
27             String schemeUrl = PropertyMgr.getInstance().getPortalURI();
28             if (TextUtils.isEmpty(schemeUrl)) {
29                 schemeUrl = DEFAULT_ANTI_THEFT_URL;
30             }
31             try {
32                 String st = CredentialManager.getInstance().getServiceTicket();
33                 if(st != null) {
34                     schemeUrl += "?st=" + st;
35                 }
36             } catch (Exception e) {
37                 SymLog.e(TAG, "Get Service Ticket failed.");
38             }
39             if(isCancelled()) {
40                 return null;
41             }
42             Intent i = new Intent(Intent.ACTION_VIEW);
43             i.setData(Uri.parse(schemeUrl));
44             startActivity(i);
45             return null;
46         }
47         
48         @Override
49         protected void onPostExecute(Void v) {
50             mActivity.runOnUiThread(new Runnable() {
51                 @Override
52                 public void run() {
53                     if (mProgressDialog != null) {
54                         mProgressDialog.dismiss();
55                         mProgressDialog = null;
56                     }
57                 }
58             });
59         }
60     }

这段代码里面,

String st = CredentialManager.getInstance().getServiceTicket();

 是个耗时操作,所以需要放在AsyncTask里面。例子中,在onPreExecute中显示ProgressDialog,在onPostExecute中销毁。

注意cancel AsyncTask的方法:

  1. 在ProgressDialog的setOnCancelListener里调用cancel(true);,表示如果ProgressDialog被cancel的话,也cancel掉AsyncTask;
  2. 在doInBackgroud方法中,判断isCancelled()是否为真,如果真,那么就返回。

为什么在doInBackgroud中判断?因为AsyncTask的重要工作都是在doInBackgroud中完成,它如果退出了,那么就意味着AsyncTask将被销毁了。

具体在doInBackgroud的哪部分判断是否要退出呢?这就需要根据程序的逻辑。一般在耗时操作的后面加上判断!代码中可以根据需要加多个这样的判断。


 

AsyncTask用于实现一些简单的多线程任务确实非常简单和好用,希望对大家有所帮助。

posted @ 2012-09-21 15:01  茶树  阅读(6539)  评论(2编辑  收藏  举报