`
zy77612
  • 浏览: 283468 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

轻量级Android HTTP框架

 
阅读更多

转自:

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.     
  12.     OperationListener listener = new OperationListener(){
  13.         @Override
  14.         public void onError(long arg0, Bundle arg1, Exception e) {
  15.             htmlText.setText("E:"  + e);
  16.         }

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

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

  25.         @Override
  26.         public void onResult(long arg0, Bundle arg1, HandledResult result) {
  27.             htmlText.setText(result.extras.getString("html"));
  28.         }
  29.         
  30.     };
  31.     private MyApplication mApplication;

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

处理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.     
  35.     private void initUI(){
  36.         this.setContentView(R.layout.rss);
  37.         this.rssListView = (ListView)findViewById(R.id.rssListView);
  38.         rssListView.setOnItemClickListener(new OnItemClickListener(){
  39.             @Override
  40.             public void onItemClick(AdapterView parent, View view,
  41.                     int position, long id) {
  42.                 Intent it = new Intent(Intent.ACTION_VIEW, Uri.parse(mAdapter.getData().get(position).getLink()));
  43.                 it.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
  44.                 startActivity(it);
  45.             }
  46.         });
  47.         mAdapter = new RssAdapter(this.getLayoutInflater());
  48.         rssListView.setAdapter(mAdapter);
  49.         findViewById(R.id.button).setOnClickListener(new OnClickListener(){
  50.             @Override
  51.             public void onClick(View v) {
  52.                 loadRss();
  53.             }
  54.         });
  55.     };
  56.     
  57.     void loadRss(){
  58.         setTitle("Loading.....");
  59.         String url = "http://rss.sina.com.cn/news/marquee/ddt.xml";//新浪的Rss订阅
  60.         GetOperation getOperation = new GetOperation(10,url,RssHandler.class, listener );
  61.         mApplication.request(getOperation);
  62.     }
  63. }
复制代码

你会发现虽然我们的程序复杂了许多,但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.         
  30.         ArrayList rssItemList = new ArrayList();
  31.         boolean isItemTAG = false;
  32.         try {
  33.             xmlPullParser.setInput(content, "utf-8");

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

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

  99.     private String clearSpecialChar(String s) {
  100.         Pattern pattern = Pattern.compile("\\s|\\r|\\n|\\t");
  101.         Matcher matcher = pattern.matcher(s);
  102.         return matcher.replaceAll("").trim();
  103.     }
  104. }
复制代码
还有一个从网上下载图片的示例大家自己看吧,不在这里进行讲解了。如果你想增加别的功能的支持,有两种选择,比如只是想增加一个功能 可以下载文件并存入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)不存在了,会把处理的 结果缓存的这个对象当中。具体操作,以后有时间再写。

分享到:
评论

相关推荐

    AndroidFramework android轻量级框架

    《Android Framework:轻量级框架解析》 在移动开发领域,Android Framework作为系统的核心部分,为开发者提供了丰富的API和工具,使得开发应用变得更加便捷。本文将深入探讨Android轻量级框架,关注其在数据库操作...

    android轻量级框架,数据库操作、Http操作等

    在Android开发中,轻量级框架的使用是提高效率和代码可维护性的重要手段。这些框架通常具有小巧、高效和易用的特点,适用于各种规模的项目。本篇将重点介绍两个关键方面:数据库操作和Http请求,这些都是Android开发...

    Node.js-AndHook是Android的轻量级hook钩子框架

    AndHook是针对Android平台设计的轻量级hook框架,它允许开发者在不修改原始代码的情况下,动态地拦截并修改应用程序的行为。AndHook的核心原理是基于Java的反射机制和底层的Native层函数挂钩。通过AndHook,开发者...

    轻量级Web应用框架blademobile.zip

    支持情况该框架是移动端框架,主要支持IOS6 、android4 框架发展第一期-MVC该框架第一期的目标是简单的webapp MVC的实现,现在也基本实现了,app支持hashChange与pushState两种方式做路由加载view,对此有兴趣的同学...

    轻量小巧http框架http库支持https

    本篇文章将重点介绍一个支持HTTPS的轻量级HTTP框架,并结合提供的资源,解析其特点和使用方法。 标题中的“轻量小巧http框架http库支持https”表明这是一个专为Java开发设计的小型HTTP库,它不仅具备基本的HTTP请求...

    droid fu android应用框架源码

    - **事件处理**:Droid Fu可能包含了自定义的事件处理机制,比如轻量级的消息传递系统,使组件间通信更简单。 - **UI辅助**:可能提供了易于使用的UI组件或者布局管理工具,简化界面设计。 - **异步操作**:可能...

    http轻量框架

    1. **轻量级**:该框架体积小巧,对应用程序的内存占用低,适合资源有限的移动设备。 2. **易用性**:提供简洁的API接口,使得HTTP请求的编写简单直观,减少代码量。 3. **异步处理**:支持异步网络请求,不阻塞UI...

    轻量级消息推送client&server

    本文将基于“轻量级消息推送client&server”这一主题,深入探讨如何构建一个简单而有效的消息推送系统,主要关注Android平台以及Java后端。 标题:“轻量级消息推送client&server” 描述中提到,开发者在尝试使用...

    okhttps轻量级HTTP客户端.rar

    因此,在这里推荐一个适用于SpringBoot项目的轻量级HTTP客户端框架retrofit-spring-boot-starter,使用非常简单方便,同时又提供诸多功能增强。目前项目已经更新至2.2.2版本,并且会持续进行迭代优化。 前言 ...

    Android AndBase框架使用封装好的函数完成Http请求(三)

    在比较AndBase和Volley这两个框架时,Volley是Google专门为Android设计的网络库,其特点是轻量级、高性能,特别注重响应速度和内存管理。Volley通过接口和抽象方法的封装,使得代码更加灵活,易于扩展。而AndBase则...

    使用android快速开发框架afinal 开发android应用程序

    4、里面包含了一个轻量级的http框架,对http请求只要一行代码就可以完成。 afinal源码地址 http://code.google.com/p/afinal/ 文章地址:http://blog.csdn.net/michael_yy/article/details/8084951

    Android网络请求框架

    1. Volley:由Google开发的轻量级网络库,专注于快速、高效的网络通信。Volley提供了一种缓存机制,适合频繁的小数据量请求,如刷新列表。 2. Retrofit:由Square公司开发,它通过注解的方式将网络接口映射到HTTP...

    android开发框架

    首先,Afinal是一个轻量级的Android开发框架,由陈昭(Final Chen)开发。它的核心特点包括简洁的API设计、高效的数据库操作和强大的网络请求库。Afinal提供了对SQLite数据库的简单易用的封装,使得开发者能够通过...

    最小的android框架

    在Android开发中,这样的框架通常由开发者根据特定需求定制,以实现轻量级、快速加载和低内存占用。 在Android开发中,框架扮演着至关重要的角色,它为开发者提供了结构化的开发环境和工具,使得应用的构建更加高效...

    Android平台中轻量级音视频引擎.pdf

    本文主要探讨了在Android平台上构建这样一个轻量级音视频引擎的方法,通过模块化设计,实现了高效、小巧且功能强大的音视频处理能力。 首先,轻量级音视频引擎的核心在于对多媒体处理流程的深入理解和优化。这一...

    android 异步操作框架

    Android提供了多种异步处理框架,其中一个被广泛使用的开源框架是“android-async-http”,它是一个轻量级、高性能的网络库,非常适合进行HTTP请求。 **Android异步操作的重要性** 在Android系统中,主线程负责处理...

    Android-轻量级自动化埋点方案

    "Android-轻量级自动化埋点方案"是一种旨在减少手动埋点工作量、提高数据采集效率的方法。该方案主要利用了Android系统中的窗口回调(Window.Callback)机制。 在Android中,每个Activity都有一个Window对象,而...

    android快速开发框架

    Volley则是一款轻量级的网络库,适合处理小量的数据请求,其强大的请求队列管理使得并发请求变得简单。 HTTP请求是移动应用与服务器交互的基础。"Android快速开发框架"通常会封装如Retrofit或Volley这样的库,提供...

    android 加载网络框架

    8. **依赖注入友好**:由于其轻量级和模块化设计,`android-async-http`可以方便地与其他依赖注入库如Dagger集成,使得代码更加可测试和可维护。 尽管`android-async-http`是一个优秀的网络库,但随着Android的发展...

    安卓开发框架工具类相关-Android项目框架搭建.rar

    6. **Coroutines**:为了解决异步编程的复杂性,项目可能采用了Kotlin的Coroutines,它提供了一种轻量级的线程管理方式,使异步代码看起来更像同步代码。 7. **Navigation组件**:用于应用程序导航的统一管理,可以...

Global site tag (gtag.js) - Google Analytics