前言 上個知識點介紹了OKHttp的基本使用,在Activity中寫了大量訪問網路的代碼,這種代碼寫起來很無聊,並且對技術沒什麼提升。在真實的企業開發中,肯定是把這些代碼封裝起來,做一個庫,給Activity調用。 封裝之前我們需要考慮以下這些問題: 封裝基本的公共方法給外部調用。get請求,Pos ...
前言
上個知識點介紹了OKHttp的基本使用,在Activity中寫了大量訪問網路的代碼,這種代碼寫起來很無聊,並且對技術沒什麼提升。在真實的企業開發中,肯定是把這些代碼封裝起來,做一個庫,給Activity調用。
封裝之前我們需要考慮以下這些問題:
- 封裝基本的公共方法給外部調用。get請求,Post請求,PostFile
- 官方建議OkHttpClient實例只new一次,那我們網路請求庫可以做成單例模式。
- 如果同一時間訪問同一個api多次,我們是不是應該取消之前的請求?
- 如果用戶連接Http代理了,就不讓訪問,防止用戶通過抓包工具看我們的介面數據。
- 每個介面都要帶上的參數如何封裝?例如app版本號,設備號,登錄之後的用戶token,這些參數可能每次請求都要帶上。
- 返回的json字元串轉成實體對象
- 訪問伺服器是非同步請求,如何在主線程中調用回調介面?
代碼實現
首先需要線上引用以下三個依賴庫
compile 'com.squareup.okhttp3:okhttp:3.2.0' //okhttp
compile 'com.google.code.gson:gson:2.7' //解析jsons數據
compile 'io.github.lizhangqu:coreprogress:1.0.2' //上傳下載回調監聽
新建一個HttpConfig類,用來配置一些請求參數,存儲請求伺服器的公共參數。設置連接時間等。。。
public class HttpConfig {
private boolean debug=false;//true:debug模式
private String userAgent="";//用戶代理 它是一個特殊字元串頭,使得伺服器能夠識別客戶使用的操作系統及版本、CPU 類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。
private boolean agent=true;//有代理的情況能不能訪問,true:有代理能訪問 false:有代理不能訪問
private String tagName="Http";
private int connectTimeout=10;//連接超時時間 單位:秒
private int writeTimeout=10;//寫入超時時間 單位:秒
private int readTimeout=30;//讀取超時時間 單位:秒
//通用欄位
private List<NameValuePair> commonField=new ArrayList<>();
public boolean isDebug() {
return debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public boolean isAgent() {
return agent;
}
public void setAgent(boolean agent) {
this.agent = agent;
}
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
public List<NameValuePair> getCommonField() {
return commonField;
}
public int getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public int getWriteTimeout() {
return writeTimeout;
}
public void setWriteTimeout(int writeTimeout) {
this.writeTimeout = writeTimeout;
}
public int getReadTimeout() {
return readTimeout;
}
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
/**
* 更新參數
* @param key
* @param value
*/
public void updateCommonField(String key,String value){
boolean result = true;
for(int i=0;i<commonField.size();i++){
NameValuePair nameValuePair = commonField.get(i);
if(nameValuePair.getName().equals(key)){
commonField.set(i,new NameValuePair(key,value));
result = false;
break;
}
}
if(result){
commonField.add(new NameValuePair(key,value));
}
}
/**
* 刪除公共參數
* @param key
*/
public void removeCommonField(String key){
for(int i=commonField.size()-1;i>=0;i--){
if(commonField.get(i).equals("key")){
commonField.remove(i);
}
}
}
/**
* 添加請求參數
* @param key
* @param value
*/
public void addCommonField(String key,String value){
commonField.add(new NameValuePair(key,value));
}
}
我們給伺服器提交表單數據都是通過name跟vulue的形式提交數據,封裝了NameValuePair類。如果是上傳文件,isFile設置true就行。
public class NameValuePair {
private String name;//請求名稱
private String value;//請求值
private boolean isFile=false;//是否是文件
public NameValuePair(String name, String value){
this.name=name;
this.value = value;
}
public NameValuePair(String name, String value,boolean isFile){
this.name=name;
this.value = value;
this.isFile=isFile;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public boolean isFile() {
return isFile;
}
public void setFile(boolean file) {
isFile = file;
}
}
請求伺服器成功或者失敗都會回調Callback介面,我們封裝HttpResponseHandler抽象類來實現這個介面,對伺服器返回的數據以及狀態碼進行了簡單的過濾,最終最調用自己的onFailure跟onSuccess方法。
public abstract class HttpResponseHandler implements Callback {
public HttpResponseHandler(){
}
public void onFailure(Call call, IOException e){
onFailure(-1,e.getMessage().getBytes());
}
public void onResponse(Call call, Response response) throws IOException {
int code =response.code();
byte[] body = response.body().bytes();
if(code>299){
onFailure(response.code(),body);
}else{
Headers headers = response.headers();
Header[] hs = new Header[headers.size()];
for (int i=0;i<headers.size();i++){
hs[i] = new Header(headers.name(i),headers.value(i));
}
onSuccess(code,hs,body);
}
}
public void onFailure(int status,byte[] data){
}
// public void onProgress(int bytesWritten, int totalSize) {
// }
public abstract void onSuccess(int statusCode, Header[] headers, byte[] responseBody);
}
Get請求封裝
封裝後的HTTPCaller代碼如下,現在裡面只有get請求,如果一下子把post請求跟postFile方法貼進來,代碼有點多。
public class HTTPCaller {
private static HTTPCaller _instance = null;
private OkHttpClient client;//okhttp對象
private Map<String,Call> requestHandleMap = null;//以URL為KEY存儲的請求
private CacheControl cacheControl = null;//緩存控制器
private Gson gson = null;
private HttpConfig httpConfig=new HttpConfig();//配置信息
private HTTPCaller() {}
public static HTTPCaller getInstance(){
if (_instance == null) {
_instance = new HTTPCaller();
}
return _instance;
}
/**
* 設置配置信息 這個方法必需要調用一次
* @param httpConfig
*/
public void setHttpConfig(HttpConfig httpConfig) {
this.httpConfig = httpConfig;
client = new OkHttpClient.Builder()
.connectTimeout(httpConfig.getConnectTimeout(), TimeUnit.SECONDS)
.writeTimeout(httpConfig.getWriteTimeout(), TimeUnit.SECONDS)
.readTimeout(httpConfig.getReadTimeout(), TimeUnit.SECONDS)
.build();
gson = new Gson();
requestHandleMap = Collections.synchronizedMap(new WeakHashMap<String, Call>());
cacheControl =new CacheControl.Builder().noStore().noCache().build();//不使用緩存
}
public <T> void get(Class<T> clazz,final String url,Header[] header,final RequestDataCallback<T> callback) {
this.get(clazz,url,header,callback,true);
}
/**
* get請求
* @param clazz json對應類的類型
* @param url 請求url
* @param header 請求頭
* @param callback 回調介面
* @param autoCancel 是否自動取消 true:同一時間請求一個介面多次 只保留最後一個
* @param <T>
*/
public <T> void get(final Class<T> clazz,final String url,Header[] header,final RequestDataCallback<T> callback, boolean autoCancel){
if (checkAgent()) {
return;
}
add(url,getBuilder(url, header, new MyHttpResponseHandler(clazz,url,callback)),autoCancel);
}
private Call getBuilder(String url, Header[] header, HttpResponseHandler responseCallback) {
url=Util.getMosaicParameter(url,httpConfig.getCommonField());//拼接公共參數
Request.Builder builder = new Request.Builder();
builder.url(url);
builder.get();
return execute(builder, header, responseCallback);
}
private Call execute(Request.Builder builder, Header[] header, Callback responseCallback) {
boolean hasUa = false;
if (header == null) {
builder.header("Connection","close");
builder.header("Accept", "*/*");
} else {
for (Header h : header) {
builder.header(h.getName(), h.getValue());
if (!hasUa && h.getName().equals("User-Agent")) {
hasUa = true;
}
}
}
if (!hasUa&&!TextUtils.isEmpty(httpConfig.getUserAgent())){
builder.header("User-Agent",httpConfig.getUserAgent());
}
Request request = builder.cacheControl(cacheControl).build();
Call call = client.newCall(request);
call.enqueue(responseCallback);
return call;
}
public class MyHttpResponseHandler<T> extends HttpResponseHandler {
private Class<T> clazz;
private String url;
private RequestDataCallback<T> callback;
public MyHttpResponseHandler(Class<T> clazz,String url,RequestDataCallback<T> callback){
this.clazz=clazz;
this.url=url;
this.callback=callback;
}
@Override
public void onFailure(int status, byte[] data) {
clear(url);
try {
printLog(url + " " + status + " " + new String(data, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
sendCallback(callback);
}
@Override
public void onSuccess(int status,final Header[] headers, byte[] responseBody) {
try {
clear(url);
String str = new String(responseBody,"utf-8");
printLog(url + " " + status + " " + str);
T t = gson.fromJson(str, clazz);
sendCallback(status,t,responseBody,callback);
} catch (Exception e){
if (httpConfig.isDebug()) {
e.printStackTrace();
printLog("自動解析錯誤:" + e.toString());
}
sendCallback(callback);
}
}
}
private void autoCancel(String function){
Call call = requestHandleMap.remove(function);
if (call != null) {
call.cancel();
}
}
private void add(String url,Call call) {
add(url,call,true);
}
/**
* 保存請求信息
* @param url 請求url
* @param call http請求call
* @param autoCancel 自動取消
*/
private void add(String url,Call call,boolean autoCancel) {
if (!TextUtils.isEmpty(url)){
if (url.contains("?")) {//get請求需要去掉後面的參數
url=url.substring(0,url.indexOf("?"));
}
if(autoCancel){
autoCancel(url);//如果同一時間對api進行多次請求,自動取消之前的
}
requestHandleMap.put(url,call);
}
}
private void clear(String url){
if (url.contains("?")) {//get請求需要去掉後面的參數
url=url.substring(0,url.indexOf("?"));
}
requestHandleMap.remove(url);
}
private void printLog(String content){
if(httpConfig.isDebug()){
Log.i(httpConfig.getTagName(),content);
}
}
/**
* 檢查代理
* @return
*/
private boolean checkAgent() {
if (httpConfig.isAgent()){
return false;
} else {
String proHost = android.net.Proxy.getDefaultHost();
int proPort = android.net.Proxy.getDefaultPort();
if (proHost==null || proPort<0){
return false;
}else {
Log.i(httpConfig.getTagName(),"有代理,不能訪問");
return true;
}
}
}
//更新欄位值
public void updateCommonField(String key,String value){
httpConfig.updateCommonField(key,value);
}
public void removeCommonField(String key){
httpConfig.removeCommonField(key);
}
public void addCommonField(String key,String value){
httpConfig.addCommonField(key,value);
}
private <T> void sendCallback(RequestDataCallback<T> callback){
sendCallback(-1,null,null,callback);
}
private <T> void sendCallback(int status,T data,byte[] body,RequestDataCallback<T> callback){
CallbackMessage<T> msgData = new CallbackMessage<T>();
msgData.body = body;
msgData.status = status;
msgData.data = data;
msgData.callback = callback;
Message msg = handler.obtainMessage();
msg.obj = msgData;
handler.sendMessage(msg);
}
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
CallbackMessage data = (CallbackMessage)msg.obj;
data.callback();
}
};
private class CallbackMessage<T>{
public RequestDataCallback<T> callback;
public T data;
public byte[] body;
public int status;
public void callback(){
if(callback!=null){
if(data==null){
callback.dataCallback(null);
}else{
callback.dataCallback(status,data,body);
}
}
}
}
}
- getInstance 用來獲取對象類的對象,因為是單例模式,這個方法無論調用多少次只會創建一個對象。
- setHttpConfig 創建了HTTPCaller對象的時候必須要調用該方法來初始化一些對象,通過參數httpConfig獲取連接時間來初始化OkHttpClient對象,用WeakHashMap來存儲每一次的請求。還需要初始化Gson對象。
- get 有兩個同名方法,這是方法重載,如果你想同一時間訪問同一介面多次,用下麵那個方法,最後一個一個參數傳false,如果沒有要求就用上面那個。我們看到在get方法裡面首先調用了getBuilder發起get請求,getBuilder會返回一個Call對象,這個方法的第三個參數傳入一個MyHttpResponseHandler對象,這個對象實現了Callback介面,請求伺服器成功或者失敗會把結果回調這個類。最外層調用了add方法,請求url作為key,Call作為vuelue保存到map裡面。
- getBuilder 這個方法的第一行調用Util的getMosaicParameter方法拼接公共參數,然後根據url生成Request.Builder對象,繼續調用execute。
- execute 首先設置請求頭信息,一般沒有特殊要求請求都不需要設置,通過builder對象的cacheControl方法設置緩存控制,通過build方法生成Request,通過OkHttpClient對象的newCall方法生成一個call對象,最後調用enqueue方法,進行非同步請求,傳入一個回調介面。
MyHttpResponseHandler類繼承我們自己寫的HttpResponseHandler抽象類,重寫兩個方法onFailure跟onSuccess。
onFailure 先從map中刪除這個請求,列印錯誤log,發送回調。
onSuccess 先從map中刪除這個請求,把json字元串轉成對象。發送回調。
- autoCancel 從map中刪除這個請求,調用Call.call取消http請求。
- add 有兩個同名的方法,方法重載。我們來分析後面這個。首先判斷這個url是否為空,為空的話肯定啥也幹不了。然後需要截取?前面的信息,不然參數不一樣我們就沒法判斷是不是同一時間多次請求同一介面了。然後判斷要不要自動取消。最後存入map。經過前面的分析我們都知道發起get請求的時候會調用add方法把call請求保存到map裡面去,請求成功回調中會把call從map中刪除。假如手機網路狀態不穩定,同一時間請求了10次同一個介面,都沒有回調回來,這也是為什麼我們需要在添加的時候會自動取消之前的請求。
- clear 從map中刪除請求
- printLog 列印log
- sendCallback 有兩個方法,方法重載。把返回內容,狀態,byte數組,回調封裝到CallbackMessage對象,通過handerl把這個對象發送到主線程中,然後調用CallbackMessage對象的callback方法。
Post請求封裝
通過以上對HTTPCaller類各個方法的解釋,相信你知道了get請求伺服器的整個調用流程。現在我們對HTTPCaller類增加post請求的方法以及上傳文件的方法。postFile方法增加了ProgressUIListener參數,通過這個介面監聽上傳文件進度的回調。
public <T> void post(final Class<T> clazz, final String url, Header[] header, List<NameValuePair> params, final RequestDataCallback<T> callback) {
this.post(clazz,url, header, params, callback,true);
}
/**
*
* @param clazz json對應類的類型
* @param url 請求url
* @param header 請求頭
* @param params 參數
* @param callback 回調
* @param autoCancel 是否自動取消 true:同一時間請求一個介面多次 只保留最後一個
* @param <T>
*/
public <T> void post(final Class<T> clazz,final String url, Header[] header, final List<NameValuePair> params, final RequestDataCallback<T> callback, boolean autoCancel) {
if (checkAgent()) {
return;
}
add(url,postBuilder(url, header, params, new HTTPCaller.MyHttpResponseHandler(clazz,url,callback)),autoCancel);
}
private Call postBuilder(String url, Header[] header, List<NameValuePair> form, HttpResponseHandler responseCallback) {
try {
if (form == null) {
form = new ArrayList<>(2);
}
form.addAll(httpConfig.getCommonField());//添加公共欄位
FormBody.Builder formBuilder = new FormBody.Builder();
for (NameValuePair item : form) {
formBuilder.add(item.getName(), item.getValue());
}
RequestBody requestBody = formBuilder.build();
Request.Builder builder = new Request.Builder();
builder.url(url);
builder.post(requestBody);
return execute(builder, header, responseCallback);
} catch (Exception e) {
if (responseCallback != null)
responseCallback.onFailure(-1, e.getMessage().getBytes());
}
return null;
}
/**
* 上傳文件
* @param clazz json對應類的類型
* @param url 請求url
* @param header 請求頭
* @param form 請求參數
* @param callback 回調
* @param <T>
*/
public <T> void postFile(final Class<T> clazz, final String url, Header[] header,List<NameValuePair> form,final RequestDataCallback<T> callback) {
postFile(url, header, form, new HTTPCaller.MyHttpResponseHandler(clazz,url,callback),null);
}
/**
* 上傳文件
* @param clazz json對應類的類型
* @param url 請求url
* @param header 請求頭
* @param form 請求參數
* @param callback 回調
* @param progressUIListener 上傳文件進度
* @param <T>
*/
public <T> void postFile(final Class<T> clazz, final String url, Header[] header,List<NameValuePair> form,final RequestDataCallback<T> callback,ProgressUIListener progressUIListener) {
add(url, postFile(url, header, form, new HTTPCaller.MyHttpResponseHandler(clazz,url,callback),progressUIListener));
}
/**
* 上傳文件
* @param clazz json對應類的類型
* @param url 請求url
* @param header 請求頭
* @param name 名字
* @param fileName 文件名
* @param fileContent 文件內容
* @param callback 回調
* @param <T>
*/
public <T> void postFile(final Class<T> clazz,final String url,Header[] header,String name,String fileName,byte[] fileContent,final RequestDataCallback<T> callback){
postFile(clazz,url,header,name,fileName,fileContent,callback,null);
}
/**
* 上傳文件
* @param clazz json對應類的類型
* @param url 請求url
* @param header 請求頭
* @param name 名字
* @param fileName 文件名
* @param fileContent 文件內容
* @param callback 回調
* @param progressUIListener 回調上傳進度
* @param <T>
*/
public <T> void postFile(Class<T> clazz,final String url,Header[] header,String name,String fileName,byte[] fileContent,final RequestDataCallback<T> callback,ProgressUIListener progressUIListener) {
add(url,postFile(url, header,name,fileName,fileContent,new HTTPCaller.MyHttpResponseHandler(clazz,url,callback),progressUIListener));
}
private Call postFile(String url, Header[] header,List<NameValuePair> form,HttpResponseHandler responseCallback,ProgressUIListener progressUIListener){
try {
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
MediaType mediaType = MediaType.parse("application/octet-stream");
form.addAll(httpConfig.getCommonField());//添加公共欄位
for(int i=form.size()-1;i>=0;i--){
NameValuePair item = form.get(i);
if(item.isFile()){//上傳文件
File myFile = new File(item.getValue());
if (myFile.exists()){
String fileName = Util.getFileName(item.getValue());
builder.addFormDataPart(item.getName(), fileName,RequestBody.create(mediaType, myFile));
}
}else{
builder.addFormDataPart(item.getName(), item.getValue());
}
}
RequestBody requestBody;
if(progressUIListener==null){//不需要回調進度
requestBody=builder.build();
}else{//需要回調進度
requestBody = ProgressHelper.withProgress(builder.build(),progressUIListener);
}
Request.Builder requestBuider = new Request.Builder();
requestBuider.url(url);
requestBuider.post(requestBody);
return execute(requestBuider, header, responseCallback);
} catch (Exception e) {
e.printStackTrace();
Log.e(httpConfig.getTagName(),e.toString());
if (responseCallback != null)
responseCallback.onFailure(-1, e.getMessage().getBytes());
}
return null;
}
private Call postFile(String url, Header[] header,String name,String filename,byte[] fileContent, HttpResponseHandler responseCallback,ProgressUIListener progressUIListener) {
try {
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
MediaType mediaType = MediaType.parse("application/octet-stream");
builder.addFormDataPart(name,filename,RequestBody.create(mediaType, fileContent));
List<NameValuePair> form = new ArrayList<>(2);
form.addAll(httpConfig.getCommonField());//添加公共欄位
for (NameValuePair item : form) {
builder.addFormDataPart(item.getName(),item.getValue());
}
RequestBody requestBody;
if(progressUIListener==null){//不需要回調進度
requestBody=builder.build();
}else{//需要回調進度
requestBody = ProgressHelper.withProgress(builder.build(),progressUIListener);
}
Request.Builder requestBuider = new Request.Builder();
requestBuider.url(url);
requestBuider.post(requestBody);
return execute(requestBuider, header,responseCallback);
} catch (Exception e) {
if (httpConfig.isDebug()) {
e.printStackTrace();
Log.e(httpConfig.getTagName(), e.toString());
}
if (responseCallback != null)
responseCallback.onFailure(-1, e.getMessage().getBytes());
}
return null;
}
增加下載文件的方法
get請求跟post請求都有了,上傳文件就是post請求,繼續增加downloadFile方法用來下載文件,通用的MyHttpResponseHandler不能處理回調,又增加了DownloadFileResponseHandler類處理下載回調。
public void downloadFile(String url, String saveFilePath, Header[] header, ProgressUIListener progressUIListener) {
downloadFile(url,saveFilePath, header, progressUIListener,true);
}
public void downloadFile(String url,String saveFilePath, Header[] header,ProgressUIListener progressUIListener,boolean autoCancel) {
if (checkAgent()) {
return;
}
add(url,downloadFileSendRequest(url,saveFilePath, header, progressUIListener),autoCancel);
}
private Call downloadFileSendRequest(String url, final String saveFilePath, Header[] header, final ProgressUIListener progressUIListener){
Request.Builder builder = new Request.Builder();
builder.url(url);
builder.get();
return execute(builder, header, new HTTPCaller.DownloadFileResponseHandler(url,saveFilePath,progressUIListener));
}
public class DownloadFileResponseHandler implements Callback {
private String saveFilePath;
private ProgressUIListener progressUIListener;
private String url;
public DownloadFileResponseHandler(String url,String saveFilePath,ProgressUIListener progressUIListener){
this.url=url;
this.saveFilePath=saveFilePath;
this.progressUIListener=progressUIListener;
}
@Override
public void onFailure(Call call, IOException e) {
clear(url);
try {
printLog(url + " " + -1 + " " + new String(e.getMessage().getBytes(), "utf-8"));
} catch (UnsupportedEncodingException encodingException) {
encodingException.printStackTrace();
}
}
@Override
public void onResponse(Call call, Response response) throws IOException {
printLog(url + " code:" + response.code());
clear(url);
ResponseBody responseBody = ProgressHelper.withProgress(response.body(),progressUIListener);
BufferedSource source = responseBody.source();
File outFile = new File(saveFilePath);
outFile.delete();
outFile.createNewFile();
BufferedSink sink = Okio.buffer(Okio.sink(outFile));
source.readAll(sink);
sink.flush();
source.close();
}
}
RequestDataCallback介面,封裝了三個方法,自己想要什麼數據就去重寫對應的方法。
public abstract class RequestDataCallback<T> {
//返回json對象
public void dataCallback(T obj) {
}
//返回http狀態和json對象
public void dataCallback(int status, T obj) {
dataCallback(obj);
}
//返回http狀態、json對象和http原始數據
public void dataCallback(int status, T obj, byte[] body) {
dataCallback(status, obj);
}
}
還有Util公共類,有三個靜態方法。
public class Util {
/**
* 獲取文件名稱
* @param filename
* @return
*/
public static String getFileName(String filename){
int start=filename.lastIndexOf("/");
// int end=filename.lastIndexOf(".");
if(start!=-1){
return filename.substring(start+1,filename.length());
}else{
return null;
}
}
/**
* 拼接公共參數
* @param url
* @param commonField
* @return
*/
public static String getMosaicParameter(String url, List<NameValuePair> commonField){
if (TextUtils.isEmpty(url))
return "";
if (url.contains("?")) {
url = url + "&";
} else {
url = url + "?";
}
url += getCommonFieldString(commonField);
return url;
}
private static String getCommonFieldString(List<NameValuePair> commonField){
StringBuffer sb = new StringBuffer();
try{
int i=0;
for (NameValuePair item:commonField) {
if(i>0){
sb.append("&");
}
sb.append(item.getName());
sb.append('=');
sb.append(URLEncoder.encode(item.getValue(),"utf-8"));
i++;
}
}catch (Exception e){
}
return sb.toString();
}
}