美文网首页
Android网络请求类(使用HttpURLConnection

Android网络请求类(使用HttpURLConnection

作者: 虚假雨 | 来源:发表于2018-03-04 00:27 被阅读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区别

    相关文章

      网友评论

          本文标题:Android网络请求类(使用HttpURLConnection

          本文链接:https://www.haomeiwen.com/subject/cooxfftx.html