如何独立开发一个网络请求框架

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

前言:

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

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

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

  作为一个合格的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             /** \r\n 是换行 */
103             body.writeBytes(twoHyphens+boundary+"\r\n"); /** 先写分隔符,标志和上面的头分开 */
104             body.writeBytes(
105                     "Content-Disposition:form-data;" +
106                             "name=\"" + streamName + "\";" +
107                             "filename=\"" + picName + "\"" + "\r\n"
108             );
109             /** 写文本数据体 */
110             body.writeBytes("\r\n");
111             if(keys!=null && values!=null){
112                 body.writeBytes(twoHyphens+boundary+"\r\n");
113                 body.writeBytes("Content-Disposition:form-data;");
114                 commonCombinePostText(keys,values,body);
115                 body.writeBytes("\r\n");
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("\r\n");
142             body.writeBytes(twoHyphens + boundary + twoHyphens +"\r\n");
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             /** \r\n 是换行 */
421             body.writeBytes(twoHyphens+boundary+"\r\n"); /** 先写分隔符,标志和上面的头分开 */
422             body.writeBytes(
423                     "Content-Disposition:form-data;" +
424                             "name=\"" + streamName + "\";" +
425                             "filename=\"" + picName + "\"" + "\r\n"
426             );
427             /** 写文本数据体 */
428             body.writeBytes("\r\n");
429             if(keys!=null && values!=null){
430                 body.writeBytes(twoHyphens+boundary+"\r\n");
431                 body.writeBytes("Content-Disposition:form-data;");
432                 commonCombinePostText(keys,values,body);
433                 body.writeBytes("\r\n");
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("\r\n");
460             body.writeBytes(twoHyphens + boundary + twoHyphens +"\r\n");
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 }

 

如果您认为这篇文章还不错或者有所收获,您可以通过扫描一下下面的支付宝二维码 打赏我一杯咖啡【物质支持】,也可以点击右下角的【推荐】按钮【精神支持】,因为这两种支持都是我继续写作,分享的最大动力

《如何独立开发一个网络请求框架》

    原文作者:HTTP
    原文地址: https://juejin.im/entry/5899352cb123db16a3acf731
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞