Android的网络编程

时间:2022-12-09 18:39:26

1、3主要接口

Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口)。下面分别介绍这些接口的功能和作用。

1.1 标准Java接口

java.net.*提供与联网有关的类,包括流、数据包套接字(socket)、Internet协议、常见Http处理等。比如:创建URL,以及URLConnection/HttpURLConnection对象、设置链接参数、链接到服务器、向服务器写数据、从服务器读取数据等通信。这些在Java网络编程中均有涉及,我们看一个简单的socket编程,实现服务器回发客户端信息。

1.2 Apache接口

对于大部分应用程序而言JDK本身提供的网络功能已远远不够,这时就需要Android提供的Apache HttpClient了。它是一个开源项目,功能更加完善,为客户端的Http编程提供高效、最新、功能丰富的工具包支持。

1.3 android.net编程:

常常使用此包下的类进行Android特有的网络编程,如:访问WiFi,访问Android联网信息,邮件等功能。

2、Http两访问方式:HttpURLConnection(java.net.)、HttpClient(org.apache.http.client.)

2.1 HttpURLConnection

  HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序。虽然HttpURLConnection的API提供的比较简单,但是同时这也使得我们可以更加容易地去使用和扩展它。

  不过在Android 2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

 private void disableConnectionReuseIfNecessary() {
     // 这是一个2.2版本之前的bug
     if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
         System.setProperty("http.keepAlive", "false");
     }
 } 

2.2 HttpClient

  DefaultHttpClient和它的兄弟AndroidHttpClient都是HttpClient具体的实现类,它们都拥有众多的API,而且实现比较稳定,bug数量也很少。但同时也由于HttpClient的API数量过多,使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,所以目前Android团队在提升和优化HttpClient方面的工作态度并不积极。

2.3 小结:

This API(HttpURLConnection) is more efficient because it reduces network use through transparent compression and response caching, and minimizes power consumption.

  Android6.0已经将HttpClient移除,官方给出的解释如上,在我看来不外乎效率和扩展两方面考虑,但HttpClient毕竟是Apache提供的一个不错的API,所以有必要将HttpURLConnection,HttpClient进行比较,让我们在对比中感受他们各自的特点。

A 对比观点一 http://blog.csdn.net/wszxl492719760/article/details/8522714

  HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能:HttpURLConnection。

  HttpURLConnection是java的标准类,HttpURLConnection继承自URLConnection,可用于向指定网站发送GET请求、POST请求。它在URLConnection的基础上提供了如下便捷的方法:

  • int getResponseCode():获取服务器的响应代码。

  • String getResponseMessage():获取服务器的响应消息。

  • String getResponseMethod():获取发送请求的方法。

  • void setRequestMethod(String method):设置发送请求的方法。

  在一般情况下,如果只是需要Web站点的某个简单页面提交请求并获取服务器响应,HttpURLConnection完全可以胜任。但在绝大部分情况下,Web站点的网页可能没这么简单,这些页面并不是通过一个简单的URL就可访问的,可能需要用户登录而且具有相应的权限才可访问该页面。在这种情况下,就需要涉及Session、Cookie的处理了,如果打算使用HttpURLConnection来处理这些细节,当然也是可能实现的,只是处理起来难度就大了。

  为了更好地处理向Web站点请求,包括处理Session、Cookie等细节问题,Apache开源组织提供了一个HttpClient项目,看它的名称就知道,它是一个简单的HTTP客户端(并不是浏览器),可以用于发送HTTP请求,接收HTTP响应。但不会缓存服务器的响应,不能执行HTML页面中嵌入的Javascript代码;也不会对页面内容进行任何解析、处理。

  简单来说,HttpClient就是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、接收响应,以及管理HTTP连接。

  使用HttpClient发送请求、接收响应很简单,只要如下几步即可。

  1. 创建HttpClient对象。
  2. 如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
  3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
  4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,执行该方法返回一个HttpResponse。
  5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。

  比如一个Android应用需要向指定页面发送请求,但该页面并不是一个简单的页面,只有当用户已经登录,而且登录用户的用户名有效时才可访问该页面。如果使用HttpURLConnection来访问这个被保护的页面,那么需要处理的细节就太复杂了。

  其实访问Web应用中被保护的页面,使用浏览器则十分简单,用户通过系统提供的登录页面登录系统,浏览器会负责维护与服务器之间的Sesion,如果用户登录的用户名、密码符合要求,就可以访问被保护资源了。

  在Android应用程序中,则可使用HttpClient来登录系统,只要应用程序使用同一个HttpClient发送请求,HttpClient会自动维护与服务器之间的Session状态,也就是说程序第一次使用HttpClient登录系统后,接下来使用HttpClient即可访问被保护页而了。

B 对比观点二 http://blog.csdn.net/huzgd/article/details/8712187

  1.HttpClient是apache的开源实现,而HttpUrlConnection是安卓标准实现;

  2.HttpUrlConnection直接支持GZIP压缩;HttpClient也支持,但要自己写代码处理;我们之前测试HttpUrlConnection的GZIP压缩在传大文件分包trunk时有问题,只适合小文件,不过这个BUG后来官方说已经修复了;

  3.HttpUrlConnection直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;HttpClient当然也能做到,但毕竟不如官方直接系统底层支持好;

  4.HttpUrlConnection直接在系统层面做了缓存策略处理,加快重复请求的速度。

  在安卓开发上,虽然HttpClient更好地支持很多细节的控制(如代理、COOKIE、鉴权、压缩、连接池),但相应地对开发人员要求更高,代码写起来更复杂,普通开发人员很难做到对它很好地驾驭,官方的支持也越来越少;而HttpUrlConnection对大部分工作进行了包装,屏蔽了不需要的细节,更适合开发人员直接调用,而且官方对它的支持和优化也会越来越好。我们既然是做安卓应用的开发,自然要遵循安卓官方的指引,选用HttpUrlConnection。

2.4 HTTP通信 GET/POST DEMO

1 HttpUrlConnection通信GET方式

 public static String loginByURLGet(String path, String username, String password) {
     String str = null;
     try {
         // 拼接路径
         path += "?username=" + URLEncoder.encode(username, "UTF-8") + "&password=" + URLEncoder.encode(password);
         URL url = new URL(path);
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         conn.setConnectTimeout(5000);
         conn.setRequestMethod("GET");
         // 请求成功
         if (200 == conn.getResponseCode()) {
             InputStream is = conn.getInputStream();
             str = StreamTools.readInputStream(is);
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     return str;
 }

2 HttpUrlConnection通信POST方式

 public static String loginByURLPost(String path, String username, String password) {
     String str = null;
     try {
         URL url = new URL(path);
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         conn.setConnectTimeout(5000);
         conn.setRequestMethod("POST");
         // 传参
         String data = "username" + URLEncoder.encode(username) + "&password" + URLEncoder.encode(password);
         conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
         conn.setRequestProperty("Content-Length", data.length() + "");
         conn.setDoOutput(true);
         OutputStream os = conn.getOutputStream();
         os.write(data.getBytes());
         // 请求成功
         if (200 == conn.getResponseCode()) {
             InputStream is = conn.getInputStream();
             str = StreamTools.readInputStream(is);
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     return str;
 }

3 HttpClient通信GET方式

 public static String loginByApacheGet(String path, String username, String password) {
     String str = null;
     try {
         HttpClient client = new DefaultHttpClient();
         path += "?username=" + URLEncoder.encode(username, "UTF-8") + "&password=" + URLEncoder.encode(password);
         HttpGet httpGet = new HttpGet(path);
         HttpResponse response = client.execute(httpGet);
         // 请求成功
         if (200 == response.getStatusLine().getStatusCode()) {
             InputStream is = response.getEntity().getContent();
             str = StreamTools.readInputStream(is);
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     return str;
 }

4 HttpClient通信POST方式

 public static String loginByApachePost(String path, String username, String password) {
     String str = null;
     try {
         HttpClient client = new DefaultHttpClient();
         HttpPost httpPost = new HttpPost(path);
         // 传参
         List<NameValuePair> parameters = new ArrayList<NameValuePair>();
         parameters.add(new BasicNameValuePair("username", username));
         parameters.add(new BasicNameValuePair("password", password));
         httpPost.setEntity(new UrlEncodedFormEntity(parameters, "UTF-8"));
         HttpResponse response = client.execute(httpPost);
         // 请求成功
         if (200 == response.getStatusLine().getStatusCode()) {
             InputStream is = response.getEntity().getContent();
             str = StreamTools.readInputStream(is);
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     return str;
 }

StreamTools.readInputStream();

     public static String readInputStream(InputStream is) {
         try {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             int len = 0;
             byte[] buffer = new byte[1024];
             while ((len = is.read(buffer)) != -1) {
                 baos.write(buffer, 0, len);
             }
             is.close();
             baos.close();
             byte[] result = baos.toByteArray();
             // 试着解析result里的字符串
             String temp = new String(result, "gbk");
             return temp;
         } catch (Exception e) {
             e.printStackTrace();
             return "获取失败";
         }
     }

3、其他通信方式

  HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码。于是乎,一些Android网络通信框架也就应运而生:

  比如说AsyncHttpClient,它把HTTP所有的通信细节全部封装在了内部,我们只需要简单调用几行代码就可以完成通信操作了。

  再比如Universal-Image-Loader,它使得在界面上显示网络图片的操作变得极度简单,开发者不用关心如何从网络上获取图片,也不用关心开启线程、回收图片资源等细节,Universal-Image-Loader已经把一切都做好了。

  Android开发团队也是意识到了有必要将HTTP的通信操作再进行简单化,于是在2013年Google I/O大会上推出了一个新的网络通信框架——Volley。Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。除了简单易用之外,Volley在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。

  总的来说,数据量不大,但网络通信频繁的应用,非常适合使用Volley。

3.1 AsyncHttpClient

  核心类,使用HttpClient执行网络请求,提供了get,put,post,delete,head等请求方法,使用起来很简单,只需以url及RequestParams调用相应的方法即可,还可以选择性地传入Context,用于取消Content相关的请求,同时必须提供ResponseHandlerInterface(AsyncHttpResponseHandler继承自ResponseHandlerInterface)的实现类,一般为AsyncHttpResponseHandler的子类,AsyncHttpClient内部有一个线程池,当使用AsyncHttpClient执行网络请求时,最终都会调用sendRequest方法,在这个方法内部将请求参数封装成AsyncHttpRequest(继承自Runnable)交由内部的线程池执行。

步骤
  1. 创建一个AsyncHttpClient;
  2. (可选的)通过RequestParams对象设置请求参数;
  3. 调用AsyncHttpClient的某个get方法,传递你需要的(成功和失败时)callback接口实现,一般都是匿名内部类,实现了AsyncHttpResponseHandler,类库自己也提供了好些现成的response handler,你一般不需要自己创建一个。
特点
  1. 发送异步http请求,在匿名callback对象中处理response;
  2. http请求发生在UI线程之外;
  3. 内部采用线程池来处理并发请求;
  4. GET/POST 参数构造,通过RequestParams类。
  5. 内置多部分文件上传,不需要第三方库支持;
  6. 流式Json上传,不需要额外的库;
  7. 能处理环行和相对重定向;
  8. 和你的app大小相比来说,库的size很小,所有的一切只有90kb;
  9. 自动智能的请求重试机制在各种各样的移动连接环境中;
  10. 自动的gzip响应解码;
  11. 内置多种形式的响应解析,有原生的字节流,string,json对象,甚至可以将response写到文件中;
  12. 永久的cookie保存,内部实现用的是Android的SharedPreferences;
  13. 通过BaseJsonHttpResponseHandler和各种json库集成;
  14. 支持SAX解析器;
  15. 支持各种语言和content编码,不仅仅是UTF-8。
DEMO

AsyncHttpClient通信Get方式

 AsyncHttpClient client = new AsyncHttpClient();
 client.get("http://www.google.com", new AsyncHttpResponseHandler() {

     @Override
     public void onStart() {
         // called before request is started
     }

     @Override
     public void onSuccess(int statusCode, Header[] headers, byte[] response) {
         // called when response HTTP status is "200 OK"
     }

     @Override
     public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
         // called when response HTTP status is "4XX" (eg. 401, 403, 404)
     }

     @Override
     public void onRetry(int retryNo) {
         // called when request is retried
     }
 });

AsyncHttpClient通信Post方式

 AsyncHttpClient client = new AsyncHttpClient();
 RequestParams params = new RequestParams();
 params.put("key", "value");
 params.put("more", "data");
 client.post("http://www.google.com", params, new
     AsyncHttpResponseHandler() {
         @Override
         public void onSuccess(int statusCode, Header[] headers, byte[] response) {
             System.out.println(response);
         }

         @Override
         public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
             Log.d("ERROR", error);
         }
     }
 );

3.2 Universal-Image-Loader

特点:

  1. 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等
  2. 支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
  3. 支持图片的内存缓存,文件系统缓存或者SD卡缓存
  4. 支持图片下载过程的监听
  5. 根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
  6. 较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动过程中暂停加载图片,停止滑动的时候去加载图片
  7. 提供在较慢的网络下对图片进行加载

DEMO1

 final ImageView mImageView = (ImageView) findViewById(R.id.image);
 String imageUrl = "http://upload.ct.youth.cn/2016/0416/1460802358119.jpg";  

 ImageLoader.getInstance().loadImage(imageUrl, new ImageLoadingListener() {  

     @Override
     public void onLoadingStarted(String imageUri, View view) {  

     }  

     @Override
     public void onLoadingFailed(String imageUri, View view,
             FailReason failReason) {  

     }  

     @Override
     public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
         mImageView.setImageBitmap(loadedImage);
     }  

     @Override
     public void onLoadingCancelled(String imageUri, View view) {  

     }
 });

DEMO2

 File cacheDir = StorageUtils.getCacheDirectory(context);
 ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
         .memoryCacheExtraOptions(480, 800) // default = device screen dimensions
         .diskCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null)
         .taskExecutor(...)
         .taskExecutorForCachedImages(...)
         .threadPoolSize(3) // default
         .threadPriority(Thread.NORM_PRIORITY - 1) // default
         .tasksProcessingOrder(QueueProcessingType.FIFO) // default
         .denyCacheImageMultipleSizesInMemory()
         .memoryCache(new LruMemoryCache(2 * 1024 * 1024))
         .memoryCacheSize(2 * 1024 * 1024)
         .memoryCacheSizePercentage(13) // default
         .diskCache(new UnlimitedDiscCache(cacheDir)) // default
         .diskCacheSize(50 * 1024 * 1024)
         .diskCacheFileCount(100)
         .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
         .imageDownloader(new BaseImageDownloader(context)) // default
         .imageDecoder(new BaseImageDecoder()) // default
         .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
         .writeDebugLogs()
         .build(); 

3.3 volley http://www.tuicool.com/articles/eeyQ3eE

特点

  1. 自动调度网络请求
  2. 多个并发的网络连接
  3. 通过使用标准的HTTP缓存机制保持磁盘和内存响应的一致
  4. 支持请求优先级
  5. 支持取消请求的强大API,可以取消单个请求或多个
  6. 易于定制
  7. 健壮性:便于正确的更新UI和获取数据
  8. 包含调试和追踪工具

优点:

  1. 默认Android2.3及以上基于HttpURLConnection,2.3以下使用基于HttpClient;
  2. 符合Http 缓存语义 的缓存机制(提供了默认的磁盘和内存等缓存);
  3. 请求队列的优先级排序;
  4. 提供多样的取消机制;
  5. 提供简便的图片加载工具(其实图片的加载才是我们最为看重的功能);
  6. 一个优秀的框架 。

缺点:它只适合数据量小,通信频繁的网络操作,如果是数据量大的,像音频,视频等的传输,还是不要使用Volley的为好。

3.4 Retrofit,OkHttp,NoHttp 

  • Retrofit: A type-safe REST client for Android and Java Retrofit简化了从Web API下载数据,解析成普通的Java对象(POJO)。
  • OKHttp是Android版Http客户端。非常高效,支持SPDY、连接池、GZIP和 HTTP 缓存。默认情况下,OKHttp会自动处理常见的网络问题,像二次连接、SSL的握手问题。如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求。Retrofit使用GSON解析JSON数据。OKHttp与HttpUrlConnection一样,都是面向java的,而且是做socket层的事情的,使用时要在子线程调用,通过handler发送结果到主线程。
  • 而NoHttp和Volley不是做socket层的东西。直接在主线程就可以调用,拿到结果后直接更新UI,不需要用handler去发送, 很简单。

  NoHttp:封装了文件下载、断点续传、304缓存、302/303传参数、传文件、请求头、多文件上传、大文件上传、Cookie自动管理等多种功能,这些是Volley而没有,使用Volley时这些功能要我们去写蛮多代码来再次封装。而且Volley用的HttpClient来解析的,Android6.0删除了HttpClient后,我们在6.0下也不能使用Volley的源码了,所以还是用NoHttp吧,NoHttp兼容2.0-6.0以上。而使用OkHttp还需要我们自己去封装, 而且Android4.4之后NoHttp也是使用OkHttp做底层的。

  Retrofit:和OkHttp都是square团队开发的。Retrofit是一套RESTful架构的Android(Java)客户端实现,基于注解,提供JSON to POJO(Plain Ordinary Java Object,简单Java对象),POJO to JSON,网络请求(POST,GET,PUT,DELETE等)封装。

  Retrofit 和Java领域的ORM概念类似, ORM把结构化数据转换为Java对象,而Retrofit 把REST API返回的数据转化为Java对象方便操作。同时还封装了网络代码的调用。

Android的网络编程

Retrofit功能组件

  • POJO或模型实体类 : 从服务器获取的JSON数据将被填充到这种类的实例中。
  • 接口 : 我们需要创建一个接口来管理像GET,POST...等请求的URL,这是一个服务类。
  • RestAdapter类 : 这是一个REST客户端(RestClient)类,retrofit中默认用的是Gson来解析JSON数据,也可以设置自己的JSON解析器,比如jackson

Retrofit使用步骤:

  1. 定义接口,参数声明,Url都通过Annotation指定
  2. 通过RestAdapter生成一个接口的实现类(动态代理)
  3. 调用接口请求数据

涉及重要知识点

  • 动态代理
  • 注解
  • Handler + MessageQueue + Thread + Looper机制
  • OKHttp请求网络
  • GSON用来转换Java POJO和解析JSON

使用技术

  • Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求
  • Retrofit的功能非常多的依赖Java反射,代码中其实还有很多细节,比如异常的捕获、抛出和处理,大量的Factory设计模式(为什么要这么多使用Factory模式?)
  • Retrofit中接口设计的恰到好处,在你创建 Retrofit 对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的 Converter 、使用不同的 CallAdapter ,这也就提供了你使用RxJava来调用Retrofit的可能

http://www.jb51.net/article/74747.htm

http://www.kwstu.com/ArticleView/kwstu_20144118313429

http://www.cnblogs.com/angeldevil/p/3729808.html

http://www.2cto.com/kf/201408/326552.html

http://www.cnblogs.com/lichenwei/p/4651576.html

http://www.bkjia.com/Androidjc/995850.html

http://www.tuicool.com/articles/26jUZjv

http://www.tuicool.com/articles/mQBZFjR

http://blog.csdn.net/laoyang360/article/details/7956611