zoukankan      html  css  js  c++  java
  • 如何独立开发一个网络请求框架

    (原创出处为本博客:http://www.cnblogs.com/linguanh/)

    目录:

        前言

      准备工作

         开发模式

        开发原则

        线程

        高并发

        TCP/UDP

      本类介绍

         开发选择

        功能列表

        优点

        拓展

      完整代码

       用法例子

         

    前言:

      已开源到GitHub,希望大家能够和我一起来完善它,该类肯定有很多不足的,但总体来说,还是不错的。

      为什么要写这个东西?原因如下:

      在此之前已经有很多不错的网络请求框架广为人知了,例如 android-async-http,okHttp等,这类优秀的框架其底层的实现大部分也是基于系统的 线程池 和 httpClient 或 HttpUrlConnection,其中OK是自己解析http协议的,我相信很多开发者在使用这些框架的时候自己是没有去了解这些东西的。

      作为一个合格的Android开发者,我觉得必须要会自己使用系统的SDK提供的API来实现功能,并不是不建议使用框架,只是建议在能够在独立地,不依赖百度,完全断网的情况下去使用现有的资源来实现需求功能后,再去使用这些框架,这样不至于成为“框架工程师”,而且,在面试的时候,也能信心十足!

      本文不仅仅是对Java/Android而言,同样的建议可以折射到其他语言的开发上。

      

    准备工作

      1,了解并使用一些常用的开发模式,例如单例、观察者、简单工厂等。要开发框架,使用开发模式的必须的,无论是从代码解耦或者是内存优化方面上去看都是必不可少,前者有ImageLoader采用了双从判断类型的单例模式,包括Android我们熟悉的事件机制,它是观察者模式,还有适配器模式。

      2,了解并使用一些开发原则,注意哦,这里是原则哦,例如接口分离,一个大的项目没几个接口怎么行,而且接口能够我们带来很大的方便。

      3,了解并学会使用线程以及优化,网络请求类框架,我们知道Android中是不能在主线程中(又称UI线程)进行网络操作的,那么我们的框架中必不可少地要使用到子线程,这就需要你掌握线程类知识,例如简单的 Thread + Runnable + Handler 组合拳,再重量级点的就使用 AsyncTask。

      4,处理好高并发,一个应用中往往要进行多线程操作,而Java虚拟机对于一个线程的内存分配大约在1M左右,具体多少要看它执行的任务而定。看到线程高并发这些字,第一想到的应该是 线程池,所以线程池你需要去了解并学会灵活使用。

      5,了解 Tcp/Udp 数据包的结构以及 Post 的方式差别。还记得有很多招聘条件里面有很多要求要了解 TCP协议的要求不?如果你看到这样的要求的时候,心里默念这个要求有什么用的话,那就证明你还没真正地去使用过它,这点等下留意下我的代码注释。

      6,其他 (例如一个编译器....)

    本类介绍

      还是不要说框架了,说类吧,哈哈,不要那么高大上。接着在上面的准备工作中谈到的点,下面我来意义对应解释,它们是怎样被用到这个 Http工具类里面的。

      1,在开发模式上,我采用了静态内部类的单例模式,这样有两个好处,一个是保证了类不使用的时候不会想饿汉模式一样在开始就占用内存,而是懒汉模式,而静态内部类则是为了解决线程安全的问题,它仅会在使用的时候在JVM虚拟机类的加载第五步时初始化,此步骤中的clinit<>是线程安全的,同时又不会因为使用同步关键字 synchronized 而导致其他的问题,同步失败或耗资源等。 

    public static LghHttp getInstance(){
          return LghHttpStatic.singleLghHttp;
    }
    
    private static class LghHttpStatic{
          private static LghHttp singleLghHttp = new LghHttp();
    }

      2,在开发原则上面,采用了接口分离,主要体现在数据处理方面。

        /** 接口分离 */
        private interface LghHttpBaseListenr{
            void onFailed(int type);
    //        void onUrlFailed();
    //        void onTimeOut();
    //        void onProtocolFailed();
    //        void onEncodingFailed();
    //        void onIoFailed();
        }
    
        /** 全局有 requestCode 区分 */
        public interface LghHttpGlobleListener extends LghHttpBaseListenr{
            void onSuccess(int requestCode,String response);
        }
    
        /** 单一的没 requestCode 区分 */
        public interface LghHttpSingleListener extends LghHttpBaseListenr{
            void onSuccess(String response);
        }

      3,线程和并发方面,采用了newFixdThreadPool 类型的线程池,如果要考虑替换,建议换为 newCacheThreadPool,他们两个之间 newFixdThreadPool 可以控制并发数量,且在整个APP运行过程中有几个常驻线程在,避免使用时反复地new,退出时再销毁,而 newCacheThreadPool 则会在任务完成后,自动回收线程,它会帮你释放线程内存,也就不会有常驻线程。

     1 /** 初始化函数 */
     2     public synchronized void init(){
     3         this.threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
     4         this.handler = new Handler(){
     5             @Override
     6             public void handleMessage(Message msg) {
     7                 super.handleMessage(msg);
     8 
     9                 HttpDataBean bean = (HttpDataBean) msg.obj;
    10                 LghHttpBaseListenr tempListener;
    11                 if(GloblelghHttpListeners!=null){ /** 以全局的优先 */
    12                     tempListener = GloblelghHttpListeners;
    13                 }else if(bean.getListeners()!=null){
    14                     tempListener = bean.getListeners();
    15                 }else{
    16                     return;
    17                 }
    18                 switch (msg.what){
    19                     case Success:
    20                         if(GloblelghHttpListeners!=null){ /** 以全局的优先 */
    21                             GloblelghHttpListeners.onSuccess(msg.arg1,bean.getResponse());
    22                         }else{
    23                             bean.getListeners().onSuccess(bean.getResponse());
    24                         }
    25                         break;
    26                     case UrlFailed:
    27                         tempListener.onFailed(UrlFailed);
    28                         break;
    29                     case TimeOut:
    30                         tempListener.onFailed(TimeOut);
    31                         break;
    32                     case ProtocolFailed:
    33                         tempListener.onFailed(ProtocolFailed);
    34                         break;
    35                     case EncodingFailed:
    36                         tempListener.onFailed(EncodingFailed);
    37                         break;
    38                     case IOFailed:
    39                         tempListener.onFailed(IOFailed);
    40                         break;
    41                     default:
    42                         /** 这里不可能会进入,也当作一个留给你自己的接口吧 */
    43                         break;
    44                 }
    45             }
    46         };
    47     }
    View Code

      4,功能方面,提供了三种常见操作:

        1)Get 请求操作    

     1     /** Get 请求整合 */
     2     public void doGet(final String url){
     3         doGet(-1, url, null);
     4     }
     5 
     6     public void doGet(final int requestCode,final String url){
     7         doGet(requestCode, url, null);
     8     }
     9 
    10     public void doGet(
    11             final int requestCode,
    12             final String url,
    13             final LghHttpSingleListener lghHttpListeners)
    14     {
    15         Runnable runnable = new Runnable() {
    16             @Override
    17             public void run() {
    18                 get(requestCode, url, lghHttpListeners);
    19             }
    20         };
    21         if(threadPool != null){
    22             threadPool.execute(runnable);
    23         }else{
    24             Log.d(TAG,"do get threadPool is null");
    25         }
    26     }
    27 
    28     private void get(int requestCode,String url,LghHttpSingleListener lghHttpListener){
    29         try {
    30             HttpURLConnection httpURLConnection = getHttpUrlConnection(url,"GET");
    31             httpURLConnection.setUseCaches(false);
    32             sendMessage(Success,requestCode, commonGetResult(httpURLConnection,lghHttpListener));
    33         } catch (MalformedURLException e) {
    34             dealWithException(e,lghHttpListener);
    35         } catch (IOException e) {
    36             dealWithException(e,lghHttpListener);
    37         }
    38     }
    View Code

        2)Post 请求操作

     1 /** Post 请求整合 */
     2     public void doPost(String url){
     3         doPost(-1, url);
     4     }
     5 
     6     public void doPost(int requestCode,String url){
     7         doPost(requestCode, url, null, null);
     8     }
     9 
    10     public void doPost(int requestCode,String url,LghHttpSingleListener listener){
    11         doPost(requestCode, url, null, null,listener);
    12     }
    13 
    14     public void doPost(int requestCode,String url,String[] keys,String[] values){
    15         doPost(requestCode, url, keys, values, null);
    16     }
    17 
    18     public void doPost(
    19             final int requestCode,
    20             final String url,
    21             final String[] keys,
    22             final String[] values,
    23             final LghHttpSingleListener listener
    24     ){
    25         Runnable runnable = new Runnable() {
    26             @Override
    27             public void run() {
    28                 post(requestCode, url,keys,values, listener);
    29             }
    30         };
    31         if(threadPool != null){
    32             threadPool.execute(runnable);
    33         }else{
    34             Log.d(TAG,"do post threadPool is null");
    35         }
    36     }
    37 
    38     /** 采用第一种post协议,application/x-www-form-urlencoded */
    39     private void post(
    40             int requestCode,
    41             String url,
    42             String[] keys,
    43             String[] values,
    44             LghHttpSingleListener listener
    45     ){
    46         if(url==null){
    47             return;
    48         }
    49         try{
    50             HttpURLConnection httpURLConnection = getHttpUrlConnection(url,"POST");
    51             httpURLConnection.setDoOutput(true); /** post 必不可少 */
    52             httpURLConnection.setUseCaches(false);
    53 
    54             if(keys!=null && values!=null){
    55                 OutputStream outputStream = httpURLConnection.getOutputStream();
    56                 commonCombinePostText(keys,values,outputStream);
    57                 outputStream.flush();
    58                 outputStream.close();
    59             }
    60             sendMessage(Success,requestCode, commonGetResult(httpURLConnection,listener));
    61         }catch (MalformedURLException e){
    62             dealWithException(e,listener);
    63         } catch (SocketTimeoutException e){
    64             dealWithException(e,listener);
    65         } catch (ProtocolException e) {
    66             dealWithException(e,listener);
    67         } catch (UnsupportedEncodingException e) {
    68             dealWithException(e,listener);
    69         } catch (IOException e) {
    70             dealWithException(e,listener);
    71         }
    72     }
    View Code

        3)上传图片操作,Tcp 数据包的一些知识,在这里有用到。

      1 /** 上传图片部分整合 */
      2     public void doUpLoadPic(
      3             String url,
      4             String picName,
      5             String streamName,
      6             Bitmap bit
      7     ){
      8         doUpLoadPic(-1, url, null, null, picName, streamName, bit, null);
      9     }
     10 
     11     public void doUpLoadPic(
     12             int requestCode,
     13             String url,
     14             String picName,
     15             String streamName,
     16             Bitmap bit
     17     ){
     18         doUpLoadPic(requestCode, url, null, null, picName, streamName, bit, null);
     19     }
     20 
     21     public void doUpLoadPic(
     22             int requestCode,
     23             String url,
     24             String picName,
     25             String streamName,
     26             Bitmap bit,
     27             LghHttpSingleListener listener
     28     ){
     29         doUpLoadPic(requestCode, url, null, null, picName, streamName, bit, listener);
     30     }
     31 
     32     public void doUpLoadPic(
     33             int requestCode,
     34             String url,
     35             String[] keys,
     36             String[] values,
     37             String picName,
     38             String streamName,
     39             Bitmap bit
     40     ){
     41         doUpLoadPic(requestCode, url, keys, values, picName, streamName, bit, null);
     42     }
     43 
     44     public void doUpLoadPic(
     45             final int requestCode,
     46             final String url,
     47             final String[] keys,
     48             final String[] values,
     49             final String picName,
     50             final String streamName,
     51             final Bitmap bit,
     52             final LghHttpSingleListener listener
     53     ){
     54         Runnable runnable = new Runnable() {
     55             @Override
     56             public void run() {
     57                 UpLoadPic(requestCode, url, keys, values, picName, streamName, bit, listener);
     58             }
     59         };
     60         if(threadPool != null){
     61             threadPool.execute(runnable);
     62         }else{
     63             Log.d(TAG,"do post threadPool is null");
     64         }
     65     }
     66 
     67     /**
     68      * 此函数用来上传图片
     69      * post 的 两种数据包格式:
     70      * 1,application/x-www-form-urlencoded;用来上传文字
     71      * 2,multipart/form-data; 二进制传输,除了文字之外,还可以用来传输 文件,例如图片!
     72      * 3,multipart/form-data; 必须要带有分隔符 boundary
     73      * 4,在http post请求的结尾,需要有一个分界线,但是是前后都有--的:--分隔符--
     74      * 参数:
     75      *      url
     76      *      picName    图片的名称
     77      *      streamName 流体值的名称
     78      *      例如采用 php 接收,那么在服务器获取图片名称的写法是:$_FILES['streamName']['picName']
     79      **/
     80     private void UpLoadPic(
     81             int requestCode,
     82             String url,
     83             String[] keys,
     84             String[] values,
     85             String picName,
     86             String streamName,
     87             Bitmap bit,
     88             LghHttpSingleListener listener
     89     ){
     90         String twoHyphens = "--";   /** 一定要是 2行 */
     91         String boundary = "******"; /** 数据包分割线可以自定义 */
     92         try{
     93             HttpURLConnection httpURLConnection = getHttpUrlConnection(url,"POST");
     94             httpURLConnection.setUseCaches(false);
     95             httpURLConnection.setDoOutput(true);
     96             httpURLConnection.setChunkedStreamingMode(1024 * 256); /** 一次传输的块大小 */
     97             /** 数据 --------包头-------- 格式组装 */
     98             httpURLConnection.setRequestProperty("Connection","Keep-Alive");
     99             httpURLConnection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
    100             /** 数据 --------包体-------- 格式组装*/
    101             DataOutputStream body = new DataOutputStream(httpURLConnection.getOutputStream());
    102             /** 
     是换行 */
    103             body.writeBytes(twoHyphens+boundary+"
    "); /** 先写分隔符,标志和上面的头分开 */
    104             body.writeBytes(
    105                     "Content-Disposition:form-data;" +
    106                             "name="" + streamName + "";" +
    107                             "filename="" + picName + """ + "
    "
    108             );
    109             /** 写文本数据体 */
    110             body.writeBytes("
    ");
    111             if(keys!=null && values!=null){
    112                 body.writeBytes(twoHyphens+boundary+"
    ");
    113                 body.writeBytes("Content-Disposition:form-data;");
    114                 commonCombinePostText(keys,values,body);
    115                 body.writeBytes("
    ");
    116             }
    117             /** -------下面开始写图片二进制------- */
    118             /** 下面是先压缩 */
    119             int compress = 100;
    120             ByteArrayOutputStream baos = new ByteArrayOutputStream();
    121             bit.compress(Bitmap.CompressFormat.JPEG, compress, baos);
    122             if(IsOpenCompress){
    123                 while (baos.toByteArray().length / 1024 > CompressLimit) {
    124                     baos.reset();
    125                     compress -= 10;
    126                     if(compress==0){
    127                         bit.compress(Bitmap.CompressFormat.JPEG, compress, baos);
    128                         break;
    129                     }
    130                     bit.compress(Bitmap.CompressFormat.JPEG, compress, baos);
    131                 }
    132             }
    133             /** 开始写 */
    134             InputStream picStream = new ByteArrayInputStream(baos.toByteArray());
    135             byte[] buffer = new byte[10*1024];
    136             int count;
    137             while((count = picStream.read(buffer))!=-1){
    138                 body.write(buffer,0,count);
    139             }
    140             picStream.close();
    141             body.writeBytes("
    ");
    142             body.writeBytes(twoHyphens + boundary + twoHyphens +"
    ");
    143             body.flush();
    144             /** 写完 */
    145             sendMessage(Success,requestCode,commonGetResult(httpURLConnection,listener));
    146             body.close();
    147         }catch (MalformedURLException e){
    148             dealWithException(e,listener);
    149         } catch (SocketTimeoutException e){
    150             dealWithException(e,listener);
    151         } catch (ProtocolException e) {
    152             dealWithException(e,listener);
    153         } catch (UnsupportedEncodingException e) {
    154             dealWithException(e,listener);
    155         } catch (IOException e) {
    156             dealWithException(e,listener);
    157         }
    158     }
    View Code

      5,优点:

        1)绝对的轻量级,可以提升APK 的体积优化,没依赖其他第三方库

        2)内存管理方面可以放心

        3)请求速度方面是纯系统的 HttpUrlConnection,没有过多的代码片段

      6,可以进一步解耦拆分类,为了方便我自己使用所以我写在了一个里面,可以分为:

        1)公共部分

     1     /** 公共部分,异常集合处理 */
     2     private void dealWithException(
     3             Exception e,
     4             LghHttpSingleListener lghHttpListeners)
     5     {
     6         HttpDataBean bean = new HttpDataBean();
     7         bean.setListeners(lghHttpListeners);
     8         if(e instanceof MalformedURLException){
     9             Log.d(TAG, "链接格式有问题 "+e.toString());
    10             sendMessage(UrlFailed,bean);
    11         }else if(e instanceof SocketTimeoutException){
    12             Log.d(TAG, "连接超时 "+e.toString());
    13             sendMessage(TimeOut,bean);
    14         }else if(e instanceof ProtocolException){
    15             Log.d(TAG, "协议异常,注意不要多次连接 " + e.toString());
    16             sendMessage(ProtocolFailed, bean);
    17         }else if(e instanceof UnsupportedEncodingException){
    18             Log.d(TAG, "编码类型异常 " + e.toString());
    19             sendMessage(EncodingFailed, bean);
    20         }else if(e instanceof IOException){
    21             Log.d(TAG, "io 异常 " + e.toString());
    22             sendMessage(IOFailed,bean);
    23         }
    24     }
    25 
    26     /** 获取一个HttpUrlConnection,合并一些公共部分 */
    27     private static HttpURLConnection getHttpUrlConnection
    28             (String url,String requestWay) throws IOException {
    29         Log.d(TAG,"url is "+url);
    30         URL mRrl = new URL(url);
    31         HttpURLConnection httpURLConnection = (HttpURLConnection) mRrl.openConnection();
    32         httpURLConnection.setRequestMethod(requestWay);
    33         httpURLConnection.setRequestProperty("Charset", "UTF-8");
    34         httpURLConnection.setConnectTimeout(5 * 1000);
    35         return httpURLConnection;
    36     }
    37 
    38     /** 获取结果公共部分 */
    39     private HttpDataBean commonGetResult(
    40             HttpURLConnection httpURLConnection,
    41             LghHttpSingleListener listener
    42     ) throws IOException {
    43         if(httpURLConnection==null){
    44             return null;
    45         }
    46         BufferedReader br = new BufferedReader
    47                 (
    48                         new InputStreamReader(httpURLConnection.getInputStream(),"UTF-8"),
    49                         8*1024
    50                 );
    51         StringBuffer resultBuffer = new StringBuffer("");
    52         String line;
    53         while ((line = br.readLine())!=null){
    54             resultBuffer.append(line);
    55         }
    56         HttpDataBean bean = new HttpDataBean();
    57         bean.setResponse(resultBuffer.toString());
    58         bean.setListeners(listener);
    59         br.close();
    60         return bean;
    61     }
    62 
    63     /** 组合 post 文本数据公共部分 */
    64     private OutputStream commonCombinePostText(
    65             String[] keys,
    66             String[] values,
    67             OutputStream outputStream) throws IOException
    68     {
    69         StringBuffer requestStr = new StringBuffer();
    70         int keysLength = keys.length;
    71         for(int i=0;i<keysLength;i++){
    72             requestStr.append(keys[i]+"="+values[i]+"&");
    73         }
    74         outputStream.write(requestStr.toString().getBytes());
    75         return outputStream;
    76     }
    View Code

        2)数据部分

     1 /**
     2      * LghHttp 基础数据类
     3      * 作为 handler 传递的数据种子,只在成功时传递
     4      * */
     5     private class HttpDataBean implements Serializable{
     6 
     7         private String response;
     8         private LghHttpSingleListener listeners;
     9 
    10         public void setResponse(String response){
    11             this.response = response;
    12         }
    13 
    14         public void setListeners(LghHttpSingleListener listeners){
    15             this.listeners = listeners;
    16         }
    17 
    18         public String getResponse(){
    19             return this.response;
    20         }
    21 
    22         public LghHttpSingleListener getListeners(){
    23             return this.listeners;
    24         }
    25     }
    View Code

        3)请求核心部分

      7,拓展,加入视频上传部分。

    完整代码:

      上面嵌套讲解拆分了,这里提供完整的。

      1 package com.lghsaleimage;
      2 
      3 import android.graphics.Bitmap;
      4 import android.os.Handler;
      5 import android.os.Message;
      6 import android.util.Log;
      7 
      8 import java.io.BufferedReader;
      9 import java.io.ByteArrayInputStream;
     10 import java.io.ByteArrayOutputStream;
     11 import java.io.DataOutputStream;
     12 import java.io.IOException;
     13 import java.io.InputStream;
     14 import java.io.InputStreamReader;
     15 import java.io.OutputStream;
     16 import java.io.Serializable;
     17 import java.io.UnsupportedEncodingException;
     18 import java.net.HttpURLConnection;
     19 import java.net.MalformedURLException;
     20 import java.net.ProtocolException;
     21 import java.net.SocketTimeoutException;
     22 import java.net.URL;
     23 import java.util.concurrent.Executors;
     24 import java.util.concurrent.ThreadPoolExecutor;
     25 
     26 /**
     27  * Created by 林冠宏(指尖下的幽灵) on 2016/8/11.
     28  *
     29  * Blog : http://www.cnblogs.com/linguanh/;
     30  *
     31  * Name : http 工具类
     32  *
     33  * 前言:
     34  *     希望大家能够和我一起来完善它,该类肯定有很多不足的,但总体来说,还是不错的。
     35  *
     36  * 下面是简介和拓展:
     37  *
     38  *     1, 考虑到网络请求必不可少,采用了静态内部类单例模式
     39  *
     40  *     2, 采用 newFixedThreadPool 线程池来管理并发线程,
     41  *        如果要替换,建议使用 newCacheThreadPool
     42  *
     43  *     3, 功能方面提供三种常见操作:
     44  *           1)Get请求
     45  *           2)Post请求
     46  *           3)图片上传
     47  *     4, 优点:
     48  *           1) 绝对的轻量级,可以提升 APK 体积优化
     49  *           2)内存管理方面可以放心
     50  *           3)请求速度方法是纯系统的 HttpUrlConnection 请求,
     51  *              没有过多的代码片段
     52  *
     53  *     5,可以进一步解耦拆分类,分为:
     54  *           1)公共部分
     55  *           2)数据部分
     56  *           3)请求核心部分
     57  *
     58  *     6, 加入视频上传部分
     59  *
     60  */
     61 
     62 public class LghHttp {
     63 
     64     private final static String TAG = "zzzzz";
     65 
     66     public final static int Success = 0x10;
     67     public final static int UrlFailed = 0x11;
     68     public final static int TimeOut = 0x12;
     69     public final static int ProtocolFailed = 0x13;
     70     public final static int EncodingFailed = 0x14;
     71     public final static int IOFailed = 0x15;
     72 
     73     private final static boolean IsOpenCompress = true;/** 是否开启压缩 */
     74     private final static int CompressLimit = 500;      /** 压缩级别,单位是 K */
     75 
     76     private ThreadPoolExecutor threadPool;
     77     private Handler handler;
     78     /**
     79      * 全局回调接口 GloblelghHttpListeners
     80      * 注意:
     81      *     个人建议,如果请求页面多的,那就不要使用全局接口。尽量采用singleInterface
     82      *     否则,你需要在用户层页面的每次onResume重新设置
     83      * */
     84     private LghHttpGlobleListener GloblelghHttpListeners;
     85 
     86     public static LghHttp getInstance(){
     87         return LghHttpStatic.singleLghHttp;
     88     }
     89 
     90     private static class LghHttpStatic{
     91         private static LghHttp singleLghHttp = new LghHttp();
     92     }
     93 
     94     /** 销毁,内存释放善后操作 */
     95     public void destroy(){
     96         if(threadPool!=null){
     97             if(!threadPool.isShutdown()){
     98                 threadPool.shutdown();
     99                 threadPool = null;
    100             }
    101         }
    102         if(handler!=null){
    103             handler.removeCallbacksAndMessages(null);
    104             handler = null;
    105         }
    106         if(GloblelghHttpListeners!=null){
    107             GloblelghHttpListeners = null;
    108         }
    109         LghHttpStatic.singleLghHttp = null;
    110     }
    111 
    112     public void setGloblelghHttpListeners(LghHttpGlobleListener GloblelghHttpListeners){
    113         this.GloblelghHttpListeners = GloblelghHttpListeners;
    114     }
    115 
    116     /**
    117      * LghHttp 基础数据类
    118      * 作为 handler 传递的数据种子,只在成功时传递
    119      * */
    120     private class HttpDataBean implements Serializable{
    121 
    122         private String response;
    123         private LghHttpSingleListener listeners;
    124 
    125         public void setResponse(String response){
    126             this.response = response;
    127         }
    128 
    129         public void setListeners(LghHttpSingleListener listeners){
    130             this.listeners = listeners;
    131         }
    132 
    133         public String getResponse(){
    134             return this.response;
    135         }
    136 
    137         public LghHttpSingleListener getListeners(){
    138             return this.listeners;
    139         }
    140     }
    141 
    142     /** 初始化函数 */
    143     public synchronized void init(){
    144         this.threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
    145         this.handler = new Handler(){
    146             @Override
    147             public void handleMessage(Message msg) {
    148                 super.handleMessage(msg);
    149 
    150                 HttpDataBean bean = (HttpDataBean) msg.obj;
    151                 LghHttpBaseListenr tempListener;
    152                 if(GloblelghHttpListeners!=null){ /** 以全局的优先 */
    153                     tempListener = GloblelghHttpListeners;
    154                 }else if(bean.getListeners()!=null){
    155                     tempListener = bean.getListeners();
    156                 }else{
    157                     return;
    158                 }
    159                 switch (msg.what){
    160                     case Success:
    161                         if(GloblelghHttpListeners!=null){ /** 以全局的优先 */
    162                             GloblelghHttpListeners.onSuccess(msg.arg1,bean.getResponse());
    163                         }else{
    164                             bean.getListeners().onSuccess(bean.getResponse());
    165                         }
    166                         break;
    167                     case UrlFailed:
    168                         tempListener.onFailed(UrlFailed);
    169                         break;
    170                     case TimeOut:
    171                         tempListener.onFailed(TimeOut);
    172                         break;
    173                     case ProtocolFailed:
    174                         tempListener.onFailed(ProtocolFailed);
    175                         break;
    176                     case EncodingFailed:
    177                         tempListener.onFailed(EncodingFailed);
    178                         break;
    179                     case IOFailed:
    180                         tempListener.onFailed(IOFailed);
    181                         break;
    182                     default:
    183                         /** 这里不可能会进入,也当作一个留给你自己的接口吧 */
    184                         break;
    185                 }
    186             }
    187         };
    188     }
    189 
    190     /** handler 发消息部分整合 */
    191     private void sendMessage(int what,int code,Object object){
    192         Message msg = new Message();
    193         msg.what = what;
    194         msg.arg1 = code;
    195         msg.obj = object;
    196         handler.sendMessage(msg);
    197     }
    198 
    199     private void sendMessage(int what,Object object){
    200         sendMessage(what, -1, object);
    201     }
    202 
    203     /**
    204      * requestCode 请求标识符,方便区分
    205      * */
    206 
    207     /** Get 请求整合 */
    208     public void doGet(final String url){
    209         doGet(-1, url, null);
    210     }
    211 
    212     public void doGet(final int requestCode,final String url){
    213         doGet(requestCode, url, null);
    214     }
    215 
    216     public void doGet(
    217             final int requestCode,
    218             final String url,
    219             final LghHttpSingleListener lghHttpListeners)
    220     {
    221         Runnable runnable = new Runnable() {
    222             @Override
    223             public void run() {
    224                 get(requestCode, url, lghHttpListeners);
    225             }
    226         };
    227         if(threadPool != null){
    228             threadPool.execute(runnable);
    229         }else{
    230             Log.d(TAG,"do get threadPool is null");
    231         }
    232     }
    233 
    234     private void get(int requestCode,String url,LghHttpSingleListener lghHttpListener){
    235         try {
    236             HttpURLConnection httpURLConnection = getHttpUrlConnection(url,"GET");
    237             httpURLConnection.setUseCaches(false);
    238             sendMessage(Success,requestCode, commonGetResult(httpURLConnection,lghHttpListener));
    239         } catch (MalformedURLException e) {
    240             dealWithException(e,lghHttpListener);
    241         } catch (IOException e) {
    242             dealWithException(e,lghHttpListener);
    243         }
    244     }
    245 
    246     /** Post 请求整合 */
    247     public void doPost(String url){
    248         doPost(-1, url);
    249     }
    250 
    251     public void doPost(int requestCode,String url){
    252         doPost(requestCode, url, null, null);
    253     }
    254 
    255     public void doPost(int requestCode,String url,LghHttpSingleListener listener){
    256         doPost(requestCode, url, null, null,listener);
    257     }
    258 
    259     public void doPost(int requestCode,String url,String[] keys,String[] values){
    260         doPost(requestCode, url, keys, values, null);
    261     }
    262 
    263     public void doPost(
    264             final int requestCode,
    265             final String url,
    266             final String[] keys,
    267             final String[] values,
    268             final LghHttpSingleListener listener
    269     ){
    270         Runnable runnable = new Runnable() {
    271             @Override
    272             public void run() {
    273                 post(requestCode, url,keys,values, listener);
    274             }
    275         };
    276         if(threadPool != null){
    277             threadPool.execute(runnable);
    278         }else{
    279             Log.d(TAG,"do post threadPool is null");
    280         }
    281     }
    282 
    283     /** 采用第一种post协议,application/x-www-form-urlencoded */
    284     private void post(
    285             int requestCode,
    286             String url,
    287             String[] keys,
    288             String[] values,
    289             LghHttpSingleListener listener
    290     ){
    291         if(url==null){
    292             return;
    293         }
    294         try{
    295             HttpURLConnection httpURLConnection = getHttpUrlConnection(url,"POST");
    296             httpURLConnection.setDoOutput(true); /** post 必不可少 */
    297             httpURLConnection.setUseCaches(false);
    298 
    299             if(keys!=null && values!=null){
    300                 OutputStream outputStream = httpURLConnection.getOutputStream();
    301                 commonCombinePostText(keys,values,outputStream);
    302                 outputStream.flush();
    303                 outputStream.close();
    304             }
    305             sendMessage(Success,requestCode, commonGetResult(httpURLConnection,listener));
    306         }catch (MalformedURLException e){
    307             dealWithException(e,listener);
    308         } catch (SocketTimeoutException e){
    309             dealWithException(e,listener);
    310         } catch (ProtocolException e) {
    311             dealWithException(e,listener);
    312         } catch (UnsupportedEncodingException e) {
    313             dealWithException(e,listener);
    314         } catch (IOException e) {
    315             dealWithException(e,listener);
    316         }
    317     }
    318 
    319     /** 上传图片部分整合 */
    320     public void doUpLoadPic(
    321             String url,
    322             String picName,
    323             String streamName,
    324             Bitmap bit
    325     ){
    326         doUpLoadPic(-1, url, null, null, picName, streamName, bit, null);
    327     }
    328 
    329     public void doUpLoadPic(
    330             int requestCode,
    331             String url,
    332             String picName,
    333             String streamName,
    334             Bitmap bit
    335     ){
    336         doUpLoadPic(requestCode, url, null, null, picName, streamName, bit, null);
    337     }
    338 
    339     public void doUpLoadPic(
    340             int requestCode,
    341             String url,
    342             String picName,
    343             String streamName,
    344             Bitmap bit,
    345             LghHttpSingleListener listener
    346     ){
    347         doUpLoadPic(requestCode, url, null, null, picName, streamName, bit, listener);
    348     }
    349 
    350     public void doUpLoadPic(
    351             int requestCode,
    352             String url,
    353             String[] keys,
    354             String[] values,
    355             String picName,
    356             String streamName,
    357             Bitmap bit
    358     ){
    359         doUpLoadPic(requestCode, url, keys, values, picName, streamName, bit, null);
    360     }
    361 
    362     public void doUpLoadPic(
    363             final int requestCode,
    364             final String url,
    365             final String[] keys,
    366             final String[] values,
    367             final String picName,
    368             final String streamName,
    369             final Bitmap bit,
    370             final LghHttpSingleListener listener
    371     ){
    372         Runnable runnable = new Runnable() {
    373             @Override
    374             public void run() {
    375                 UpLoadPic(requestCode, url, keys, values, picName, streamName, bit, listener);
    376             }
    377         };
    378         if(threadPool != null){
    379             threadPool.execute(runnable);
    380         }else{
    381             Log.d(TAG,"do post threadPool is null");
    382         }
    383     }
    384 
    385     /**
    386      * 此函数用来上传图片
    387      * post 的 两种数据包格式:
    388      * 1,application/x-www-form-urlencoded;用来上传文字
    389      * 2,multipart/form-data; 二进制传输,除了文字之外,还可以用来传输 文件,例如图片!
    390      * 3,multipart/form-data; 必须要带有分隔符 boundary
    391      * 4,在http post请求的结尾,需要有一个分界线,但是是前后都有--的:--分隔符--
    392      * 参数:
    393      *      url
    394      *      picName    图片的名称
    395      *      streamName 流体值的名称
    396      *      例如采用 php 接收,那么在服务器获取图片名称的写法是:$_FILES['streamName']['picName']
    397      **/
    398     private void UpLoadPic(
    399             int requestCode,
    400             String url,
    401             String[] keys,
    402             String[] values,
    403             String picName,
    404             String streamName,
    405             Bitmap bit,
    406             LghHttpSingleListener listener
    407     ){
    408         String twoHyphens = "--";   /** 一定要是 2行 */
    409         String boundary = "******"; /** 数据包分割线可以自定义 */
    410         try{
    411             HttpURLConnection httpURLConnection = getHttpUrlConnection(url,"POST");
    412             httpURLConnection.setUseCaches(false);
    413             httpURLConnection.setDoOutput(true);
    414             httpURLConnection.setChunkedStreamingMode(1024 * 256); /** 一次传输的块大小 */
    415             /** 数据 --------包头-------- 格式组装 */
    416             httpURLConnection.setRequestProperty("Connection","Keep-Alive");
    417             httpURLConnection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
    418             /** 数据 --------包体-------- 格式组装*/
    419             DataOutputStream body = new DataOutputStream(httpURLConnection.getOutputStream());
    420             /** 
     是换行 */
    421             body.writeBytes(twoHyphens+boundary+"
    "); /** 先写分隔符,标志和上面的头分开 */
    422             body.writeBytes(
    423                     "Content-Disposition:form-data;" +
    424                             "name="" + streamName + "";" +
    425                             "filename="" + picName + """ + "
    "
    426             );
    427             /** 写文本数据体 */
    428             body.writeBytes("
    ");
    429             if(keys!=null && values!=null){
    430                 body.writeBytes(twoHyphens+boundary+"
    ");
    431                 body.writeBytes("Content-Disposition:form-data;");
    432                 commonCombinePostText(keys,values,body);
    433                 body.writeBytes("
    ");
    434             }
    435             /** -------下面开始写图片二进制------- */
    436             /** 下面是先压缩 */
    437             int compress = 100;
    438             ByteArrayOutputStream baos = new ByteArrayOutputStream();
    439             bit.compress(Bitmap.CompressFormat.JPEG, compress, baos);
    440             if(IsOpenCompress){
    441                 while (baos.toByteArray().length / 1024 > CompressLimit) {
    442                     baos.reset();
    443                     compress -= 10;
    444                     if(compress==0){
    445                         bit.compress(Bitmap.CompressFormat.JPEG, compress, baos);
    446                         break;
    447                     }
    448                     bit.compress(Bitmap.CompressFormat.JPEG, compress, baos);
    449                 }
    450             }
    451             /** 开始写 */
    452             InputStream picStream = new ByteArrayInputStream(baos.toByteArray());
    453             byte[] buffer = new byte[10*1024];
    454             int count;
    455             while((count = picStream.read(buffer))!=-1){
    456                 body.write(buffer,0,count);
    457             }
    458             picStream.close();
    459             body.writeBytes("
    ");
    460             body.writeBytes(twoHyphens + boundary + twoHyphens +"
    ");
    461             body.flush();
    462             /** 写完 */
    463             sendMessage(Success,requestCode,commonGetResult(httpURLConnection,listener));
    464             body.close();
    465         }catch (MalformedURLException e){
    466             dealWithException(e,listener);
    467         } catch (SocketTimeoutException e){
    468             dealWithException(e,listener);
    469         } catch (ProtocolException e) {
    470             dealWithException(e,listener);
    471         } catch (UnsupportedEncodingException e) {
    472             dealWithException(e,listener);
    473         } catch (IOException e) {
    474             dealWithException(e,listener);
    475         }
    476     }
    477 
    478     /** 公共部分,异常集合处理 */
    479     private void dealWithException(
    480             Exception e,
    481             LghHttpSingleListener lghHttpListeners)
    482     {
    483         HttpDataBean bean = new HttpDataBean();
    484         bean.setListeners(lghHttpListeners);
    485         if(e instanceof MalformedURLException){
    486             Log.d(TAG, "链接格式有问题 "+e.toString());
    487             sendMessage(UrlFailed,bean);
    488         }else if(e instanceof SocketTimeoutException){
    489             Log.d(TAG, "连接超时 "+e.toString());
    490             sendMessage(TimeOut,bean);
    491         }else if(e instanceof ProtocolException){
    492             Log.d(TAG, "协议异常,注意不要多次连接 " + e.toString());
    493             sendMessage(ProtocolFailed, bean);
    494         }else if(e instanceof UnsupportedEncodingException){
    495             Log.d(TAG, "编码类型异常 " + e.toString());
    496             sendMessage(EncodingFailed, bean);
    497         }else if(e instanceof IOException){
    498             Log.d(TAG, "io 异常 " + e.toString());
    499             sendMessage(IOFailed,bean);
    500         }
    501     }
    502 
    503     /** 获取一个HttpUrlConnection,合并一些公共部分 */
    504     private static HttpURLConnection getHttpUrlConnection
    505             (String url,String requestWay) throws IOException {
    506         Log.d(TAG,"url is "+url);
    507         URL mRrl = new URL(url);
    508         HttpURLConnection httpURLConnection = (HttpURLConnection) mRrl.openConnection();
    509         httpURLConnection.setRequestMethod(requestWay);
    510         httpURLConnection.setRequestProperty("Charset", "UTF-8");
    511         httpURLConnection.setConnectTimeout(5 * 1000);
    512         return httpURLConnection;
    513     }
    514 
    515     /** 获取结果公共部分 */
    516     private HttpDataBean commonGetResult(
    517             HttpURLConnection httpURLConnection,
    518             LghHttpSingleListener listener
    519     ) throws IOException {
    520         if(httpURLConnection==null){
    521             return null;
    522         }
    523         BufferedReader br = new BufferedReader
    524                 (
    525                         new InputStreamReader(httpURLConnection.getInputStream(),"UTF-8"),
    526                         8*1024
    527                 );
    528         StringBuffer resultBuffer = new StringBuffer("");
    529         String line;
    530         while ((line = br.readLine())!=null){
    531             resultBuffer.append(line);
    532         }
    533         HttpDataBean bean = new HttpDataBean();
    534         bean.setResponse(resultBuffer.toString());
    535         bean.setListeners(listener);
    536         br.close();
    537         return bean;
    538     }
    539 
    540     /** 组合 post 文本数据公共部分 */
    541     private OutputStream commonCombinePostText(
    542             String[] keys,
    543             String[] values,
    544             OutputStream outputStream) throws IOException
    545     {
    546         StringBuffer requestStr = new StringBuffer();
    547         int keysLength = keys.length;
    548         for(int i=0;i<keysLength;i++){
    549             requestStr.append(keys[i]+"="+values[i]+"&");
    550         }
    551         outputStream.write(requestStr.toString().getBytes());
    552         return outputStream;
    553     }
    554 
    555     /** 接口分离 */
    556     private interface LghHttpBaseListenr{
    557         void onFailed(int type);
    558 //        void onUrlFailed();
    559 //        void onTimeOut();
    560 //        void onProtocolFailed();
    561 //        void onEncodingFailed();
    562 //        void onIoFailed();
    563     }
    564 
    565     /** 全局有 requestCode 区分 */
    566     public interface LghHttpGlobleListener extends LghHttpBaseListenr{
    567         void onSuccess(int requestCode,String response);
    568     }
    569 
    570     /** 单一的没 requestCode 区分 */
    571     public interface LghHttpSingleListener extends LghHttpBaseListenr{
    572         void onSuccess(String response);
    573     }
    574 
    575 }

    用法例子

      1 package lgh.httpdemo;
      2 
      3 import android.graphics.BitmapFactory;
      4 import android.os.Bundle;
      5 import android.support.v7.app.AppCompatActivity;
      6 import android.util.Log;
      7 import android.widget.Toast;
      8 
      9 public class MainActivity extends AppCompatActivity {
     10 
     11     private LghHttp lghHttp = LghHttp.getInstance();
     12 
     13     @Override
     14     protected void onCreate(Bundle savedInstanceState) {
     15         super.onCreate(savedInstanceState);
     16         setContentView(R.layout.activity_main);
     17 
     18         /** Example 1 */
     19         /** 使用全局接口并发起 post 操作 */
     20         lghHttp.setGloblelghHttpListeners(new LghHttp.LghHttpGlobleListener() {
     21             @Override
     22             public void onFailed(int type) {
     23                 switch (type) {
     24                     case LghHttp.UrlFailed:
     25 
     26                         break;
     27                     case LghHttp.Success:
     28 
     29                         break;
     30                     case LghHttp.TimeOut:
     31 
     32                         break;
     33                     /**  .... */
     34                 }
     35             }
     36 
     37             @Override
     38             public void onSuccess(int requestCode, String response) {
     39                 Log.d("zzzzz", "LghHttpGlobleListener do post response "
     40                         + response + " requestCode is " + requestCode);
     41                 switch (requestCode) {
     42                     case 123:
     43                         /** 对应操作 */
     44                         break;
     45                 }
     46             }
     47 
     48         });
     49         for(int i=0;i<30;i++){
     50             final int j = i;
     51             lghHttp.doPost
     52                     (
     53                             j,
     54                             "http://121.42.190.18/ydnurse/Controller/noteController.php?func=GetNote",
     55                             null
     56                     );
     57         }
     58         // 带有键值
     59         lghHttp.doPost
     60                 (
     61                         145,
     62                         "http://121.42.190.18/ydnurse/Controller/noteController.php?func=GetNote",
     63                         new String[]{"userName","userAge","userSex"},
     64                         new String[]{"林冠宏","21","Box"},
     65                         null
     66                 );
     67         
     68         /** -----测试下面的例子,要把全局的接口 LghHttpGlobleListener 设置为 NUll----- */
     69         /** Example 2 */
     70         lghHttp.doGet
     71                 (
     72                         123,
     73                         "http://121.42.190.18/ydnurse/Controller/noteController.php?func=GetNote",
     74                         new LghHttp.LghHttpSingleListener() {
     75                             @Override
     76                             public void onSuccess(String response) {
     77 
     78                             }
     79 
     80                             @Override
     81                             public void onFailed(int type) {
     82 
     83                             }
     84                         }
     85                 );
     86         /** UpPic Example 3 */
     87 
     88         lghHttp.doUpLoadPic(
     89                 "http://www.xiangjiaoyun.com:8888/BCapp/BananaCloudServer/userPicUploadFile.php?" +
     90                         "account=13726204215&postid=0&type=2",
     91                 "123.jpg",
     92                 "uploadedfile",
     93                 BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)
     94         );
     95 
     96         lghHttp.doUpLoadPic(
     97                 1456,
     98                 "http://www.xiangjiaoyun.com:8888/BCapp/BananaCloudServer/userPicUploadFile.php?" +
     99                         "account=13726204215&postid=0&type=2",
    100                 "123.jpg",
    101                 "uploadedfile",
    102                 BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher),
    103                 new LghHttp.LghHttpSingleListener() {
    104                     @Override
    105                     public void onSuccess(String response) {
    106                         Toast.makeText(MainActivity.this,"上传图片成功",Toast.LENGTH_SHORT).show();
    107                     }
    108 
    109                     @Override
    110                     public void onFailed(int type) {
    111 
    112                     }
    113                 }
    114         );
    115     }
    116 }
  • 相关阅读:
    jquery插件layer
    获取订单的product_id 和订单的数量
    Python psutil模块
    Linuc bazaar命令
    分布式版本控制系统
    launchpad, jira, github
    C/C++ 经典面试题汇总
    Windows Cmder
    Reddit指南
    Linux xclip命令
  • 原文地址:https://www.cnblogs.com/linguanh/p/5767835.html
Copyright © 2011-2022 走看看