当前位置: 代码迷 >> Android >> Android 兑现联网(三)——在线程中联网
  详细解决方案

Android 兑现联网(三)——在线程中联网

热度:82   发布时间:2016-05-01 19:09:10.0
Android 实现联网(三)——在线程中联网

在前面的关于Java?I/O的学习中,有一个我们需要牢记的是:对数据流的操作都是阻塞的,在一般情况下,我们是不需要考虑这个问题的,但是在Android?实现联网的时候,我们必须考虑到这个问题。比如:从网络上下载一张图片:

Java代码?
  1. public?Bitmap returnBitmap(String url)
  2. {
  3. URL myFileUrl =?null;
  4. Bitmap bitmap =?null;
  5. try{
  6. myFileUrl =?new?URL(url);
  7. }catch(MalformedURLException e){
  8. e.printStackTrace();
  9. return?null;
  10. };
  11. try{
  12. HttpURLConnection conn = (HttpURLConnection)myFileUrl.openConnection();
  13. conn.setDoInput(true);
  14. conn.connect();
  15. InputStream is = conn.getInputStream();
  16. bitmap = BitmapFactroy.decodeStream(is);
  17. is.close();
  18. }catch(IOException e){
  19. e.printStackTrace();
  20. }
  21. return?bitmap;
  22. }


由于网络连接需要很长的时间,需要3-5秒,甚至更长的时间才能返回页面的内容。如果此连接动作直接在主线程,也就是UI线程中处理,会发生什么情况呢? 整个程序处于等待状态,界面似乎是“死”掉了。为了解决这个问题,必须把这个任务放置到单独线程中运行,避免阻塞UI线程,这样就不会对主线程有任何影 响。举个例子如下:

Java代码?
  1. private?void?connect(String strURL){
  2. new?Thread() {
  3. public?void?run() {
  4. try?{
  5. HttpClient client =?new?DefaultHttpClient();
  6. // params[0]代表连接的url
  7. HttpGet get =?new?HttpGet(url.getText().toString());
  8. HttpResponse response = client.execute(get);
  9. 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. Log.V(“moandroid sample”,s);
  24. }
  25. }?catch?(Exception e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }.start();
  30. }

?

使用Handler更新界面

如何将下载的信息显示在界面上了,比如说下载的进度。Android?SDK平台只允许在主线程中调用相关View的方法来更新界面。如果返回结果在新线程中获得,那么必须借助Handler来更新界面。为此,在界面 Activity中创建一个Handler对象,并在handleMessage()中更新UI。

Java代码?
  1. //Task在另外的线程执行,不能直接在Task中更新UI,因此创建了Handler
  2. private?Handler handler =?new?Handler() {
  3. @Override
  4. public?void?handleMessage(Message msg) {
  5. String m = (String) msg.obj;
  6. message.setText(m);
  7. }
  8. };


只需要将上面的


替换为:

Java代码?
  1. s =?new?String(baos.toByteArray());
  2. Message mg = Message.obtain();
  3. mg.obj = s;
  4. handler.sendMessage(mg);

?

AsyncTask

看上去修改后的connect()方法已经可用了,但是这种匿名程的方式是存在缺陷的:

  • 线程的开销较大,如果每个任务都要创建一个线程,那么应用程 序的效率要低很多;
  • 线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。
  • 另外,前面已经看到,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。

为了解决这一问题,Android在1.5版本引入了AsyncTask。AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程 中执行,这就有效地避免了使用Handler带来的麻烦。阅读AsyncTask的源码可知,AsyncTask是使用 java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了 匿名线程存在的问题。
AsyncTask是抽象类,其结构图如下图所示:
AsyncTask-class?
AsyncTask定义了三种泛型类型 Params,Progress和Result。

  • Params 启动任务执行的输入参数,比如HTTP请求的URL。
  • Progress 后台任务执行的百分比。
  • Result 后台执行任务最终返回的结果,比如String。

子类必须实现抽象方法doInBackground(Params… p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。通常还应该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。需要注意的是AsyncTask一定要在主线程中创建实例。
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务 的执行过程中,这些方法被自动调用,运行过程,如下图所示:

AsyncTask-Usage

  • onPreExecute() 当任务执行之前开始调用此方法,可以在这里显示进度对话框。
  • doInBackground(Params…) 此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的 进度。
  • onProgressUpdate(Progress…) 此方法在主线程执行,用于显示任务执行的进度。
  • onPostExecute(Result) 此方法在主线程执行,任务执行的结果作为此方法的参数返回。

举个简单的例子如下:

Java代码?
  1. // 设置三种类型参数分别为String,Integer,String
  2. class?PageTask?extends?AsyncTask {
  3. // 可变长的输入参数,与AsyncTask.exucute()对应
  4. @Override
  5. protected?String doInBackground(String… params) {
  6. try?{
  7. HttpClient client =?new?DefaultHttpClient();
  8. // params[0]代表连接的url
  9. HttpGet get =?new?HttpGet(params[0]);
  10. HttpResponse response = client.execute(get);
  11. HttpEntity entity = response.getEntity();
  12. long?length = entity.getContentLength();
  13. InputStream is = entity.getContent();
  14. String s =?null;
  15. if?(is !=?null) {
  16. ByteArrayOutputStream baos =?new?ByteArrayOutputStream();
  17. byte[] buf =?new?byte[128];
  18. int?ch = -1;
  19. int?count =?0;
  20. while?((ch = is.read(buf)) != -1) {
  21. baos.write(buf,?0, ch);
  22. count += ch;
  23. if?(length >?0) {
  24. // 如果知道响应的长度,调用publishProgress()更新进度
  25. publishProgress((int) ((count / (float) length) *?100));
  26. }
  27. // 为了在模拟器中清楚地看到进度,让线程休眠100ms
  28. Thread.sleep(100);
  29. }
  30. s =?new?String(baos.toByteArray()); }
  31. // 返回结果
  32. return?s;
  33. }?catch?(Exception e) {
  34. e.printStackTrace();
  35. }
  36. return?null;

  相关解决方案