編輯:關於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實現環形進度條的效果圖如下:自定義控件:AttendanceProgressBar代碼如下:public class AttendanceProgressB
效果:滑動切換,自動切換。 代碼:https://github.com/ldb-github/Layout_Tab1、布局界面通過ViewPager標簽來實現視
Activities提供了一種方便管理的創建、保存、回復的對話框機制,例如 onCreateDialog(int), onPrepareDialog(int, Dialo
第二十二章、享元模式 享元模式是結構型設計模式之一,是對對象池的一種實現。就像它的名字一樣,共享對象,避免重復的創建。我們常用的String 就是使用了共享模式,所以St