Android网络请求类(使用HttpURLConnection

2018-03-04  本文已影响0人  虚假雨

这周导师要求我不用框架写一个网络请求类。平时都是习惯拿来主义,突然自己写发现其中还是有很多东西的。

要求:写一个网络请求的工具类,支持GET、POST,可满足日常请求网络,提交表单,下载文件这些的使用。不使用框架。

一、GET

这个应该是最简单的,很多的api请求也是直接采用的get的形式。实现如下:

 /**
     * get请求
     * @param url url
     * */
    public static  String get(String url) {
        HttpURLConnection conn = null;
        try {
            // 利用string url构建URL对象S
            URL mURL = new URL(url);
            conn = (HttpURLConnection) mURL.openConnection();
            conn.setRequestMethod("GET");
            conn.setReadTimeout(readTimeout);
            conn.setConnectTimeout(connectTimeout);
            int responseCode = conn.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                InputStream is = conn.getInputStream();
                return getStringFromInputStream(is);
            } else {
                Log.d(TAG,"response status is "+responseCode);
            }
            return "";
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }
 /**
     * 从InputStream中获取内容
     * @param is 输入流
     * @return  InputStream中内容
     * */
    private static String getStringFromInputStream(InputStream is) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len ;
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        is.close();
        String state = os.toString();
        os.close();
        return state;
    }

HttpURLConnection中支持很多配置参数,在下面的例子中我们会用到更多,参考网上的例子,在get这一块都比较简洁,我也就这么写了。

小细节:

  1. HttpURLConnection.HTTP_OK = 200,直接写200的话不是很直观。

  2. 注意在finally中断开连接而不是在try中,确保最终有执行。

二、post

这一块稍微多一点,如下:

 /**
 * post请求,同步操作
 * @param urlPath url
 * @param content 请求内容
 * */
 public static String post(String urlPath , String content) {
 HttpURLConnection conn = null;
 try {
 // 创建一个URL对象
 URL url = new URL(urlPath);
 // 调用URL的openConnection()方法,获取HttpURLConnection对象
 conn = (HttpURLConnection) url.openConnection();
 conn.setRequestMethod("POST");// 设置请求方法为post
 conn.setReadTimeout(readTimeout);
 conn.setConnectTimeout(connectTimeout);
 //下面两行为post必须
 conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容
 conn.setDoInput(true);
 // 获得一个输出流,向服务器写数据,默认情况下,系统不允许向服务器输出内容
 OutputStream out = conn.getOutputStream();// 获得一个输出流,向服务器写数据
 out.write(content.getBytes());
 out.flush();
 out.close();
 int responseCode = conn.getResponseCode();
 //正确返回
 if (responseCode == HttpURLConnection.HTTP_OK) {
 InputStream is = conn.getInputStream();
 return getStringFromInputStream(is);
 } else {//状态错误
 throw new NetworkErrorException("response status is "+responseCode);
 }
​
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 if (conn != null) {
 conn.disconnect();// 关闭连接
 }
 }
 return null;
 }

与get对比,可以看到多了设置input output的设置,是因为默认服务器是不允许向服务器输出内容的。不打开这个开关你的post如何传递参数呢是吧。

三、下载文件

/**
 * 下载文件,同步
 *
 * @param url 文件下载路径
 * @param filePath 下载文件保存路劲
 * @param name 下载文件保存名称
 *@param listener 下载监听器
 * @return 下载成功返回0,否则返回1
 * *///测试通过
 public static int  downloadFile(String url,String filePath,String name,DownloadListener listener) throws IOException {
 HttpURLConnection httpURLConnection = null;
 FileOutputStream fos = null;
 InputStream inputStream = null;
 try {
 // 利用string url构建URL对象
 URL mURL = new URL(url);
 httpURLConnection = (HttpURLConnection) mURL.openConnection();
 httpURLConnection.setConnectTimeout(connectTimeout);
 int contentLength = httpURLConnection.getContentLength();
 //得到输入流
 inputStream = httpURLConnection.getInputStream();
 //获取字节数组
 write2SDFromInput(filePath,name,inputStream,contentLength,listener);
 //文件保存位置
 return 1;
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 if (httpURLConnection != null) {
 httpURLConnection.disconnect();
 }
 if (fos != null) {
 fos.close();
 }
 if (inputStream != null) {
 inputStream.close();
 }
 }
 return 0;
 }
​
 /**
 * 从下载输入流中读取文件,实时更新进度到listener中
 * @param listener 下载监听器
 * @param path 文件存放位置
 * @param contentLength 总下载大小
 * @param fileName 存放文件名字
 * */
 private static void write2SDFromInput(String path ,String fileName,InputStream input,int contentLength,DownloadListener listener) {
 File file ;
 OutputStream output = null;
 //int length = input.
 try {
 file =new File(path ,fileName);
 output = new FileOutputStream(file);
 int ch ;
 double totalReaded = 0;
 double progress ;
 while((ch=input.read())!=-1) {
 output.write(ch);
 totalReaded++;
 progress = totalReaded / contentLength;
 listener.process(progress);
 }
 output.flush();
 } catch (IOException e) {
 e.printStackTrace();
 }
 finally {
 try {
 if(output!=null) {
 output.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }

这一点我出问题是在保存文件上,一开始采用了跟之前一样的 byte[] buffer = new byte[1024]; 进行获取,可能是设太大的原因,最后的文件是有问题的,比如图片最下会出现黑条,应该是字节数不对齐的问题,所以我这里采用一字节一字节读的方式。关于这点恳请大家指教。

四、文件上传

刚刚我们做的post,没有指明Content-Type,也就是参数的编码格式,使用的是默认的x-www-form-urlencoded,比如:tel=13637829200&password=123456这样直接拼接的。这种编码的优点是简洁,基本上可以适用于所有的情况,唯独在上传文件或者二进制的数据时效率低。所以在上传这块我们需要使用multipart/form-data。他的格式是这样的:

-----------------------------7cd1d6371ec
Content-Disposition: form-data; name="name"

ryan ou
-----------------------------7cd1d6371ec
Content-Disposition: form-data; name="email"

ryan@rhythmtechnology.com
-----------------------------7cd1d6371ec
Content-Disposition: form-data; name="Logo"; filename="D:\My Documents\My Pictures\Logo.jpg"
Content-Type: image/jpeg

每个field被分成小部分,而且包含一个value是"form-data"的"Content-Disposition"的头部;一个"name"属性对应field的ID。

所以,我们不仅需要指定Content-Type,还需要对参数进行处理。如下:

/**
 * 发送文件post请求,使用multipart/form-data类型,传输比较大的二进制或者文本数据时效率比较好
 *@param urlstr 上传路劲
 *@param files 上传文件map,可空
 *@param map 上传文本内容,可空
 *@return 服务器响应内容
 */
 public static  String doFilePost(String urlstr, Map<String, String> map, Map<String, File> files) throws IOException {
 String BOUNDARY = "----WebKitFormBoundaryDwvXSRMl0TBsL6kW"; // 定义数据分隔线
 URL url = new URL(urlstr);
 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
 // 发送POST请求必须设置如下两行
 connection.setDoOutput(true);
 connection.setDoInput(true);
 connection.setUseCaches(false);
 connection.setRequestMethod("POST");
 connection.setRequestProperty("Accept", "*/*");
 connection.setRequestProperty("connection", "Keep-Alive");
 connection.setRequestProperty("Charsert", "UTF-8");
 connection.setRequestProperty("Accept-Encoding", "gzip,deflate");
 connection.setRequestProperty("Content-Type",
 "multipart/form-data; boundary=" + BOUNDARY);
 OutputStream out = new DataOutputStream(connection.getOutputStream());
 byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();// 定义最后数据分隔线
​
 // 文件
 if (files != null && !files.isEmpty()) {
 for (Map.Entry<String, File> entry : files.entrySet()) {
 File file = entry.getValue();
 String fileName = entry.getKey();
 StringBuilder sb = new StringBuilder();
 sb.append("--");
 sb.append(BOUNDARY);
 sb.append("\r\n");
 sb.append("Content-Disposition: form-data;name=\"" + fileName
 + "\";filename=\"" + file.getName() + "\"\r\n");
 String type =  getMimeType(file.getAbsolutePath());
 Log.d(TAG,"type = "+type);
 sb.append("Content-Type: "+type+"\r\n\r\n");
 byte[] data = sb.toString().getBytes();
 out.write(data);
 DataInputStream in = new DataInputStream(new FileInputStream(
 file));
 int bytes ;
 byte[] bufferOut = new byte[1024];
 while ((bytes = in.read(bufferOut)) != -1) {
 out.write(bufferOut, 0, bytes);
 }
 out.write("\r\n".getBytes()); // 多个文件时,二个文件之间加入这个
 in.close();
 }
 }
 // 数据参数
 if (map != null && !map.isEmpty()) {
 for (Map.Entry<String, String> entry : map.entrySet()) {
 StringBuilder sb = new StringBuilder("");
 sb.append("--");
 sb.append(BOUNDARY);
 sb.append("\r\n");
 sb.append("Content-Disposition: form-data; name=\""
 + entry.getKey() + "\"");
 sb.append("\r\n");
 sb.append("\r\n");
 sb.append(entry.getValue());
 sb.append("\r\n");
 byte[] data = sb.toString().getBytes();
 out.write(data);
 }
 }
 out.write(end_data);
 out.flush();
 out.close();
 if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
 InputStream inStream = connection.getInputStream();
 String json = getStringFromInputStream(inStream);
 return json;
 }
 return null;
 }
​

五、其他点

下载监听:可以在HttpURLConnection连接之后使用

httpURLConnection.getContentLength()

获得总大小,然后在处理InputStream的时候加上对比得到下载进度。

参考文档:

  1. https://www.jianshu.com/p/3141d4e46240 - Android网络请求心路历程

  2. http://blog.csdn.net/hxchuan000/article/details/48949061 - x-www-form-urlencoded与multipart/form-data区别

上一篇 下一篇

猜你喜欢

热点阅读