MyException - 我的异常网
当前位置:我的异常网» 移动开发 » AsyncTask的应用

AsyncTask的应用

www.MyException.Cn  网友分享于:2015-08-26  浏览:17次
AsyncTask的使用
本片不多说 直接引用:
http://www.ophonesdn.com/article/show/36
http://developer.android.com/reference/android/os/AsyncTask.html
   连接互联网的能力大大提升了移动电话的业务范围,中国移动很多出色的业务,像移动随身听,飞信等都是基于网络连接的应用程序,这也说明未来的移动互联网领域大有可为。因此深入掌握的联网应用程序的开发和设计至关重要。本文主要介绍在开发联网应用过程中如何管理线程,如何从主线程之外更新界面。?(作者:中国移动通信研究院 詹建飞)


为什么需要线程
       假设需要开发一个联网应用程序,需要从一个网址抓取网页内容,这里读取的网页地址是笔者在本地机器上自己建立的服务器地址。当然在读取网页内容的时候,可以使用HttpClient提供的API,但是这并不是本文的介绍重点。缺乏联网程序开发经验的程序员可能写出下面的代码。



view plaincopy to clipboardprint?
01.package com.ophone.network;  
02. 
03.//这里为了节省篇幅,忽略了import项  
04.public class NetworkActivity extends Activity {  
05. 
06.    // 显示任务的执行状态和返回结果  
07.    private TextView message;  
08.    private Button open;  
09.    private EditText url;  
10. 
11.    @Override 
12.    public void onCreate(Bundle savedInstanceState) {  
13.        super.onCreate(savedInstanceState);  
14.        setContentView(R.layout.main);  
15.        message = (TextView) findViewById(R.id.message);  
16.        url = (EditText) findViewById(R.id.url);  
17.        open = (Button) findViewById(R.id.open);  
18.        open.setOnClickListener(new View.OnClickListener() {  
19.            public void onClick(View arg0) {  
20.                connect();  
21.            }  
22.        });  
23.    }  
24. 
25.    private String connect() {  
26.        try {  
27.            HttpClient client = new DefaultHttpClient();  
28.            // params[0]代表连接的url  
29.            HttpGet get = new HttpGet(url.getText().toString());  
30.            HttpResponse response = client.execute(get);  
31.            HttpEntity entity = response.getEntity();  
32.            long length = entity.getContentLength();  
33.            InputStream is = entity.getContent();  
34.            String s = null;  
35.            if (is != null) {  
36.                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
37.                byte[] buf = new byte[128];  
38.                int ch = -1;  
39.                int count = 0;  
40.                while ((ch = is.read(buf)) != -1) {  
41.                    baos.write(buf, 0, ch);  
42.                    count += ch;  
43.                    // 为了在模拟器中清楚地看到进度,让线程休眠1000ms  
44.                    Thread.sleep(50000);  
45.                }  
46.                s = new String(baos.toByteArray());  
47.            }  
48.            // 返回结果  
49.            return s;  
50.        } catch (Exception e) {  
51.            e.printStackTrace();  
52.        }  
53.        return null;  
54.    }  
55.} 
package com.ophone.network; //这里为了节省篇幅,忽略了import项 public class NetworkActivity extends Activity { // 显示任务的执行状态和返回结果 private TextView message; private Button open; private EditText url; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); message = (TextView) findViewById(R.id.message); url = (EditText) findViewById(R.id.url); open = (Button) findViewById(R.id.open); open.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { connect(); } }); } private String connect() { try { HttpClient client = new DefaultHttpClient(); // params[0]代表连接的url HttpGet get = new HttpGet(url.getText().toString()); HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); long length = entity.getContentLength(); InputStream is = entity.getContent(); String s = null; if (is != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[128]; int ch = -1; int count = 0; while ((ch = is.read(buf)) != -1) { baos.write(buf, 0, ch); count += ch; // 为了在模拟器中清楚地看到进度,让线程休眠1000ms Thread.sleep(50000); } s = new String(baos.toByteArray()); } // 返回结果 return s; } catch (Exception e) { e.printStackTrace(); } return null; } }

        网络连接通常是比较耗时的,尤其是在当前的GPRS这种低速率的网络情况下,这样connect()方法可能需要3-5秒,甚至更长的时间才能返回页面的内容。如果此连接动作直接在主线程,也就是UI线程中处理,会发生什么情况呢?为了在模拟器中更好的模拟网络读取速度慢的情况,笔者在读取过程中让线程休眠了50秒,运行NetworkActivity,点击“连接”按钮。意外发生了,按钮长时间没有反应,整个界面似乎是“死”掉了。系统随后显示出了ANR(应用程序无响应)错误提示,如图1所示:




在线程中联网
  为什么出现ANR?答案是联网动作阻塞在了主线程,长时间没有返回,这样OPhone弹出ANR错误。这个错误提示我们,如果否个任务可能需要长时间的运行才能返回,则必须把这个任务放置到单独线程中运行,避免阻塞UI线程。Java语言内置了对线程的支持,可以使用Thread类创建一个新线程,然后在run()方法中读取网页的内容,获得页面内容后调用TextView.setText()更新界面。修改后的connect()方法如下所示:



view plaincopy to clipboardprint?
01.private void connect() {  
02.        new Thread() {  
03.            public void run() {  
04.                try {  
05.                    HttpClient client = new DefaultHttpClient();  
06.                    // params[0]代表连接的url  
07.                    HttpGet get = new HttpGet(url.getText().toString());  
08.                    HttpResponse response = client.execute(get);  
09.                    HttpEntity entity = response.getEntity();  
10.                    long length = entity.getContentLength();  
11.                    InputStream is = entity.getContent();  
12.                    String s = null;  
13.                    if (is != null) {  
14.                        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
15.                        byte[] buf = new byte[128];  
16.                        int ch = -1;  
17.                        int count = 0;  
18.                        while ((ch = is.read(buf)) != -1) {  
19.                            baos.write(buf, 0, ch);  
20.                            count += ch;  
21.                        }  
22.                        s = new String(baos.toByteArray());  
23.                        message.setText(s);  
24.                    }  
25.                } catch (Exception e) {  
26.                    e.printStackTrace();  
27.                }  
28.            }  
29.        }.start();  
30.    } 
private void connect() { new Thread() { public void run() { try { HttpClient client = new DefaultHttpClient(); // params[0]代表连接的url HttpGet get = new HttpGet(url.getText().toString()); HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); long length = entity.getContentLength(); InputStream is = entity.getContent(); String s = null; if (is != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[128]; int ch = -1; int count = 0; while ((ch = is.read(buf)) != -1) { baos.write(buf, 0, ch); count += ch; } s = new String(baos.toByteArray()); message.setText(s); } } catch (Exception e) { e.printStackTrace(); } } }.start(); }

       重新运行NetworkActivity,点击“连接”按钮。程序并没有像预期的那种获得网页的内容,并显示到TextView上。查看log可以看到在connect的执行过程中抛出了异常。接下来分析问题的所在。


使用Handler更新界面
        其实,connect()方法中抛出的异常是由于界面更新引起的。Connect()方法直接在新启动的线程中调用message.setText()方法是不正确的。OPhone平台只允许在主线程中调用相关View的方法来更新界面。如果返回结果在新线程中获得,那么必须借助Handler来更新界面。为此,在NetworkActivity中创建一个Handler对象,并在handleMessage()中更新UI。


view plaincopy to clipboardprint?
01.//Task在另外的线程执行,不能直接在Task中更新UI,因此创建了Handler  
02.    private Handler handler = new Handler() {  
03.        @Override 
04.        public void handleMessage(Message msg) {  
05.            String m = (String) msg.obj;  
06.            message.setText(m);  
07.        }  
08.    }; 
//Task在另外的线程执行,不能直接在Task中更新UI,因此创建了Handler private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { String m = (String) msg.obj; message.setText(m); } };


 当从connect()方法中获得网页内容后,使用如下方法更新界面。



view plaincopy to clipboardprint?
01.s = new String(baos.toByteArray());  
02.    Message mg = Message.obtain();  
03.    mg.obj = s;  
04.    handler.sendMessage(mg); 
s = new String(baos.toByteArray()); Message mg = Message.obtain(); mg.obj = s; handler.sendMessage(mg);

        重新运行NetworkActivity,点击“连接”按钮,结果如图2示,正确读取了网页的内容。





AsyncTask
        看上去修改后的connect()方法已经可用了,但是这种匿名程的方式是存在缺陷的:第一,线程的开销较大,如果每个任务都要创建一个线程,那么应用程序的效率要低很多;第二,线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。另外,前面已经看到,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。
        为了解决这一问题,OPhone在1.5版本引入了AsyncTask。AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦。阅读AsyncTask的源码可知,AsyncTask是使用java.util.concurrent框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了匿名线程存在的问题。
        AsyncTask是抽象类,子类必须实现抽象方法doInBackground(Params... p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。通常还应该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。需要注意的是AsyncTask一定要在主线程中创建实例。AsyncTask定义了三种泛型类型Params,Progress和Result。 (我的话:用的了泛型,这也是为什么android推荐安装jdk版本1.5以上的原因之一)

•Params 启动任务执行的输入参数,比如HTTP请求的URL。
•Progress 后台任务执行的百分比。
•Result 后台执行任务最终返回的结果,比如String。
•AsyncTask的执行分为四个步骤,与前面定义的TaskListener类似。每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务的执行过程中,这些方法被自动调用。
•onPreExecute() 当任务执行之前开始调用此方法,可以在这里显示进度对话框。
•doInBackground(Params...) 此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress...)来更新任务的进度。不同线程,其他方法都是在主线程上
•onProgressUpdate(Progress...) 此方法在主线程执行,用于显示任务执行的进度。
•onPostExecute(Result) 此方法在主线程执行,任务执行的结果作为此方法的参数返回。

PageTask扩展了AsyncTask,在doInBackground()方法中读取网页内容。PageTask的源代码如下所示:



view plaincopy to clipboardprint?
01.// 设置三种类型参数分别为String,Integer,String  
02.    class PageTask extends AsyncTask<String, Integer, String> {  
03. 
04.        // 可变长的输入参数,与AsyncTask.exucute()对应  
05.        @Override 
06.        protected String doInBackground(String... params) {  
07.            try {  
08.                HttpClient client = new DefaultHttpClient();  
09.                // params[0]代表连接的url  
10.                HttpGet get = new HttpGet(params[0]);  
11.                HttpResponse response = client.execute(get);  
12.                HttpEntity entity = response.getEntity();  
13.                long length = entity.getContentLength();  
14.                InputStream is = entity.getContent();  
15.                String s = null;  
16.                if (is != null) {  
17.                    ByteArrayOutputStream baos = new ByteArrayOutputStream();  
18.                    byte[] buf = new byte[128];  
19.                    int ch = -1;  
20.                    int count = 0;  
21.                    while ((ch = is.read(buf)) != -1) {  
22.                        baos.write(buf, 0, ch);  
23.                        count += ch;  
24.                        if (length > 0) {  
25.                            // 如果知道响应的长度,调用publishProgress()更新进度  
26.                            publishProgress((int) ((count / (float) length) * 100));  
27.                        }  
28.                        // 为了在模拟器中清楚地看到进度,让线程休眠100ms  
29.                        Thread.sleep(100);  
30.                    }  
31.                    s = new String(baos.toByteArray());             }  
32.                // 返回结果  
33.                return s;  
34.            } catch (Exception e) {  
35.                e.printStackTrace();  
36.            }  
37.            return null;  
38.        }  
39.        @Override 
40.        protected void onCancelled() {  
41.            super.onCancelled();  
42.        }  
43.        @Override 
44.        protected void onPostExecute(String result) {  
45.            // 返回HTML页面的内容  
46.            message.setText(result);  
47.        }  
48.        @Override 
49.        protected void onPreExecute() {  
50.            // 任务启动,可以在这里显示一个对话框,这里简单处理  
51.            message.setText(R.string.task_started);  
52.        }  
53.        @Override 
54.        protected void onProgressUpdate(Integer... values) {  
55.            // 更新进度  
56.            message.setText(values[0]);  
57.        }  
58.    } 
// 设置三种类型参数分别为String,Integer,String class PageTask extends AsyncTask<String, Integer, String> { // 可变长的输入参数,与AsyncTask.exucute()对应 @Override protected String doInBackground(String... params) { try { HttpClient client = new DefaultHttpClient(); // params[0]代表连接的url HttpGet get = new HttpGet(params[0]); HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); long length = entity.getContentLength(); InputStream is = entity.getContent(); String s = null; if (is != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[128]; int ch = -1; int count = 0; while ((ch = is.read(buf)) != -1) { baos.write(buf, 0, ch); count += ch; if (length > 0) { // 如果知道响应的长度,调用publishProgress()更新进度 publishProgress((int) ((count / (float) length) * 100)); } // 为了在模拟器中清楚地看到进度,让线程休眠100ms Thread.sleep(100); } s = new String(baos.toByteArray()); } // 返回结果 return s; } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onCancelled() { super.onCancelled(); } @Override protected void onPostExecute(String result) { // 返回HTML页面的内容 message.setText(result); } @Override protected void onPreExecute() { // 任务启动,可以在这里显示一个对话框,这里简单处理 message.setText(R.string.task_started); } @Override protected void onProgressUpdate(Integer... values) { // 更新进度 message.setText(values[0]); } }


  执行PageTask非常简单,只需要调用如下代码。重新运行NetworkActivity,不但可以抓取网页的内容,还可以实时更新读取的进度。读者尝试读取一个较大的网页,看看百分比的更新情况。



view plaincopy to clipboardprint?
01.PageTask task = new PageTask();  
02.        task.execute(url.getText().toString()); 
PageTask task = new PageTask(); task.execute(url.getText().toString());
总结

  本文介绍了OPhone联网应用开发中应该注意的两个问题:线程管理和界面更新。不但分析了问题的所在,也给出了多种解决方案。这里笔者推荐使用AsyncTask处理联网,播放大尺寸媒体文件等较为耗时的工作,不但执行效率高,也可以节省代码。

 3.

Java代码 复制代码 收藏代码
  1. class DownloadImagesTask extends AsyncTask<String, Integer, Bitmap> {    
  2.     
  3. private int imageViewID;    
  4.     
  5.     protected void onPostExecute(Bitmap bitmap1) {    
  6.     setImage(imageViewID, bitmap1);    
  7. }    
  8.     
  9.     public void setImageId(int imageViewID) {    
  10.         this.imageViewID = imageViewID;    
  11.     }    
  12.     
  13.     @Override    
  14.     protected Bitmap doInBackground(String... url) {    
  15.         Bitmap bitmap1 =     
  16.             DownloadImage(url[0]);    
  17.         return bitmap1;    
  18.     }    
  19. }  
class DownloadImagesTask extends AsyncTask<String, Integer, Bitmap> {
private int imageViewID;
protected void onPostExecute(Bitmap bitmap1) {
setImage(imageViewID, bitmap1);
}
public void setImageId(int imageViewID) {
this.imageViewID = imageViewID;
}
@Override
protected Bitmap doInBackground(String... url) {
Bitmap bitmap1 =
DownloadImage(url[0]);
return bitmap1;
}
}

 

Java代码 复制代码 收藏代码
  1. DownloadImagesTask task1 = new DownloadImagesTask();    
  2. task1.setImageId(R.id.img1);    
  3. task1.execute("http://assets.devx.com/articlefigs/39810_1.jpg");   
DownloadImagesTask task1 = new DownloadImagesTask();
task1.setImageId(R.id.img1);
task1.execute("http://assets.devx.com/articlefigs/39810_1.jpg");

 

Java代码 复制代码 收藏代码
  1. private Bitmap DownloadImage(String URL)   
  2.    {           
  3.        Bitmap bitmap = null;   
  4.        InputStream in = null;           
  5.        try {   
  6.            in = OpenHttpConnection(URL);   
  7.            bitmap = BitmapFactory.decodeStream(in);   
  8.            in.close();   
  9.        } catch (IOException e1) {   
  10.            // TODO Auto-generated catch block   
  11.            e1.printStackTrace();   
  12.        }   
  13.        return bitmap;                   
  14.    }  
 private Bitmap DownloadImage(String URL)
{
Bitmap bitmap = null;
InputStream in = null;
try {
in = OpenHttpConnection(URL);
bitmap = BitmapFactory.decodeStream(in);
in.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return bitmap;
}

 在其中使用进度对话框

 

Java代码 复制代码 收藏代码
  1. class GetTask extends AsyncTask<Object, Void, String>    
  2.     {    
  3.         Context mContext;    
  4.             ProgressDialog mDialog = null;    
  5.     
  6.             GetPhotoFeedTask(Context context)    
  7.     {    
  8.         mContext = context;    
  9.     }    
  10.     
  11.         @Override    
  12.         protected void onPreExecute()    
  13.         {    
  14.             super.onPreExecute();    
  15.     
  16.              mDialog = new ProgressDialog(mContext);    
  17.              mDialog.setMessage("Please wait...");    
  18.              mDialog.show();    
  19.         }    
  20.                 @Override    
  21.         protected String doInBackground(Object... params)    
  22.         {    
  23.                   // do stuff in background : fetch response    
  24.                 }    
  25.     
  26.                 @Override    
  27.         protected void onPostExecute(String result)    
  28.         {    
  29.             super.onPostExecute(result);    
  30.             setProgressBarIndeterminateVisibility(false);    
  31.             // mDialog.dismiss();    
  32.                 }    
  33. }   
 

文章评论

“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
我的丈夫是个程序员
我的丈夫是个程序员
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员和编码员之间的区别
程序员和编码员之间的区别
程序员都该阅读的书
程序员都该阅读的书
每天工作4小时的程序员
每天工作4小时的程序员
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
 程序员的样子
程序员的样子
程序员的鄙视链
程序员的鄙视链
总结2014中国互联网十大段子
总结2014中国互联网十大段子
鲜为人知的编程真相
鲜为人知的编程真相
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员必看的十大电影
程序员必看的十大电影
代码女神横空出世
代码女神横空出世
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
Java程序员必看电影
Java程序员必看电影
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
漫画:程序员的工作
漫画:程序员的工作
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
中美印日四国程序员比较
中美印日四国程序员比较
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
程序员应该关注的一些事儿
程序员应该关注的一些事儿
为什么程序员都是夜猫子
为什么程序员都是夜猫子
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
旅行,写作,编程
旅行,写作,编程
那些争议最大的编程观点
那些争议最大的编程观点
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
我是如何打败拖延症的
我是如何打败拖延症的
10个调试和排错的小建议
10个调试和排错的小建议
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
编程语言是女人
编程语言是女人
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有