編輯:關於Android編程
上一篇文章中,講了 Volley 的 get 和 post 請求,並且對 volley 的基本使用和基本分析做了講解,而這篇 blog 將講解用 volley 實現表單的提交
要實現表單的提交,就要知道表單提交的數據格式是怎麼樣,這裡我從某知名網站抓了一條數據,先來分析別人提交表單的數據格式。
數據包:
Connection: keep-alive
Content-Length: 123
X-Requested-With: ShockwaveFlash/16.0.0.296
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36
Content-Type: multipart/form-data; boundary=----------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1409052493497
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="position"
1425264476444
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="editorIndex"
ue_con_1425264252856
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="cm"
100672
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--
看到上面的數據包,我們不需要全部分析,我們主要關心的是數據如何封裝,因為http請求頭,在網絡請求中已經為我們封裝好了;可以看到這裡總共是提交了三條數據,每一條數據的格式都是一樣的,所以我們只需要分析一條數據即可,這裡拿最後一條數據來說,因為在所有的數據之後還有一個結尾的標志。
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-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;
}
}
在上一篇文章中我們講了如何定制自己的 Request,在這裡同樣需要用到。在定制 Request 的時候,需要重寫獲取實體的方法
public byte[] getBody() throws AuthFailureError {}
把參數通過二進制的形式傳給服務器,當然就不需要重寫獲取參數的方法
protected Map 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 mListItem ;
private String BOUNDARY = "---------8888888888888"; //數據分隔線
private String MULTIPART_FORM_DATA = "multipart/form-data";
public PostFormRequest(String url, List 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 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 formTextList = new ArrayList() ;
formTextList.add(new FormText(" test",value));
Request request = new PostFormRequest(Constant.MiNonghost,formTextList,new TypeToken(){}.getType(),listener) ;
VolleyUtil.getRequestQueue().add(request) ;
}
PostFormActivity.Java的測試代碼如下
public class PostFormActivity extends ActionBarActivity {
private ListView mListView ;
private SongAdapter mAdapter ;
private List 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() ;
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() {
@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 請求的方式,那什麼時候我們需要用到表單提交呢,當表單中含有附件,如圖片,視頻等文件的的時候
https://github.com/hongyangAndroid/MagicViewPager實現效果:Rotate Y Rotate Dow
1、效果圖如下:這效果用戶體驗還是很酷炫,今天我們就來講解如何實現這個效果。2、分析為了方便理解,作圖分析如圖所示,整個頁面分為四個部分: &nbs
現在app基本都有推送的功能,於是看了下百度雲的推送,官方文檔和Demo都很到位,記錄下使用過程,目標是利用百度雲推送最為服務器寫個及時通訊的例子~當然了,
Android中登錄界面的記住密碼功能實現,將用戶輸入的賬號和密碼以SharedPreferences方式存儲(注意的是,密碼要用MD5明文加密)。 界面xml