转自:

http://www.pin5i.com/showtopic-android-http-framework.html

编写Android程序,离不开和Http打交道。android 的单线程UI模型,使得处理Http这样,耗时操作变得麻烦。传统的作法有Thread + Handler和AsyncTask 而这两种方式都是需要自己写很多重复的代码如创建HttpClient etc.不符合DRY(Don't repeat yourself),使Activity中需要作的逻辑处理非常多,代码变得臃肿, 导出,可复用性差,后期维护性差 。Activity的生命周期是极其不稳定的。无法控制,无法预判。试想下面的一段场景,用户正在向服务器发送一条信息,由于网速慢,或者网络 出现阻塞,发送到接收持续了几十秒,在这期间来了个电话或者用户决定切换出去换一首歌,这时候线程完成的提交工作并从服务器获得了数据再更新UI的时候,原来的 Activity已经不存在了。更有可能的是系统发现资源不够用了,决定直接把我们的进程杀掉了。

如果你也和我一样遇到了这些问题,这篇文章就是为你写的。

从Hello World开始

看段小程序,程序本身很简单,Activity中有三个控件上边是个EditText,中间是个TextView,下边是个Button,用户在EditText中输入一段网址,点击按钮,把 网页中html内容显示到TextView当中。

  1. package com.chon.demo.httpoperation;

  2. import java.io.IOException;

  3. import com.chon.httpoperation.GetOperation;
  4. import com.chon.httpoperation.HandledResult;
  5. import com.chon.httpoperation.OperationListener;

  6. //省略了一些引入

  7. public class GetDemoActivity extends Activity {

  8. private EditText urlEdit;
  9. private TextView htmlText;
  10. private Button submitButton;

  11. OperationListener listener = new OperationListener(){
  12. @Override
  13. public void onError(long arg0, Bundle arg1, Exception e) {
  14. htmlText.setText("E:" + e);
  15. }

  16. @Override
  17. public void onError(long arg0, Bundle arg1, IOException e) {
  18. htmlText.setText("IOE:" + e);
  19. }

  20. @Override
  21. public void onNotOkay(long arg0, Bundle arg1, int code, String content) {
  22. htmlText.setText("code:" + code + "content:" + content);
  23. }

  24. @Override
  25. public void onResult(long arg0, Bundle arg1, HandledResult result) {
  26. htmlText.setText(result.extras.getString("html"));
  27. }

  28. };
  29. private MyApplication mApplication;

  30. @Override
  31. protected void onCreate(Bundle savedInstanceState) {
  32. super.onCreate(savedInstanceState);
  33. this.mApplication = (MyApplication)this.getApplication();
  34. initUI();
  35. }

  36. private void initUI(){
  37. this.setContentView(R.layout.get_demo);
  38. urlEdit = (EditText)findViewById(R.id.urlEdit);
  39. htmlText = (TextView)findViewById(R.id.htmlText);
  40. htmlText.setMovementMethod(ScrollingMovementMethod.getInstance());
  41. submitButton = (Button)findViewById(R.id.submitButton);
  42. submitButton.setOnClickListener(new OnClickListener(){
  43. @Override
  44. public void onClick(View v) {
  45. GetOperation getOperation = new GetOperation(10, urlEdit.getText().toString(),
  46. DummyHtmlHandler.class, listener);
  47. mApplication.request(getOperation);
  48. }
  49. });
  50. }
  51. }
复制代码

处理Http请求可以如此简单和清晰。我们来看一下上面的代码都作了些什么

  1. submitButton.setOnClickListener(new OnClickListener(){
  2. @Override
  3. public void onClick(View v) {
  4. GetOperation getOperation = new GetOperation(10, urlEdit.getText().toString(),
  5. DummyHtmlHandler.class, listener);
  6. mApplication.request(getOperation);
  7. }
  8. });
复制代码

当按钮被按下时,创建了一个GepOperation对象,在构造函数中传入了一定的参数,第一个是一次请求的id,因为本例中只有一次请求,所以这个参数可以 忽略,第二个是请求的url,第三个是一个类,表示如果处理服务器端返回的内容(如xml解析规则等,因本例不作任何处理,这个处理类也非常简单,一会儿 再说),第四个是一个OperationListener 的对象,一个回调方法,当HttpGet请求处理完毕后会把结果回调给这个对象。因为是从UI线程回调,我们可以直接 对UI进行操作。

下面我来解释下这期间到底发生了什么,GetOperation封装了对HttpGet的操作,当调用mApplication.request(getOperation)的时候,mApplication负责把这个请求 对象转发到后台的Service当中,并在一个线程池中处理运行,当服务器响应后,后台的Service会把服务器返回的数据交给DummyHtmlHanlder这个类处理,并把处理 结果从UI线程回调listener的onResult()返回给当前的Activity。 再看下这个极其简单的DummyHtmlHandler:

  1. package com.chon.demo.httpoperation;

  2. import java.io.InputStream;

  3. import android.os.Bundle;

  4. import com.chon.httpoperation.Handleable;
  5. import com.chon.httpoperation.HandledResult;

  6. public class DummyHtmlHandler implements Handleable {
  7. @Override
  8. public int getContentType() {
  9. return Handleable.TYPE_STRING;
  10. }

  11. @Override
  12. public HandledResult handle(String content, Bundle bundle) {
  13. Bundle extras = new Bundle();
  14. extras.putString("html", content);
  15. return new HandledResult(extras, null, null);
  16. }

  17. @Override
  18. public HandledResult handle(InputStream arg0, Bundle arg1) {
  19. return null;
  20. }
  21. }
复制代码

希望处理服务器响应数据的类需要实现Handleable这个接口,接口定义了三个方法public int getContentType()是用来表示,自己希望得到什么形式的数据 有TYPE_STRING 和 TYPE_STREAM ,Service会根据这个值来回调下面的两个handle中的一种(也就是说如果getContentType返回的是TYPE_STRING, public HandledResult handle(String content, Bundle bundle),如果是TYPE_STREAMpublic HandledResult handle(InputStream arg0, Bundle arg1) 并将服务器返回的数据传入该方法中。处理后的数据通过一个叫HandledResult的类的对你返回给Activity,其实是个很简单的类,里边有三 个成员变量 一个Bundle,一个ArrayList和一个Object。起到信息传输的作用。好了,最后list.onResult(long arg0, Bundle arg1, HandledResult result)被回调,我们从 HandledResult对象中取出我们处理好的数据,更新UI就可以了。

这个小例子可能看不出,多大的好处,我们再看一下RssActivity中的代码。

  1. package com.chon.demo.httpoperation;

  2. //省略了一些引入
  3. public class RssActivity extends Activity {

  4. private OperationListener listener = new OperationListener(){

  5. @Override
  6. public void onError(long arg0, Bundle arg1, Exception e) {
  7. setTitle("Exception " + e);
  8. }

  9. @Override
  10. public void onError(long arg0, Bundle arg1, IOException e) {
  11. setTitle("IOException " + e);
  12. }

  13. @Override
  14. public void onNotOkay(long arg0, Bundle arg1, int code, String arg3) {
  15. setTitle("Oh Oh " + code);//404之类的会触发本回调
  16. }

  17. @SuppressWarnings("unchecked")
  18. @Override
  19. public void onResult(long arg0, Bundle arg1, HandledResult result) {
  20. System.out.println("size:" + result.results.size());
  21. setTitle("Loading Complete");
  22. mAdapter.setData((ArrayList) result.results);
  23. }
  24. };
  25. private MyApplication mApplication;
  26. private ListView rssListView;
  27. private RssAdapter mAdapter;

  28. @Override
  29. protected void onCreate(Bundle savedInstanceState) {
  30. super.onCreate(savedInstanceState);
  31. this.mApplication = (MyApplication) this.getApplication();
  32. initUI();
  33. }

  34. private void initUI(){
  35. this.setContentView(R.layout.rss);
  36. this.rssListView = (ListView)findViewById(R.id.rssListView);
  37. rssListView.setOnItemClickListener(new OnItemClickListener(){
  38. @Override
  39. public void onItemClick(AdapterView parent, View view,
  40. int position, long id) {
  41. Intent it = new Intent(Intent.ACTION_VIEW, Uri.parse(mAdapter.getData().get(position).getLink()));
  42. it.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
  43. startActivity(it);
  44. }
  45. });
  46. mAdapter = new RssAdapter(this.getLayoutInflater());
  47. rssListView.setAdapter(mAdapter);
  48. findViewById(R.id.button).setOnClickListener(new OnClickListener(){
  49. @Override
  50. public void onClick(View v) {
  51. loadRss();
  52. }
  53. });
  54. };

  55. void loadRss(){
  56. setTitle("Loading.....");
  57. String url = "http://rss.sina.com.cn/news/marquee/ddt.xml";//新浪的Rss订阅
  58. GetOperation getOperation = new GetOperation(10,url,RssHandler.class, listener );
  59. mApplication.request(getOperation);
  60. }
  61. }
复制代码

你会发现虽然我们的程序复杂了许多,但Activity的代码量并没有增加多少,我们不需要创建Handler,定义许多常量,创建Thread对象 etc.一切都己被处理好了 我们需要作的就只有简单的三步,创建一个对象,编写自己的处理类,被回调时更新UI(或者别的)。 有兴趣的可以看看Rss的解析类,本例中使用了XmlPull,当然你可以使用任何一种解析方式。或者解析json etc.(面向接口编程的好处 :))

  1. package com.chon.demo.rssreader;
  2. public class RssHandler implements Handleable {

  3. static final String ITEM = "item";
  4. private int currentstate = -1;
  5. final int TITLE = 1;
  6. final int LINK = 2;
  7. final int DESCRIPTION = 3;
  8. final int PUBDATE = 4;

  9. @Override
  10. public int getContentType() {
  11. return Handleable.TYPE_STREAM;
  12. }

  13. @Override
  14. public HandledResult handle(String content,Bundle extras) {
  15. return null;
  16. }

  17. @Override
  18. public HandledResult handle(InputStream content,Bundle extras) {

  19. XmlPullParserFactory xmlPullParserFactory;
  20. XmlPullParser xmlPullParser = null;
  21. try {
  22. xmlPullParserFactory = XmlPullParserFactory.newInstance();
  23. xmlPullParserFactory.setNamespaceAware(true);
  24. xmlPullParser = xmlPullParserFactory.newPullParser();
  25. } catch (XmlPullParserException e1) {
  26. e1.printStackTrace();
  27. }
  28. RssItem rssItem = null;

  29. ArrayList rssItemList = new ArrayList();
  30. boolean isItemTAG = false;
  31. try {
  32. xmlPullParser.setInput(content, "utf-8");

  33. int eventType = xmlPullParser.getEventType();
  34. while (eventType != XmlPullParser.END_DOCUMENT) {

  35. if (eventType == XmlPullParser.START_DOCUMENT) {
  36. System.out.println("start Document...");
  37. } else if (eventType == XmlPullParser.END_DOCUMENT) {
  38. System.out.println("end Document...");
  39. } else if (eventType == XmlPullParser.START_TAG) {
  40. if (xmlPullParser.getName().equals("item")) {
  41. rssItem = new RssItem();
  42. isItemTAG = true;
  43. }
  44. if (xmlPullParser.getName().equals("title")) {
  45. currentstate = TITLE;
  46. }
  47. if (xmlPullParser.getName().equals("link")) {
  48. currentstate = LINK;
  49. }
  50. if (xmlPullParser.getName().equals("description")) {
  51. currentstate = DESCRIPTION;
  52. }
  53. if (xmlPullParser.getName().equals("pubDate")) {
  54. currentstate = PUBDATE;
  55. }
  56. } else if (eventType == XmlPullParser.END_TAG) {
  57. if (xmlPullParser.getName().equals("item")) {
  58. rssItemList.add(rssItem);
  59. }
  60. } else if (eventType == XmlPullParser.TEXT) {
  61. if (isItemTAG) {
  62. switch (currentstate) {
  63. case TITLE:
  64. System.out.println(xmlPullParser.getText());
  65. rssItem.setTitle(clearSpecialChar(xmlPullParser
  66. .getText()));
  67. currentstate = -1;
  68. break;
  69. case LINK:
  70. rssItem.setLink(clearSpecialChar(xmlPullParser
  71. .getText()));
  72. currentstate = -1;
  73. break;
  74. case DESCRIPTION:
  75. rssItem.setDescription(clearSpecialChar(xmlPullParser
  76. .getText()));
  77. currentstate = -1;
  78. break;
  79. case PUBDATE:
  80. rssItem.setPubData(clearSpecialChar(xmlPullParser
  81. .getText()));
  82. currentstate = -1;
  83. break;
  84. default:
  85. break;
  86. }
  87. }
  88. }
  89. eventType = xmlPullParser.next();
  90. }
  91. } catch (XmlPullParserException e) {
  92. e.printStackTrace();
  93. } catch (IOException e) {
  94. e.printStackTrace();
  95. }
  96. return new HandledResult(null,rssItemList,null);
  97. }

  98. private String clearSpecialChar(String s) {
  99. Pattern pattern = Pattern.compile("\\s|\\r|\\n|\\t");
  100. Matcher matcher = pattern.matcher(s);
  101. return matcher.replaceAll("").trim();
  102. }
  103. }
复制代码 还有一个从网上下载图片的示例大家自己看吧,不在这里进行讲解了。如果你想增加别的功能的支持,有两种选择,比如只是想增加一个功能 可以下载文件并存入sd卡什么的,operation对象,可以接收一个Bundle,你可以把文件路径,名字等放到 这个Bundle当中,并写一个自己的 处理类(实现Handleable的)。或者你想加入对WebService的支持(现在正在作,但功能不是很完善) 要只需要继承AbstractOperations类,覆写handleOperation()方法(逻辑处理创建HttpClient等放在这里,而不是run()), 并把处理结果或者产生的错误等信息,通过父类提供的protected void sendExceptionMsg(final Exception e), protected void sendSuccessMsg(final HandledResult result) 等方法回调给OperationsListener对象。

对了别忘 了继承HttpOperationApplication和HttpOperationService在manifest中注册。并把Service告诉Application。像这样:
  1. package com.chon.httpoperation.test;

  2. import com.chon.httpoperation.HttpOperationApplication;

  3. public class MyApplication extends HttpOperationApplication {
  4. {
  5. this.setOperationService(MyService.class);
  6. }
  7. }
复制代码 HttpOperationService的子类可以通过setIdleInMinute(),Service空闲多少分钟时自行关闭,不过不用担心 当有心的请求时Service会自动开启。还有一些功,比如有个cachable接口,可以在operation对象中传入实现了 这个接口的对象,当Service执行完后发现如果调用的Activity(确切说是OperationListener)不存在了,会把处理的 结果缓存的这个对象当中。具体操作,以后有时间再写。

更多相关文章

  1. Android(安卓)Duplicate files copied in APK
  2. Android(安卓)webkit 事件传递流程
  3. Android开启自学之路
  4. portrait表示横向,landscape表示纵向
  5. Android(安卓)Parcelable
  6. android app崩溃日志收集以及上传
  7. 新手android 开发 错误集锦(持续更新中)
  8. Android(安卓)本地代码中的LIKELY和UNLIKELY宏
  9. Android的事件处理机制(概念理解)

随机推荐

  1. android studio常用控件
  2. Android入门一:Android(安卓)开发环境安装
  3. Android(安卓)requires compiler complia
  4. 使用Javascript判断浏览器终端设备(PC、I
  5. Android概述
  6. android SDK系统图片资源的路径。
  7. 补间动画--透明渐变XML
  8. Android启动画面实现
  9. 【读书笔记】【Android(安卓)开发艺术探
  10. 修改Android(安卓)EditText光标颜色