(原创出处为本博客:http://www.cnblogs.com/linguanh/)
前言
准备工作
开发模式
开发原则
线程
高并发
TCP/UDP
本类介绍
开发选择
功能列表
优点
拓展
完整代码
用法例子
前言:
已开源到 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 }