編輯:關於Android編程
一、構建思路
1、構建一個Request用來封裝 HTTP請求的類型、請求頭參數、請求體、優先級、返回類型、等一些必要的屬性。 這個Request定義為抽象的,使得用戶可以擴展。
2、構建一個隊列(BlockingQueue) 用來存貯這些請求,用戶可以自己將請求添加到這個隊列中
3、創建多個線程NetworkExecutor,用來遍歷隊列(BlockingQueue)獲得Request,請求傳遞給 一個專門用來發送HTTP請求的類HttpStack
4、創建 HttpStack的實現類用來發送Http請求
5、HttpStack將請求結果分裝傳遞返回給NetworkExecutor,然後NetworkExecute調用ResponseDelivery.deliveryResponse(Response response) 將結果 轉換到UI線程,
最終將結果告知用戶。

二、實戰
1、構建Request
創建一個Request < T > 其中T 參數為返回的類型,可以為String,Json對象 或者xml對象,可由用戶擴展設置。
package com.blueberry.sample.module.http; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; /** * Created by blueberry on 2016/8/16. * 網絡請求類,注意GET和DELETE不能傳遞請求參數,因為其請求的性質所致,用戶可以將參數構建到URL後傳進到Request * 中。 */ public abstract class Requestimplements Comparable > { //默認的編碼格式 private static final String DEFAULT_PARAMS_ENCODING = "UTF-8"; public static final String HEADER_CONTENT_TYPE = "Content-Type"; //請求序列紅啊 protected int mSerialNum = 0; //優先級默認為NORMAL protected Priority mPriority = Priority.NORMAL; //是否取消該請求 protected boolean isCancel = false; //請求是否應該緩存 private boolean mShouldCache = true; //請求Listener protected RequestListener mRequestListener; //請求的URL private String mUrl = ""; //請求的方法 HttpMethod mHttpMethod = HttpMethod.GET; //請求的header private Map mHeader = new HashMap<>(); //請求參數 private Map mBodyParams = new HashMap<>(); public Request(HttpMethod httpMethod, String url, RequestListener listener) { mHttpMethod = httpMethod; mUrl = url; mRequestListener = listener; } //從原生的網絡請求中解析結果,子類必須覆寫 public abstract T parseResponse(Response response); //處理Response ,該方法需要運行在UI線程 public final void deliveryResponse(Response response) { //解析得到請求結果 T result = parseResponse(response); if (mRequestListener != null) { int stCode = response != null ? response.getStatusCode() : -1; String msg = response != null ? response.getMessage() : "unknown error"; mRequestListener.onComplete(stCode, result, msg); } } protected String getParamsEncoding() { return DEFAULT_PARAMS_ENCODING; } public String getBodyContentType() { return "application/x-www-form-urlencoded;charset=" + getParamsEncoding(); } //返回POST或者PUT請求時的Body參數字節數組 public byte[] getBody() { Map params = getParams(); if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; } //將參數轉換為 URL編碼的參數串,格式key1=value&key2=value2 private byte[] encodeParameters(Map params, String paramsEncoding) { StringBuilder encodeParams = new StringBuilder(); try { for (Map.Entry entry : params.entrySet()) { encodeParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); encodeParams.append('='); encodeParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); encodeParams.append('&'); } return encodeParams.toString().getBytes(paramsEncoding); } catch (Exception e) { throw new RuntimeException("Encoding not supported: " + paramsEncoding, e); } } //用於對象的排序處理,根據優先級加入到隊列的序號進行排序 @Override public int compareTo(Request another) { Priority myPriority = this.getPriority(); Priority anotherPriority = another.getPriority(); return myPriority.equals(anotherPriority) ? this.getSerialNumber() - another.getSerialNumber() : myPriority.ordinal() - anotherPriority.ordinal(); } public Map getParams() { return mBodyParams; } public Priority getPriority() { return mPriority; } public int getSerialNumber() { return mSerialNum; } public void setSerialNumber(int serialNumber) { this.mSerialNum = serialNumber; } public void setShouldCache(boolean shouldCache) { this.mShouldCache = shouldCache; } public String getUrl() { return mUrl; } public boolean shouldCache(){ return mShouldCache; } public Map getHeaders() { return mHeader; } public HttpMethod getHttpMethod() { return mHttpMethod; } public static enum HttpMethod { GET("GET"), POST("POST"), PUT("PUT"), DELETE("DELETE"); private String mHttpMethod = ""; private HttpMethod(String method) { this.mHttpMethod = method; } @Override public String toString() { return mHttpMethod; } } public static enum Priority { LOW, NORMAL, HIGH, IMMEDIATE } public static interface RequestListener { //請求完成回調 public void onComplete(int stCode, T response, String errMsg); } }
它的實現:
package com.blueberry.sample.module.http; /** * Created by blueberry on 2016/8/16. */ public class StringRequest extends Request{ public StringRequest(HttpMethod httpMethod, String url, RequestListener listener) { super(httpMethod, url, listener); } @Override public String parseResponse(Response response) { return new String(response.getRawData()); } }
或者:
package com.blueberry.sample.module.http; import org.json.JSONException; import org.json.JSONObject; /** * Created by blueberry on 2016/8/16. */ public class JsonRequest extends Request{ public JsonRequest(HttpMethod httpMethod, String url, RequestListener listener) { super(httpMethod, url, listener); } @Override public JSONObject parseResponse(Response response) { String jsonString = new String(response.getRawData()); try { return new JSONObject(jsonString); } catch (JSONException e) { e.printStackTrace(); return null; } } }
即重寫parseResponse 將或返回的數據轉化成對應的類型。
2、構建請求隊列
package com.blueberry.sample.module.http;
import android.util.Log;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by blueberry on 2016/8/16.
*/
public final class RequestQueue {
private static final String TAG = "RequestQueue";
//線程安全的請求隊列
private BlockingQueue> mRequestQueue = new PriorityBlockingQueue<>();
//請求的序列化生成器
private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);
//默認的核心數 為CPU 個數+1
public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors()+1;
// cpu核心數+1分發線程
private int mDispatchNums = DEFAULT_CORE_NUMS;
//NetworkExecutor[],執行網絡請求的線程
private NetworkExecutor[] mDispatchers =null;
// Http 請求的真正執行者
private HttpStack mHttpStack;
protected RequestQueue(int coreNums,HttpStack httpStack){
mDispatchNums = coreNums;
mHttpStack = httpStack!=null?httpStack:HttpStackFactory.createHttpStack();
}
// 啟動NetworkExecutor
private final void startNetworkExecutors(){
mDispatchers = new NetworkExecutor[mDispatchNums];
for (int i = 0; i < mDispatchNums; i++) {
mDispatchers[i] = new NetworkExecutor(mRequestQueue,mHttpStack);
mDispatchers[i].start();
}
}
public void start(){
stop();
startNetworkExecutors();
}
/**
* 停止NetworkExecutor
*/
private void stop() {
if(mDispatchers!=null && mDispatchers.length>0){
for (int i = 0; i < mDispatchers.length; i++) {
mDispatchers[i].quit();
}
}
}
public void addRequest(Request request){
if(!mRequestQueue.contains(request)){
//為請求設置序列號
request.setSerialNumber(this.generateSerialNumber());
mRequestQueue.add(request);
}else{
Log.d(TAG,"請求隊列中已經含有了") ;
}
}
private int generateSerialNumber() {
return mSerialNumGenerator.incrementAndGet();
}
}
其中NetworkExecutor 是一個線程,隊列開始時會有 cpu核數+1 個線程請求BlockingQueue< Request >這個隊列
3、NetworkExecutor
package com.blueberry.sample.module.http;
import android.util.Log;
import java.util.concurrent.BlockingQueue;
/**
* Created by blueberry on 2016/8/16.
*/
public class NetworkExecutor extends Thread {
private static final String TAG = "NetworkExecutor";
//網絡請求隊列
private BlockingQueue> mRequestQueue;
//網絡請求棧
private HttpStack mHttpStack;
//結果分發器,將結果投遞到主線程
private static ResponseDelivery mResponseDelivery = new ResponseDelivery();
//請求緩存
private static Cache mReqCache = new Cache.LruMemCache();
//是否停止
private boolean isStop = false;
public NetworkExecutor(BlockingQueue> mRequestQueue, HttpStack mHttpStack) {
this.mRequestQueue = mRequestQueue;
this.mHttpStack = mHttpStack;
}
@Override
public void run() {
try {
while (!isStop) {
final Request request = mRequestQueue.take();
if (request.isCancel) {
continue;
}
Response response = null;
if (isUseCache(request)) {
//從緩存中讀取
response = mReqCache.get(request.getUrl());
} else {
// 從網絡上獲取數據
response = mHttpStack.performRequest(request);
//如果該請求需要緩存,那麼請求成功緩存到mResponseCache中
if (request.shouldCache() && isSuccess(response)) {
mReqCache.put(request.getUrl(), response);
}
}
mResponseDelivery.deliveryResponse(request, response);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private boolean isSuccess(Response response) {
return response != null && response.getStatusCode() == 200;
}
private boolean isUseCache(Request request) {
return request.shouldCache() && mReqCache.get(request.getUrl()) != null;
}
public void quit() {
isStop =true;
}
}
這裡加了一個緩存,如果使用緩存存在,就讀取緩存中的,負責使用mHttpStack.performRequest(request);請求數據,然後通過 mResponseDelivery.deliveryResponse(request, response);將請求結果切換到主線程
4、Cache
package com.blueberry.sample.module.http; import android.annotation.TargetApi; import android.os.Build; import android.util.Log; import android.util.LruCache; /** * Created by blueberry on 2016/8/16. */ public interface Cache{ V get(K key); void put(K key,V value); @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) class LruMemCache implements Cache { private static final String TAG = "LruMemCache"; private LruCache cache = new LruCache ((int) (Runtime.getRuntime() .freeMemory() / 10)) { @Override protected int sizeOf(K key, V value) { return value.getRawData().length; } }; @Override public V get(K key) { Log.i(TAG, "get: "); return cache.get(key); } @Override public void put(K key, V value) { cache.put(key,value); } } }
5、HttpStack
public interface HttpStack {
Response performRequest(Request request);
}
它有2個實現 HttpClientStack和HttpUrlStack,其中在android2.3 之後使用HttpUrlStack。
因為 HttpClientStack使用 apache 的HttpClient ,HttpUrlStack使用HttpUrlConnection,因為在2.3之後google推薦HttpUrlConnection,所以我們根據平台的版本選擇合適的實現。
package com.blueberry.sample.module.http;
import android.os.Build;
/**
* Created by blueberry on 2016/8/16.
*/
public class HttpStackFactory {
private static final int GINGERBREAD_SDK_NUM =9;
public static HttpStack createHttpStack() {
int runtimeSDKApi = Build.VERSION.SDK_INT ;
if(runtimeSDKApi>=GINGERBREAD_SDK_NUM){
return new HttpUrlConnStack();
}
return new HttpClientStack();
}
}
package com.blueberry.sample.module.http;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Created by blueberry on 2016/8/16.
*/
public class HttpUrlConnStack implements HttpStack {
private Config mConfig = new Config.Builder().build();
@Override
public Response performRequest(Request request) {
HttpURLConnection urlConnection = null;
//構建HttpURLConnection
try {
urlConnection = createUrlConnection(request.getUrl());
//設置headers
setRequestHeaders(urlConnection, request);
//設置Body參數
setRequestParams(urlConnection, request);
return fetchResponse(urlConnection);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Response fetchResponse(HttpURLConnection urlConnection) throws IOException {
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = urlConnection.getResponseCode();
if (responseCode == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
// 狀態行數據
StatusLine responseStatus = new BasicStatusLine(protocolVersion, responseCode, urlConnection
.getResponseMessage());
// 構建response.
Response response = new Response(responseStatus);
//設置 response數據
response.setEntity(entityFromURLConnection(urlConnection));
addHeaderToResponse(response, urlConnection);
return response;
}
private void addHeaderToResponse(Response response, HttpURLConnection urlConnection) {
for (Map.Entry> header : urlConnection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
}
private HttpEntity entityFromURLConnection(HttpURLConnection urlConnection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream = null;
try {
inputStream = urlConnection.getInputStream();
} catch (IOException e) {
e.printStackTrace();
inputStream = urlConnection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(urlConnection.getContentLength());
entity.setContentEncoding(urlConnection.getContentEncoding());
entity.setContentType(urlConnection.getContentType());
return entity;
}
private void setRequestParams(HttpURLConnection urlConnection, Request request)
throws IOException {
Request.HttpMethod method = request.getHttpMethod();
urlConnection.setRequestMethod(method.toString());
// add params
byte[] body = request.getBody();
if (body != null) {
urlConnection.setDoOutput(true);
// set content type
urlConnection.addRequestProperty(Request.HEADER_CONTENT_TYPE,
request.getBodyContentType());
// write params data to connection.
DataOutputStream dataOutputStream =
new DataOutputStream(urlConnection.getOutputStream());
dataOutputStream.write(body);
dataOutputStream.close();
}
}
private void setRequestHeaders(HttpURLConnection urlConnection, Request request) {
Set headersKeys = request.getHeaders().keySet();
for (String headerName : headersKeys) {
urlConnection.addRequestProperty(headerName, request.getHeaders().get(headerName));
}
}
private HttpURLConnection createUrlConnection(String url) throws IOException {
URL newURL = new URL(url);
URLConnection urlConnection = newURL.openConnection();
urlConnection.setConnectTimeout(mConfig.connTimeOut);
urlConnection.setReadTimeout(mConfig.soTimeOut);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(true);
return (HttpURLConnection) urlConnection;
}
}
6、將結果切換到主線程。
package com.blueberry.sample.module.http;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
/**
* Created by blueberry on 2016/8/16.
* 請求結果投遞類,將請求結果投遞給UI線程
*/
public class ResponseDelivery implements Executor{
/*關聯主線程消息隊列的handler*/
Handler mResponseHandler = new Handler(Looper.getMainLooper());
public void deliveryResponse(final Request request, final Response response) {
Runnable respRunnable = new Runnable() {
@Override
public void run() {
request.deliveryResponse(response);
}
};
execute(respRunnable);
}
@Override
public void execute(Runnable command) {
mResponseHandler.post(command);
}
}
最終將結果使用Request的工具方法deliveryResponse 轉化對應的實現,通知監聽器:
//處理Response ,該方法需要運行在UI線程
public final void deliveryResponse(Response response) {
//解析得到請求結果
T result = parseResponse(response);
if (mRequestListener != null) {
int stCode = response != null ? response.getStatusCode() : -1;
String msg = response != null ? response.getMessage() : "unknown error";
mRequestListener.onComplete(stCode, result, msg);
}
}
8、Response
package com.blueberry.sample.module.http;
import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.ReasonPhraseCatalog;
import org.apache.http.StatusLine;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.Locale;
/**
* Created by blueberry on 2016/8/16.
*/
public class Response extends BasicHttpResponse {
//原始的response 主體數據
public byte[] rawData = new byte[0];
public Response(StatusLine statusline, ReasonPhraseCatalog catalog, Locale locale) {
super(statusline, catalog, locale);
}
public Response(StatusLine statusline) {
super(statusline);
}
public Response(ProtocolVersion ver, int code, String reason) {
super(ver, code, reason);
}
public void setEntity(HttpEntity entity){
super.setEntity(entity);
rawData = entityToBytes(getEntity());
}
private byte[] entityToBytes(HttpEntity entity) {
try {
return EntityUtils.toByteArray(entity);
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
}
public byte[] getRawData() {
return rawData;
}
public String getMessage() {
return getReason(getStatusLine().getStatusCode());
}
public int getStatusCode() {
return getStatusLine().getStatusCode();
}
}
9、調用程序
private void setGetRequest() {
StringRequest stringRequest = new StringRequest(Request.HttpMethod.GET,
"http://www.baidu.com",
new Request.RequestListener() {
@Override
public void onComplete(int stCode, String response, String errMsg) {
Logger.i("code = %d, response= %s, errMsg= %s", stCode, response, errMsg);
}
});
stringRequest.setShouldCache(true);
RequestQueue mQueue = NetManager.newRequestQueue();
mQueue.addRequest(stringRequest);
mQueue.start();
}
11、上傳文件
我們構建一個上傳文件的Request
package com.blueberry.sample.module.http; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * Created by blueberry on 2016/8/16. */ public class MultipartRequest extends Request{ MultipartEntity multipartEntity = new MultipartEntity(); public MultipartRequest(HttpMethod httpMethod, String url, RequestListener listener) { super(HttpMethod.POST, url, listener); } public MultipartEntity getMultipartEntity() { return multipartEntity; } @Override public String parseResponse(Response response) { if(response!=null && response.getRawData()!=null) return new String (response.getRawData()); return null; } @Override public byte[] getBody() { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { multipartEntity.writeTo(bos); } catch (IOException e) { e.printStackTrace(); } return bos.toByteArray(); } @Override public String getBodyContentType() { return multipartEntity.getContentType().getValue(); } }
MultipartEntity的實現:
package com.blueberry.sample.module.http;
import android.text.TextUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.message.BasicHeader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Random;
/**
* Created by blueberry on 2016/8/16.
*
* 最終生成的報文格式大致如下:
*
* POST /api/ HTTP/1.1
* Content-Type: multipart/form-data; boundary=03fdafareqjk2-5542jkfda
* User-Agent:Dalvik/1.6.0 (Linux;U anroid 4.4.4;M040 Build/KTU84P)
* Host: www.myhost.com
* Connection: Keep:Alive
* Accept-Encoding: gzip
* Content-Length:168324
*
* --03fdafareqjk2-5542jkfda
* Content-Type: text/plain;charset=UTF-8
* Content-Disposition: form-data;name="type"
* Content-Transfer-Encoding: 8bit
*
* This is my type
*
* --03fdafareqjk2-5542jkfda
* Content-Type: application/octet-stream
* Content-Disposition: form-data; name="image";filename="no-file"
* Content-Transfer-Encoding:binary
*
* --03fdafareqjk2-5542jkfda
* Content-Type: application/octet-stream
* Content-Disposition: form-data; name="file";filename="image.jpg"
* Content-Transfer-Encoding:binary
*
* --03fdafareqjk2-5542jkfda--
*/
public class MultipartEntity implements HttpEntity {
private final static char[] MULTIPART_CHARS = ("-123456789abcdefghihkl" +
"mnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ").toCharArray();
// 回車符和換行符
private final String NEW_LINE_STR = "\r\n";
private final String CONTENT_TYPE = "Content-Type: ";
private final String CONTENT_DISPOSITION = "Content-Disposition: ";
// 文本參數和字符集
private final String TYPE_TEXT_CHARSET = "text/plain; charset=UTF-8";
// 字節流參數
private final String TYPE_OCTET_STREAM = "application/octet-stream";
// 字節數組參數
private final byte[] BINARY_ENCODING = "Content-Transfer-Encoding: binary\r\n\r\n".getBytes();
// 文本參數
private final byte[] BIT_ENCODING = "Content-Transfer-Encoding: 8bit\r\n\r\n".getBytes();
// 參數分割符
private String mBoundary = null;
// 輸出流,用於緩存參數數據
ByteArrayOutputStream mOutputStream = new ByteArrayOutputStream();
public MultipartEntity() {
this.mBoundary = generateBoundary();
}
private String generateBoundary() {
final StringBuffer buf = new StringBuffer();
final Random rand = new Random();
for (int i = 0; i < 30; i++) {
buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
}
return buf.toString();
}
// 參數開頭的分割符
private void writeFirstBoundary() throws IOException {
mOutputStream.write(("--" + mBoundary + "\r\n").getBytes());
}
// 添加文本參數
public void addStringPart(final String paramName, final String value) {
writeToOutputString(paramName, value.getBytes(), TYPE_TEXT_CHARSET, BIT_ENCODING, "");
}
/**
* 添加字節數組參數,例如Bitmap的字節流參數
* @param paramsName 參數名
* @param rawData 字節數組數據
*/
public void addByteArrayPart(String paramsName,final byte[] rawData){
writeToOutputString(paramsName,rawData,TYPE_OCTET_STREAM,BINARY_ENCODING,"no-file");
}
/**
* 添加文件參數,可以實現文件上傳功能
* @param key 參數名
* @param file 文件參數
*/
public void addFilePart(final String key,final File file){
InputStream fin =null;
try {
fin = new FileInputStream(file);
writeFirstBoundary();
final String type = CONTENT_TYPE+TYPE_OCTET_STREAM+NEW_LINE_STR;
mOutputStream.write(type.getBytes());
mOutputStream.write(getContentDispositionBytes(key,file.getName()));
mOutputStream.write(BINARY_ENCODING);
final byte[] tmp= new byte[4096];
int len = 0;
while((len=fin.read(tmp))!=-1){
mOutputStream.write(tmp,0,len);
}
mOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
closeSilently(fin);
}
}
private void closeSilently(InputStream fin) {
if(fin!=null) try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 將數據寫出到輸出流中
*
* @param key 參數名
* @param rawData 原始的字節數據
* @param type 類型
* @param encodingBytes 編碼類型
* @param fileName 文件名
*/
private void writeToOutputString(String key, byte[] rawData, String type,
byte[] encodingBytes, String fileName) {
try {
writeFirstBoundary();
mOutputStream.write(getContentDispositionBytes(key, fileName));
mOutputStream.write((CONTENT_TYPE + type + NEW_LINE_STR).getBytes());
mOutputStream.write(encodingBytes);
mOutputStream.write(rawData);
mOutputStream.write(NEW_LINE_STR.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private byte[] getContentDispositionBytes(String key, String fileName) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(CONTENT_DISPOSITION + "form-data;name=\"" + key + "\"");
// 文本參數沒有filename參數 ,設置為空即可
if (!TextUtils.isEmpty(fileName)) {
stringBuilder.append("; filename=\"" + fileName + "\"");
}
return stringBuilder.append(NEW_LINE_STR).toString().getBytes();
}
@Override
public boolean isRepeatable() {
return false;
}
@Override
public boolean isChunked() {
return false;
}
@Override
public long getContentLength() {
return mOutputStream.toByteArray().length;
}
@Override
public Header getContentType() {
return new BasicHeader("Content-Type","multipart/form-data; boundary="+mBoundary);
}
@Override
public Header getContentEncoding() {
return null;
}
@Override
public InputStream getContent() throws IOException, IllegalStateException {
return new ByteArrayInputStream(mOutputStream.toByteArray());
}
@Override
public void writeTo(OutputStream outputStream) throws IOException {
final String endString = "\r\n--"+mBoundary+"--\r\n";
//寫入結束符
// mOutputStream.write(endString.getBytes());
// 將緩存在mOutputStream 中的數據全部寫入到outputStream中
outputStream.write(mOutputStream.toByteArray());
outputStream.write(endString.getBytes());
outputStream.flush();
}
@Override
public boolean isStreaming() {
return false;
}
@Override
public void consumeContent() throws IOException {
if(isStreaming()){
throw new UnsupportedEncodingException("Streaming" +
" entity dose not implement #consumeContent()");
}
}
}
上傳文件類似於html中的表單提交,(請求頭為 Content-Type: form-data)
最終生成的請求報文中 每個part都使用一個 boundary來分割
上傳文件調用程序:
RequestQueue mQueue = NetManager.newRequestQueue();
MultipartRequest multipartRequest = new MultipartRequest(Request.HttpMethod.POST,
"http://192.168.10.142:8080/WebTest/hello", new Request.RequestListener() {
@Override
public void onComplete(int stCode, String response, String errMsg) {
Logger.i("code = %d, response= %s, errMsg= %s", stCode, response, errMsg);
}
});
multipartRequest.setShouldCache(false);
MultipartEntity multipartEntity = multipartRequest.getMultipartEntity();
multipartEntity.addFilePart("imgFile",new File(getExternalCacheDir().getPath(),"test.jpg"));
// 4.將請求添加到隊列中
mQueue.addRequest(multipartRequest);
mQueue.start();
12、服務端接收
package com.blueberry.example;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Part;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by Administrator on 2016/8/16.
*/
@MultipartConfig
@WebServlet(name = "MultipartServlet", urlPatterns = "/hello")
public class MultipartServlet extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException, IOException {
System.out.print("post 請求");
request.setCharacterEncoding("UTF-8");
Part part = request.getPart("imgFile");
//格式如:form-data; name="upload"; filename="YNote.exe"
String disposition = part.getHeader("content-disposition");
System.out.println(disposition);
String fileName = disposition.substring(disposition.lastIndexOf("=") + 2, disposition.length() - 1);
String fileType = part.getContentType();
long fileSize = part.getSize();
System.out.println("fileName: " + fileName);
System.out.println("fileType: " + fileType);
System.out.println("fileSize: " + fileSize);
// String uploadPath = request.getServletContext().getRealPath("/upload");
// System.out.println("uploadPath" + uploadPath);
// part.write(uploadPath + File.separator + fileName);
FileOutputStream fos =null;
InputStream inputStream = null;
inputStream = part.getInputStream();
File targetFile = new File("E:/test/upload", fileName);
if (!targetFile.getParentFile().exists()){
targetFile.getParentFile().mkdirs();
}
fos = new FileOutputStream(targetFile);
int len ;
byte[] bytes = new byte[8096];
while ((len= inputStream.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.flush();
fos.close();
inputStream.close();
}
protected void doGet(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException, IOException {
System.out.print("Get 請求");
}
}
Android實現環形進度條的實例
Android實現環形進度條的效果圖如下:自定義控件:AttendanceProgressBar代碼如下:public class AttendanceProgressB
Android Tab -- 使用ViewPager、PagerAdapter來實現
效果:滑動切換,自動切換。 代碼:https://github.com/ldb-github/Layout_Tab1、布局界面通過ViewPager標簽來實現視
Android 對話框(Dialog) 及 自定義Dialog
Activities提供了一種方便管理的創建、保存、回復的對話框機制,例如 onCreateDialog(int), onPrepareDialog(int, Dialo
《Android源碼設計模式解析與實戰》讀書筆記(二十二)
第二十二章、享元模式 享元模式是結構型設計模式之一,是對對象池的一種實現。就像它的名字一樣,共享對象,避免重復的創建。我們常用的String 就是使用了共享模式,所以St