Android #03 与 WebServiceAQ 的交互

2019-05-06

目录:

  1. 功能需求--分析
  2. 具体实现--动手
  3. 踩过的坑
  4. 表单类型的请求--造轮子
  5. 总结

1. 功能需求--分析


文字描述:Android 与 WebServiceAQ 的交互,提交用户填写的数据和选择的图片文件到 server , 解析返回的数据做判断是否提交成功。

具体展示:
拿到的 API 文档( 已修改原始数据,这是模拟数据):

SOAP 1.1
POST /WebServiceAQ.asmx HTTP/1.1
Host: 10.100.101.100  //模拟数据
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/complaint"



  
    
  

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length



  
    
  


POST /WebServiceAQ.asmx/complaint HTTP/1.1
Host: 10.100.101.100 //模拟数据
Content-Type: application/x-www-form-urlencoded
Content-Length: length

HTTP/1.1 200 OK

思考实现方式:
[1] WebServiceAQ 是什么?
根据搜索引擎提供的知识得到的结论:WebServiceAQ 是通过 URL ,指定某一个方法名,发出请求,站点的这个方法,接收请求后,根据传入的参数做一些处理,然后将处理后的结果以 xml 的形式返回,Android 解析数据显示或者做其他操作。
[2] 与 Socket 的区别在哪?
第一, Socket是基于TCP/IP的传输层协议。 Webservice是基于HTTP协议传输数据,采用了基于http的soap协议传输数据。
第二,Socket接口通过流传输,不支持面向对象。 Webservice 接口支持面向对象,最终webservice将对象进行序列化后通过流传输。 Webservice采用soap协议进行通信,不需专门针对数据流的发送和接收进行处理,是一种跨平台的面向对象远程调用技术。
第三,Socket适用于高性能大数据的传输,传输的数据需要手动处理,socket通信的接口协议需要自定义。--此段解释来源

2. 具体实现--动手


[1] postman 测试:
对于与 server 的交互一般可以先用 postman 测试成功了,点击 code ,选择 java okhttp 后查看代码,如果项目刚好是用 okhttp 框架写的,那么真的是太幸运了,如果不是,学会框架间的对应转换也是不错的方法。

Android #03 与 WebServiceAQ 的交互_第1张图片
image.png

经过 postman 的测试,可以得到以下几点要注意的:
1.content-type : multipart/form-data
2.boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
3.url
4.请求体

[2] Android 的实现
首先是请求体部分:
name=\"json\"\r\n\r\n{\"Attachment_1\":\"open_screen_bg_img_1317.png\",\"Attachment_2\":\"open_screen_bg_img_1733.png\",\"Attachment_3\":\"open_screen_bg_img_1325.png\",\"Attachment_4\":\"open_screen_bg_img_1329.png\",\"Attachment_5\":\"open_screen_bg_img_1525.png\",\"CCID\":\"A\",\"CName\":\"测试\",\"DTNuber\":\"9875652358\",\"Demands\":\"testing\",\"EMail\":\"[email protected]\",\"FDP\":\"AAT\",\"FDate\":\"2019-5-16\",\"FName\":\"AQ1056\",\"Message\":\"testing\",\"ODP\":\"AAT\",\"PhoneNo\":\"1597698258\",\"SDP\":\"AMS\",\"CTID\":1,\"DTID\":1,\"FCCID\":1}
这是 json 格式的数据,需要把 Object -> JSONObject,构建 json 表单.

complaintInfo = new ComplaintInfo(FCCID, CCID, CTID, CName, DTID, DTNuber, FName, FDate, PhoneNo, EMail, ODP, SDP, FDP, Message, Demands, Attachment_1, Attachment_2, Attachment_3, Attachment_4, Attachment_5);
Gson gson = new Gson();
final String json = gson.toJson(complaintInfo);

其次是用户选择的文件部分(5张图片):
Content-Disposition: form-data; name=\"Attachment_1\"; filename=\"a.jpeg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"Attachment_2\"; filename=\"p.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"Attachment_3\"; filename=\"g.jpeg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"Attachment_4\"; filename=\"h.jpeg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"Attachment_5\"; filename=\"a.jpeg\"\r\nContent-Type: image/jpeg
需要获取文件名,转换成 file -> byte[] ,构建图片表单.

[1、获取文件名]
private List path = new ArrayList<>();
String pathImage = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Images.Media.DATA));
path.add(pathImage);
Attachment_1 = path.get(i).substring(path.get(i).lastIndexOf("/") + 1, path.get(i).length());

[2、转换 file -> byte[] ]
    /**
     * @param fileName 文件名
     * @return byte[]
     * @throws Exception
     */
    public static byte[] readStream(String fileName) throws Exception {
        FileInputStream inStream = new FileInputStream(new File(fileName));
        byte[] buffer = new byte[1024];
        int len = -1;
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        byte[] data = outStream.toByteArray();
        outStream.close();
        inStream.close();
        return data;
    }

[3、构建图片表单]
files[0] = new FormFile(Attachment_1, ImageDispose.readStream(path.get(0)), "Attachment_1", "multipart/form-data");

其中 FormFile 类
/**
 * Author : Emily CH
 * Date : 2019/5/27 上午9:38
 * UpdateUser : XXX
 * UpdateDate : 2019/5/27 上午9:38
 */
public class FormFile {

    /* 上传文件的数据 */
    private byte[] data;
    /* 文件名称 */
    private String filname;
    /* 表单字段名称*/
    private String formname;
    /* 内容类型 */
    private String contentType = "multipart/form-data"; //需要查阅相关的资料

    public FormFile(String filname, byte[] data, String formname, String contentType) {
        this.data = data;
        this.filname = filname;
        this.formname = formname;
        if(contentType!=null) this.contentType = contentType;
    }

    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public String getFilname() {
        return filname;
    }

    public void setFilname(String filname) {
        this.filname = filname;
    }

    public String getFormname() {
        return formname;
    }

    public void setFormname(String formname) {
        this.formname = formname;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

}

最后,请求体准备好,即可以发送请求了。


import com.jiuair.booking.model.FormFile;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

/**
 * Author : Emily CH
 * Date : 2019/5/27 上午9:39
 * UpdateUser : XXX
 * UpdateDate : 2019/5/27 上午9:39
 */
public class HttpService {

    public HttpService() {
    }

    public void postHttpImageRequest(final String netWorkAddress, final Map params, final FormFile[] files,
                                     final HttpCallBackListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    String BOUNDARY = "----WebKitFormBoundary7MA4YWxkTrZu0gW"; //数据分隔线
                    String MULTIPART_FORM_DATA = "multipart/form-data";

                    URL url = new URL(HttpClientUtil.BASEURL_ADD + netWorkAddress);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setDoInput(true);//允许输入
                    connection.setDoOutput(true);//允许输出
                    connection.setUseCaches(false);//不使用Cache
                    connection.setRequestMethod("POST");
                    connection.setRequestProperty("Connection", "Keep-Alive");
                    connection.setRequestProperty("Charset", "UTF-8");
                    connection.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);

                    StringBuilder sb = new StringBuilder();

                    //上传的表单参数部分
                    for (Map.Entry entry : params.entrySet()) {//构建表单字段内容
                        sb.append("--");
                        sb.append(BOUNDARY);
                        sb.append("\r\n");
                        sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n");
                        sb.append(entry.getValue());
                        sb.append("\r\n");
                    }
                    DataOutputStream outStream = new DataOutputStream(connection.getOutputStream());
                    outStream.write(sb.toString().getBytes());//发送表单字段数据
                    //上传的文件部分
                    for (FormFile file : files) {
                        StringBuilder split = new StringBuilder();
                        split.append("--");
                        split.append(BOUNDARY);
                        split.append("\r\n");
                        split.append("Content-Disposition: form-data; name=\"" + file.getFormname() + "\"; filename=\"" + file.getFilname() + "\"\r\n");
                        split.append("Content-Type: " + file.getContentType() + "\r\n\r\n");
                        outStream.write(split.toString().getBytes());
                        outStream.write(file.getData(), 0, file.getData().length);
                        outStream.write("\r\n".getBytes());
                    }
                    byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();//数据结束标志
                    outStream.write(end_data);
                    outStream.flush();
                    int cah = connection.getResponseCode();
                    if (cah != 200) throw new RuntimeException("请求url失败");
                    InputStream in = connection.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }

                    if (listener != null) {
                        listener.onFinish(response.toString());
                    }
                    outStream.close();
                } catch (Exception e) {
                    if (listener != null) {
                        listener.onError(e);
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

    public interface HttpCallBackListener {
        void onFinish(String response);

        void onError(Exception e);
    }
}

在 Activity 中调用:

 httpService.postHttpImageRequest("/complaintinfo", params, files, new HttpService.HttpCallBackListener() {

            public void onFinish(final String response) {
                 //  showCustomToast("ATG"+response);
            }

            public void onError(Exception e) {
                //  showCustomToast("提交失败!");
                e.printStackTrace();
            }
        });

3. 踩过的坑


3.1 在用 postman 的 模版 code 时,复制黏贴的后果是 \ 变成 \\,然后\\r\\n 是不符合原意的,应该就是一个 . 但如果你的本意是想要显示出 \ 的,那么就应该是 triple \

斜杠“/”表示地址路径的下一级目录;
反斜杠“\”表示转义字符,例如:要做制表,可以输入:\t;做换行:\n等。
如果要输出反斜杠“\”也需要用转义字符:“\\”
    在java中后台给前台传的时候如果我们的字符串中有“\”的话,我们可以通过string中的substring方法将‘\’转化为‘/’

3.2 尝试过 搜索引擎的前20条 android 与 webservice 交互的经验,全部跌入谷底,因为请求的数据类型等各种原因,但每次掉入都让我进一步揭开了 server 端的神秘面纱,很高兴我最终从深渊爬上来了,感谢前辈们的分享。

4. 表单类型的请求--造轮子


轮子 HttpServiceUtil

5. 总结


踩过无数的坑,才知道只有真的懂了,明白了每一句代码的意思,才能做到想改哪改哪,前辈们的知识精华哪是一朝一夕就能拿来用的,关键还不是靠自己?搬砖搬砖!!!

文章是 Android 面向需求开发系列中的一文,更多相关文章请关注。如若有什么问题,也可以通过扫描二维码发消息给我。转载请注明出处,谢谢!

二维码

作者:Emily CH
2019年5月7日

你可能感兴趣的:(Android #03 与 WebServiceAQ 的交互)