当前位置: 代码迷 >> Android >> Android Volley解析(2)之表单提交篇
  详细解决方案

Android Volley解析(2)之表单提交篇

热度:95   发布时间:2016-04-28 02:12:37.0
Android Volley解析(二)之表单提交篇

上一篇文章中,讲了 Volley 的 get 和 post 请求,并且对 volley 的基本使用和基本分析做了讲解,而这篇 blog 将讲解用 volley 实现表单的提交,在看这篇文章之前,如果对 Volley 基本知识不够了解的朋友,可以移驾前往Android Volley解析(一)之GET、POST请求篇

表单提交的数据格式

要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式。
数据包:

Connection: keep-aliveContent-Length: 123X-Requested-With: ShockwaveFlash/16.0.0.296User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36Content-Type: multipart/form-data; boundary=----------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1Accept: */*Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.8Cookie: bdshare_firstime=1409052493497------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1Content-Disposition: form-data; name="position"1425264476444------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1Content-Disposition: form-data; name="editorIndex"ue_con_1425264252856------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1Content-Disposition: form-data; name="cm"100672------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--

看到上面的数据包,我们不需要全部分析,我们主要关心的是数据如何封装,因为http请求头,在网络请求中已经为我们封装好了;可以看到这里总共是提交了三条数据,每一条数据的格式都是一样的,所以我们只需要分析一条数据即可,这里拿最后一条数据来说,因为在所有的数据之后还有一个结尾的标志。

------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1Content-Disposition: form-data; name="cm"100672------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--

这条数据一共有四行组成,加上结尾标志共有五行
1、第一行:

"--" + boundary + "\r\n" ;

说明:”–”为数据开始标志,boundary为http实体头定义的边界分割线,boundary可以是任意的字符串,只要和Content-Type: multipart/form-data; boundary=———-Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1中保持一直即可,”\r\n”为回车换行;
2、第二行:

"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;

说明:Content-Disposition表示上传的内容特性,form-data上传内容特性为表单的形式;
3、第三行:

"\r\n" ;

4、说明:这是一个空行,只有一个回车换行 ;
第四行:

"参数的值" + "\r\n" ;

说明:每个参数都是由一个key和value组成,而这一行就是value跟回车换行符
结尾行:

"--" + boundary + "--" + "\r\n" ;

说明:在所有的数据结束之后,需要有这个结尾标志。
如果有多个参数,则重复1、2、3、4,直至最后一个参数的最后加上结尾行。

参数实体类

这里对参数做一个封装,因为往往提交表单的时候,都需要提交多个参数

/** * Created by gyzhong on 15/3/2. *//*表单提交的参数*/public class FormText {    /*参数的名称*/    private String mName ;    /*参数的值*/    private String mValue ;    public FormText(String mName, String mValue) {        this.mName = mName;        this.mValue = mValue;    }    public String getName() {        return mName;    }    public String getValue() {        return mValue;    }}

Volley 对数据的封装

在上一篇文章中我们讲了如何定制自己的 Request,在这里同样需要用到。在定制 Request 的时候,需要重写获取实体的方法

public byte[] getBody() throws AuthFailureError {}

把参数通过二进制的形式传给服务器,当然就不需要重写获取参数的方法

protected Map<String, String> getParams() throws AuthFailureError {}

最核心的方法也就在getBody()中,这个方法的实现,如果对表单提交的数据格式很了解,实现起来非常简单,因为这个方法就是把参数拼接成我们所分析的数据格式;

    @Override    public byte[] getBody() throws AuthFailureError {        if (mListItem == null||mListItem.size() == 0){            return super.getBody() ;        }        ByteArrayOutputStream bos = new ByteArrayOutputStream() ;        int N = mListItem.size() ;        FormText formText ;        for (int i = 0; i < N ;i++){            formText = mListItem.get(i) ;            StringBuffer sb= new StringBuffer() ;            /*第一行:"--" + boundary + "\r\n" ;*/            sb.append("--"+BOUNDARY);            sb.append("\r\n") ;            /*第二行:"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;*/            sb.append("Content-Disposition: form-data;");            sb.append("name=\"");            sb.append(formText.getName()) ;            sb.append("\"") ;            sb.append("\r\n") ;            /*第三行:"\r\n" ;*/            sb.append("\r\n") ;            /*第四行:"参数的值" + "\r\n" ;*/            sb.append(formText.getValue()) ;            sb.append("\r\n") ;            try {                bos.write(sb.toString().getBytes("utf-8"));            } catch (IOException e) {                e.printStackTrace();            }        }        /*结尾行:"--" + boundary + "--" + "\r\n" ;*/        String endLine = "--" + BOUNDARY + "--"+ "\r\n" ;        try {            bos.write(endLine.toString().getBytes("utf-8"));        } catch (IOException e) {            e.printStackTrace();        }        Log.v("zgy","=====formText====\n"+bos.toString()) ;        return bos.toByteArray();    }

可以看到,这个方法的实现,就是对数据按照我们所分析的格式组装。
在 Request 中还有一个关键的地方,需要在 http 头部中声明内容类型为表单数据

Content-Type: multipart/form-data; boundary=----------8888888888888

所以的重写下面方法为

public String getBodyContentType() {        return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;    }

同样,我们还是前面我们blog 中讲到的接口http://www.minongbang.com/test.php?test=minongbang来测试,所以解析数据那一块跟 前面我们将的一样,具体实现如下:

/** * Created by gyzhong on 15/3/2. */public class PostFormRequest<T> extends Request<T> {    /**     * 正确数据的时候回掉用     */    private ResponseListener mListener ;    /*用来解析 json 用的*/    private Gson mGson ;    /*在用 gson 解析 json 数据的时候,需要用到这个参数*/    private Type mClazz ;    /*请求 数据通过参数的形式传入*/    private List<FormText> mListItem ;    private String BOUNDARY = "---------8888888888888"; //数据分隔线    private String MULTIPART_FORM_DATA = "multipart/form-data";    public PostFormRequest(String url, List<FormText> listItem, Type type, ResponseListener listener) {        super(Method.POST, url, listener);        this.mListener = listener ;        mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() ;        mClazz = type ;        setShouldCache(false);        mListItem = listItem ;    }    /**     * 这里开始解析数据     * @param response Response from the network     * @return     */    @Override    protected Response<T> parseNetworkResponse(NetworkResponse response) {        try {            T result ;            String jsonString =                    new String(response.data, HttpHeaderParser.parseCharset(response.headers));            Log.v("zgy", "====SearchResult===" + jsonString);            result = mGson.fromJson(jsonString,mClazz) ;            return Response.success(result,                    HttpHeaderParser.parseCacheHeaders(response));        } catch (UnsupportedEncodingException e) {            return Response.error(new ParseError(e));        }    }    /**     * 回调正确的数据     * @param response The parsed response returned by     */    @Override    protected void deliverResponse(T response) {        mListener.onResponse(response);    }    @Override    public byte[] getBody() throws AuthFailureError {        if (mListItem == null||mListItem.size() == 0){            return super.getBody() ;        }        ByteArrayOutputStream bos = new ByteArrayOutputStream() ;        int N = mListItem.size() ;        FormText formText ;        for (int i = 0; i < N ;i++){            formText = mListItem.get(i) ;            StringBuffer sb= new StringBuffer() ;            /*第一行:"--" + boundary + "\r\n" ;*/            sb.append("--"+BOUNDARY);            sb.append("\r\n") ;            /*第二行:"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;*/            sb.append("Content-Disposition: form-data;");            sb.append("name=\"");            sb.append(formText.getName()) ;            sb.append("\"") ;            sb.append("\r\n") ;            /*第三行:"\r\n" ;*/            sb.append("\r\n") ;            /*第四行:"参数的值" + "\r\n" ;*/            sb.append(formText.getValue()) ;            sb.append("\r\n") ;            try {                bos.write(sb.toString().getBytes("utf-8"));            } catch (IOException e) {                e.printStackTrace();            }        }        /*结尾行:"--" + boundary + "--" + "\r\n" ;*/        String endLine = "--" + BOUNDARY + "--"+ "\r\n" ;        try {            bos.write(endLine.toString().getBytes("utf-8"));        } catch (IOException e) {            e.printStackTrace();        }        Log.v("zgy","=====formText====\n"+bos.toString()) ;        return bos.toByteArray();    }    /*获取内容类型,这里为表单类型*/    @Override    public String getBodyContentType() {        return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;    }}

表单提交接口

接口我们在上一篇博文中也做了比较详细的讲解,这里就不累赘了。

    /**     *  minong 测试post表单提交接口     * @param  value 测试数据     * @param listener 回调接口,包含错误回调和正确的数据回调     */    public static void postFormMiNongApi(String value,ResponseListener listener){        List<FormText> formTextList = new ArrayList<FormText>() ;        formTextList.add(new FormText(" test",value));        Request request = new PostFormRequest(Constant.MiNonghost,formTextList,new TypeToken<SearchResult>(){}.getType(),listener) ;        VolleyUtil.getRequestQueue().add(request) ;    }

测试

PostFormActivity.java的测试代码如下

public class PostFormActivity extends ActionBarActivity {    private ListView mListView ;    private SongAdapter mAdapter ;    private List<SongDetail> mSongList ;    private ProgressDialog mDialog ;    private View mHeaderView ;    private TextView mShowPostData ;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_post_form);        mSongList = new ArrayList<SongDetail>() ;        mListView = (ListView) findViewById(R.id.id_list_view);        mAdapter = new SongAdapter(this,mSongList) ;        mHeaderView = getLayoutInflater().inflate(R.layout.list_header_item,null) ;        mShowPostData = (TextView) mHeaderView.findViewById(R.id.id_post_data) ;        mListView.addHeaderView(mHeaderView);        mListView.setAdapter(mAdapter);        mDialog = new ProgressDialog(this) ;        mDialog.setMessage("数据提交中...");        mDialog.show() ;        /*请求网络获取数据*/        MiNongApi.postFormMiNongApi(" minongbang",new DataResponseListener<SearchResult>() {            @Override            public void onErrorResponse(VolleyError error) {                Log.v("zgy","======onErrorResponse====="+error);                mDialog.dismiss();            }            @Override            public void onResponse(SearchResult response) {                Log.v("zgy","======onResponse====="+response);                mSongList.addAll(response.getData().getInfo()) ;                mAdapter.notifyDataSetChanged();                mDialog.dismiss();            }            @Override            public void postData(String data) {                mShowPostData.setText(data);            }        });    }

通过以上内容我们不难发现,表单提交就是把文本通过二进制的形式传给服务器,从而得到对应的响应,这篇 blog 其实也算是一篇过度的文章,因为一般我们不会这么提交文字数据,而是通过我们上一篇 blog 中的post 请求的方式,那什么时候我们需要用到表单提交呢,当表单中含有附件,如图片,视频等文件的的时候;这也就是我们下一篇 blog 要讲的:volley 解析(三)文件上传篇。

下载源码

  相关解决方案