");
164 | mResultReceived = true;
165 | mResult = result;
166 | notifyAll();
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/ByteArrayPool.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.Comparator;
6 | import java.util.LinkedList;
7 | import java.util.List;
8 |
9 | /**
10 | * ByteArrayPool is a source and repository of byte[] objects. Its
11 | * purpose is to supply those buffers to consumers who need to use them for a
12 | * short period of time and then dispose of them. Simply creating and disposing
13 | * such buffers in the conventional manner can considerable heap churn and
14 | * garbage collection delays on Android, which lacks good management of
15 | * short-lived heap objects. It may be advantageous to trade off some memory in
16 | * the form of a permanently allocated pool of buffers in order to gain heap
17 | * performance improvements; that is what this class does.
18 | *
19 | * A good candidate user for this class is something like an I/O system that
20 | * uses large temporary byte[] buffers to copy data around. In
21 | * these use cases, often the consumer wants the buffer to be a certain minimum
22 | * size to ensure good performance (e.g. when copying data chunks off of a
23 | * stream), but doesn't mind if the buffer is larger than the minimum. Taking
24 | * this into account and also to maximize the odds of being able to reuse a
25 | * recycled buffer, this class is free to return buffers larger than the
26 | * requested size. The caller needs to be able to gracefully deal with getting
27 | * buffers any size over the minimum.
28 | *
29 | * If there is not a suitably-sized buffer in its recycling pool when a buffer
30 | * is requested, this class will allocate a new buffer and return it.
31 | *
32 | * This class has no special ownership of buffers it creates; the caller is free
33 | * to take a buffer it receives from this pool, use it permanently, and never
34 | * return it to the pool; additionally, it is not harmful to return to this pool
35 | * a buffer that was allocated elsewhere, provided there are no other lingering
36 | * references to it.
37 | *
38 | * This class ensures that the total size of the buffers in its recycling pool
39 | * never exceeds a certain byte limit. When a buffer is returned that would
40 | * cause the pool to exceed the limit, least-recently-used buffers are disposed.
41 | */
42 | public class ByteArrayPool {
43 | /** The buffer pool, arranged both by last use and by buffer size */
44 | private final List mBuffersByLastUse = new LinkedList();
45 | private final List mBuffersBySize = new ArrayList(64);
46 |
47 | /** The total size of the buffers in the pool */
48 | private int mCurrentSize = 0;
49 |
50 | /**
51 | * The maximum aggregate size of the buffers in the pool. Old buffers are
52 | * discarded to stay under this limit.
53 | */
54 | private final int mSizeLimit;
55 |
56 | /** Compares buffers by size */
57 | protected static final Comparator BUF_COMPARATOR = new Comparator() {
58 | @Override
59 | public int compare(byte[] lhs, byte[] rhs) {
60 | return lhs.length - rhs.length;
61 | }
62 | };
63 |
64 | /**
65 | * @param sizeLimit
66 | * the maximum size of the pool, in bytes
67 | */
68 | private ByteArrayPool(int sizeLimit) {
69 | mSizeLimit = sizeLimit;
70 | }
71 |
72 | /** Singleton for this class. */
73 | private static ByteArrayPool mPool = new ByteArrayPool(4096);
74 |
75 | /** Get the singleton instance. */
76 | public static ByteArrayPool get() {
77 | return mPool;
78 | }
79 |
80 | /** Init and persisting the singleton instance. */
81 | public static void init(int poolSize) {
82 | mPool = new ByteArrayPool(poolSize);
83 | }
84 |
85 | /**
86 | * Returns a buffer from the pool if one is available in the requested size,
87 | * or allocates a new one if a pooled one is not available.
88 | *
89 | * @param len
90 | * the minimum size, in bytes, of the requested buffer. The
91 | * returned buffer may be larger.
92 | * @return a byte[] buffer is always returned.
93 | */
94 | public synchronized byte[] getBuf(int len) {
95 | for (int i = 0; i < mBuffersBySize.size(); i++) {
96 | byte[] buf = mBuffersBySize.get(i);
97 | if (buf.length >= len) {
98 | mCurrentSize -= buf.length;
99 | mBuffersBySize.remove(i);
100 | mBuffersByLastUse.remove(buf);
101 | return buf;
102 | }
103 | }
104 | return new byte[len];
105 | }
106 |
107 | /**
108 | * Returns a buffer to the pool, throwing away old buffers if the pool would
109 | * exceed its allotted size.
110 | *
111 | * @param buf
112 | * the buffer to return to the pool.
113 | */
114 | public synchronized void returnBuf(byte[] buf) {
115 | if (buf == null || buf.length > mSizeLimit) {
116 | return;
117 | }
118 | mBuffersByLastUse.add(buf);
119 | int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
120 | if (pos < 0) {
121 | pos = -pos - 1;
122 | }
123 | mBuffersBySize.add(pos, buf);
124 | mCurrentSize += buf.length;
125 | trim();
126 | }
127 |
128 | /**
129 | * Removes buffers from the pool until it is under its size limit.
130 | */
131 | private synchronized void trim() {
132 | while (mCurrentSize > mSizeLimit) {
133 | byte[] buf = mBuffersByLastUse.remove(0);
134 | mBuffersBySize.remove(buf);
135 | mCurrentSize -= buf.length;
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/dispatcher/NetworkDispatcher.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.dispatcher;
2 | import java.util.Timer;
3 | import java.util.TimerTask;
4 | import java.util.concurrent.BlockingQueue;
5 |
6 | import net.robinx.lib.http.RequestContext;
7 | import net.robinx.lib.http.base.Request;
8 | import net.robinx.lib.http.cache.CacheData;
9 | import net.robinx.lib.http.delivered.IDelivery;
10 | import net.robinx.lib.http.network.HttpError;
11 | import net.robinx.lib.http.network.HttpException;
12 | import net.robinx.lib.http.network.Network;
13 | import net.robinx.lib.http.response.NetworkResponse;
14 | import net.robinx.lib.http.response.Response;
15 | import net.robinx.lib.http.utils.CLog;
16 | import net.robinx.lib.http.utils.NetworkUtils;
17 |
18 | import android.annotation.TargetApi;
19 | import android.net.TrafficStats;
20 | import android.os.Build;
21 | import android.os.Process;
22 |
23 | /**
24 | * Provides a thread for performing network dispatch from a queue of requests.
25 | *@author Robin
26 | *@since 2015-05-08 12:30
27 | */
28 | public class NetworkDispatcher extends Thread {
29 | /** The queue of requests to service. */
30 | private final BlockingQueue> mQueue;
31 |
32 | private final Network mNetwork;
33 | private final IDelivery mDelivery;
34 |
35 | /** Used for telling us to die. */
36 | private volatile boolean mQuit = false;
37 |
38 |
39 | /**
40 | * Creates a new network dispatcher thread. You must call {@link #start()}
41 | * in order to begin processing.
42 | *
43 | * @param queue Queue of incoming requests for triage
44 | */
45 | public NetworkDispatcher(BlockingQueue> queue,Network network, IDelivery delivery) {
46 | mQueue = queue;
47 | mNetwork=network;
48 | mDelivery=delivery;
49 | }
50 |
51 | /**
52 | * Forces this dispatcher to quit immediately. If any requests are still in
53 | * the queue, they are not guaranteed to be processed.
54 | */
55 | public void quit() {
56 | mQuit = true;
57 | interrupt();
58 | }
59 |
60 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
61 | private void addTrafficStatsTag(Request> request) {
62 | // Tag the request (if API >= 14)
63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
64 | TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
65 | }
66 | }
67 |
68 | @Override
69 | public void run() {
70 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
71 | while (true) {
72 | final Request> request;
73 | try {
74 | // Take a request from the queue.
75 | request = mQueue.take();
76 | } catch (InterruptedException e) {
77 | // We may have been interrupted because it was time to quit.
78 | if (mQuit) {
79 | return;
80 | }
81 | continue;
82 | }
83 |
84 | try {
85 | CLog.d("network-queue-take");
86 |
87 | // If the request was cancelled already, do not perform the
88 | // network request.
89 | if (request.isCanceled()) {
90 | request.finish();
91 | CLog.e("cache-discard-canceled-----------cacheKey:"+request.getCacheKey());
92 | continue;
93 | }
94 |
95 | addTrafficStatsTag(request);
96 |
97 | //Reset the current request has not been paid for
98 | request.resetDelivered();
99 |
100 | //prepare to request
101 | mDelivery.postRequestPrepare(request);
102 |
103 | //if set "UseCacheDataWhenTimeout"
104 | if (request.getRequestCacheOptions().isUseCacheDataWhenTimeout()) {
105 | final CacheData> cacheData = request.getCache(request.getCacheKey());
106 | if (cacheData != null) {
107 | new Timer().schedule(new TimerTask() {
108 |
109 | @Override
110 | public void run() {
111 | //hand in main thread to call "onCacheDataLoadFinish"
112 | CLog.d("Time has come , Delivered:%s ",request.hasHadResponseDelivered());
113 | if (!request.hasHadResponseDelivered()) {
114 | mDelivery.postCacheResponse(request, cacheData);
115 | }
116 |
117 | }
118 | }, request.getRequestCacheOptions().getTimeController().getTimeout());
119 | }
120 |
121 | }
122 |
123 | // Perform the network request.
124 | if (NetworkUtils.checkNet(RequestContext.getInstance())){
125 | //request.doRequest();
126 | NetworkResponse networkResponse=mNetwork.performRequest(request);
127 | /*Response response=Response.success(networkResponse.data, networkResponse.headers);*/
128 | Response> response=request.parseNetworkResponse(networkResponse);
129 | mDelivery.postRequestResponse(request, response);
130 | }else{
131 | mDelivery.postError(request, new HttpException("No Network",HttpError.ERROR_NOT_NETWORK));
132 |
133 | }
134 |
135 | }catch (HttpException e) {
136 | CLog.e( "network-http-error NetworkDispatcher Unhandled exception : "+ e.toString());
137 | mDelivery.postError(request, e);
138 | } catch (Exception e) {
139 | CLog.e( "network-http-error NetworkDispatcher Unhandled exception : "+ e.toString());
140 | mDelivery.postError(request, new HttpException(e.getMessage()));
141 | }
142 | }
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/ex/hurl/RequestBodyConstants.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network.ex.hurl;
2 |
3 | import java.io.File;
4 | import java.nio.charset.Charset;
5 | import java.util.Map;
6 |
7 | /**
8 | * Constants for Request
9 | * @author Robin
10 | * @since 2016-01-07 17:01:11
11 | *
12 | */
13 | public class RequestBodyConstants {
14 |
15 | public static final String CRLF = "\r\n";
16 | public static final String HEADER_CONTENT_TYPE = "Content-Type";
17 | public static final String HEADER_USER_AGENT = "User-Agent";
18 | public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
19 | public static final String HEADER_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
20 | public static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; charset=%s; boundary=%s";
21 | public static final String CONTENT_TYPE_TEXT = "text/plain";
22 | public static final String BINARY = "binary";
23 | public static final String EIGHT_BIT = "8bit";
24 | public static final String FORM_DATA = "form-data; name=\"%s\"";
25 | public static final String BOUNDARY_PREFIX = "--";
26 | public static final String CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
27 | public static final String FILENAME = "filename=\"%s\"";
28 | public static final String COLON_SPACE = ": ";
29 | public static final String SEMICOLON_SPACE = "; ";
30 |
31 | public static final int CRLF_LENGTH = CRLF.getBytes().length;
32 | public static final int HEADER_CONTENT_DISPOSITION_LENGTH = HEADER_CONTENT_DISPOSITION.getBytes().length;
33 | public static final int COLON_SPACE_LENGTH = COLON_SPACE.getBytes().length;
34 | public static final int HEADER_CONTENT_TYPE_LENGTH = HEADER_CONTENT_TYPE.getBytes().length;
35 | public static final int CONTENT_TYPE_OCTET_STREAM_LENGTH = CONTENT_TYPE_OCTET_STREAM.getBytes().length;
36 | public static final int HEADER_CONTENT_TRANSFER_ENCODING_LENGTH = HEADER_CONTENT_TRANSFER_ENCODING.getBytes().length;
37 | public static final int BINARY_LENGTH = BINARY.getBytes().length;
38 | public static final int BOUNDARY_PREFIX_LENGTH = BOUNDARY_PREFIX.getBytes().length;
39 |
40 | public static final Charset ASCII = Charset.forName("US-ASCII");
41 |
42 | public static final byte[] CRLF_BYTES = getAsciiBytes(CRLF);
43 |
44 | /*public static int getContentLengthForMultipartRequest(String boundary, Map multipartParams, Map filesToUpload) {
45 | final int boundaryLength = boundary.getBytes().length;
46 | int contentLength = 0;
47 | for (String key : multipartParams.keySet()) {
48 | MultiPartRequest.MultiPartValue param = multipartParams.get(key);
49 | int size = boundaryLength +
50 | CRLF_LENGTH + HEADER_CONTENT_DISPOSITION_LENGTH + COLON_SPACE_LENGTH + String.format(FORM_DATA, key).getBytes().length +
51 | CRLF_LENGTH + HEADER_CONTENT_TYPE_LENGTH + COLON_SPACE_LENGTH + param.contentType.getBytes().length +
52 | CRLF_LENGTH + CRLF_LENGTH + param.value.getBytes().length + CRLF_LENGTH;
53 |
54 | contentLength += size;
55 | }
56 |
57 | for (String key : filesToUpload.keySet()) {
58 | File file = new File(filesToUpload.get(key));
59 | int size = boundaryLength +
60 | CRLF_LENGTH + HEADER_CONTENT_DISPOSITION_LENGTH + COLON_SPACE_LENGTH + String.format(FORM_DATA + SEMICOLON_SPACE + FILENAME, key, file.getName()).getBytes().length +
61 | CRLF_LENGTH + HEADER_CONTENT_TYPE_LENGTH + COLON_SPACE_LENGTH + CONTENT_TYPE_OCTET_STREAM_LENGTH +
62 | CRLF_LENGTH + HEADER_CONTENT_TRANSFER_ENCODING_LENGTH + COLON_SPACE_LENGTH + BINARY_LENGTH + CRLF_LENGTH + CRLF_LENGTH;
63 |
64 | size += (int) file.length();
65 | size += CRLF_LENGTH;
66 | contentLength += size;
67 | }
68 |
69 | int size = boundaryLength + BOUNDARY_PREFIX_LENGTH + CRLF_LENGTH;
70 | contentLength += size;
71 | return contentLength;
72 | }*/
73 |
74 | public static int getContentLength(String boundary, Map, ?> requestParams) {
75 | final int boundaryLength = boundary.getBytes().length;
76 | int contentLength = 0;
77 | for (Object key : requestParams.keySet()) {
78 | Object value = requestParams.get(key);
79 | if (value instanceof File) {
80 | File fileValue = (File) value;
81 | int size = boundaryLength +
82 | CRLF_LENGTH + HEADER_CONTENT_DISPOSITION_LENGTH + COLON_SPACE_LENGTH + String.format(FORM_DATA + SEMICOLON_SPACE + FILENAME, key, fileValue.getName()).getBytes().length +
83 | CRLF_LENGTH + HEADER_CONTENT_TYPE_LENGTH + COLON_SPACE_LENGTH + CONTENT_TYPE_OCTET_STREAM_LENGTH +
84 | CRLF_LENGTH + HEADER_CONTENT_TRANSFER_ENCODING_LENGTH + COLON_SPACE_LENGTH + BINARY_LENGTH + CRLF_LENGTH + CRLF_LENGTH;
85 |
86 | size += (int) fileValue.length();
87 | size += CRLF_LENGTH;
88 | contentLength += size;
89 | }else {
90 | String stringValue = "";
91 | if (value instanceof String) {
92 | stringValue = (String) value;
93 | }else if (value instanceof Integer) {
94 | stringValue = (Integer) value +"";
95 | }
96 | int size = boundaryLength +
97 | CRLF_LENGTH + HEADER_CONTENT_DISPOSITION_LENGTH + COLON_SPACE_LENGTH + String.format(FORM_DATA, key).getBytes().length +
98 | CRLF_LENGTH + HEADER_CONTENT_TYPE_LENGTH + COLON_SPACE_LENGTH +
99 | // param.contentType.getBytes().length +
100 | CRLF_LENGTH + CRLF_LENGTH + stringValue.getBytes().length + CRLF_LENGTH;
101 |
102 | contentLength += size;
103 | }
104 | }
105 |
106 | int size = boundaryLength + BOUNDARY_PREFIX_LENGTH + CRLF_LENGTH;
107 | contentLength += size;
108 | return contentLength;
109 | }
110 |
111 | public static byte[] getAsciiBytes(final String data) {
112 | notNull(data, "Input");
113 | return data.getBytes(ASCII);
114 | }
115 |
116 | private static T notNull(final T argument, final String name) {
117 | if (argument == null) {
118 | throw new IllegalArgumentException(name + " may not be null");
119 | }
120 | return argument;
121 | }
122 |
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/ex/RequestParams.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network.ex;
2 |
3 | import android.text.TextUtils;
4 |
5 | import net.robinx.lib.http.utils.CLog;
6 |
7 | import java.io.File;
8 | import java.io.UnsupportedEncodingException;
9 | import java.net.URLEncoder;
10 | import java.util.Map;
11 | import java.util.concurrent.ConcurrentHashMap;
12 |
13 | /**
14 | * Request parameters
15 | *
16 | * @author Robin
17 | * @since 2016-01-08 14:21:27
18 | */
19 | public class RequestParams extends ConcurrentHashMap {
20 | private static final long serialVersionUID = 1L;
21 |
22 | private final Map mHeaders = new ConcurrentHashMap();
23 | private String mJsonParams;
24 |
25 | /*
26 | * =========================================================================
27 | * Constructor
28 | * =========================================================================
29 | */
30 |
31 | public RequestParams() {
32 | }
33 |
34 | public RequestParams(String cookie) {
35 | mHeaders.put("cookie", cookie);
36 | }
37 |
38 | /*
39 | * =========================================================================
40 | * Override Super
41 | * =========================================================================
42 | */
43 |
44 | @Override
45 | public Object put(String key, Object value) {
46 | if (value instanceof String || value instanceof Integer || value instanceof Long || value instanceof Short || value instanceof Float || value instanceof Double || value instanceof File) {
47 | return super.put(key, value);
48 | } else {
49 | CLog.e("Parameters need to be a file type or can be converted to string type");
50 | throw new IllegalArgumentException("Parameters need to be a file type or can be converted to string type");
51 | }
52 | }
53 |
54 | /*
55 | * =========================================================================
56 | * Public Method
57 | * =========================================================================
58 | */
59 |
60 | public void putParams(String key, int value) {
61 | this.putParams(key, String.valueOf(value));
62 | }
63 |
64 | public void putParams(String key, String value) {
65 | put(key, value);
66 | }
67 |
68 | public void putParams(String key, File value) {
69 | put(key, value);
70 | }
71 |
72 | public void putParams(String jsonString) {
73 | this.mJsonParams = jsonString;
74 | }
75 |
76 | public void putHeaders(String key, int value) {
77 | this.putHeaders(key, String.valueOf(value));
78 | }
79 |
80 | public void putHeaders(String key, long value) {
81 | this.putHeaders(key, String.valueOf(value));
82 | }
83 |
84 | public void putHeaders(String key, short value) {
85 | this.putHeaders(key, String.valueOf(value));
86 | }
87 |
88 | public void putHeaders(String key, float value) {
89 | this.putHeaders(key, String.valueOf(value));
90 | }
91 |
92 | public void putHeaders(String key, double value) {
93 | this.putHeaders(key, String.valueOf(value));
94 | }
95 |
96 | public void putHeaders(String key, String value) {
97 | mHeaders.put(key, value);
98 | }
99 |
100 | public String buildJsonParams() {
101 | return mJsonParams;
102 | }
103 |
104 | /**
105 | * Converts params into an application/x-www-form-urlencoded encoded string.
106 | */
107 | /*public StringBuilder buildParameters() {
108 | StringBuilder result = new StringBuilder();
109 | try {
110 | for (Entry entry : this.entrySet()) {
111 | Object value = entry.getValue();
112 | if (value == null) {
113 | continue;
114 | }
115 | if (value instanceof String || value instanceof Integer) {
116 | result.append("&");
117 | result.append(URLEncoder.encode(entry.getKey(), "utf-8"));
118 | result.append("=");
119 | result.append(URLEncoder.encode(String.valueOf(value), "utf-8"));
120 | } else {
121 | CLog.e("Filter value,Type : %s,Value : %s", value.getClass().getName());
122 | }
123 | }
124 | return result;
125 | } catch (UnsupportedEncodingException e) {
126 | throw new RuntimeException("Encoding not supported: " + "utf-8", e);
127 | }
128 |
129 | }*/
130 | public StringBuilder buildQueryParameters() {
131 | StringBuilder result = new StringBuilder();
132 | boolean isFirst = true;
133 | try {
134 | for (Entry entry : this.entrySet()) {
135 | Object value = entry.getValue();
136 | if (value == null) {
137 | continue;
138 | }
139 | if (value instanceof String || value instanceof Integer) {
140 | if (!isFirst) {
141 | result.append("&");
142 | } else {
143 | result.append("?");
144 | isFirst = false;
145 | }
146 | result.append(URLEncoder.encode(entry.getKey(), "utf-8"));
147 | result.append("=");
148 | result.append(URLEncoder.encode(String.valueOf(value), "utf-8"));
149 | } else {
150 | CLog.e("Filter value,Type : %s,Value : %s", value.getClass().getName());
151 | }
152 |
153 | }
154 | return result;
155 | } catch (UnsupportedEncodingException e) {
156 | throw new RuntimeException("Encoding not supported: " + "utf-8", e);
157 | }
158 |
159 | }
160 |
161 | public Map buildHeaders() {
162 | return mHeaders;
163 | }
164 |
165 | public boolean hasFileInParams() {
166 | for (Entry entry : this.entrySet()) {
167 | Object value = entry.getValue();
168 | if (value == null) {
169 | continue;
170 | }
171 | if (value instanceof File) {
172 | return true;
173 | }
174 | }
175 | return false;
176 | }
177 |
178 | public boolean hasJsonInParams() {
179 | return !TextUtils.isEmpty(mJsonParams);
180 | }
181 |
182 | @Override
183 | public String toString() {
184 | if (!TextUtils.isEmpty(mJsonParams)) {
185 | return mJsonParams;
186 | }
187 | return super.toString();
188 | }
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/dispatcher/CacheDispatcher.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.dispatcher;
2 |
3 | import java.util.concurrent.BlockingQueue;
4 |
5 | import net.robinx.lib.http.base.Request;
6 | import net.robinx.lib.http.cache.CacheData;
7 | import net.robinx.lib.http.delivered.IDelivery;
8 | import net.robinx.lib.http.utils.CLog;
9 |
10 | import android.os.Process;
11 |
12 | /**
13 | * Provides a thread for performing cache triage on a queue of requests.
14 | *@author Robin
15 | *@since 2015-05-08 16:20:45
16 | */
17 | public class CacheDispatcher extends Thread {
18 |
19 | /** The queue of requests coming in for triage. */
20 | private final BlockingQueue> mCacheQueue;
21 |
22 | /** The queue of requests going out to the network. */
23 | private final BlockingQueue> mNetworkQueue;
24 |
25 | /** Used for telling us to die. */
26 | private volatile boolean mQuit = false;
27 |
28 | private final IDelivery mDelivery;
29 |
30 | /* @SuppressLint("HandlerLeak")
31 | private Handler handler=new Handler(){
32 | @SuppressWarnings("unchecked")
33 | public void handleMessage(Message msg) {
34 | HashMap hashMap=(HashMap) msg.obj;
35 | CacheData cacheData = (CacheData) hashMap.get("data");
36 | Request> request=(Request>) hashMap.get("request");
37 |
38 | //Reset the current request has not been paid for
39 | request.resetDelivered();
40 |
41 | request.onCacheDataLoadFinish(cacheData);
42 | };
43 | };*/
44 |
45 | /**
46 | * Creates a new cache triage dispatcher thread. You must call {@link #start()}
47 | * in order to begin processing.
48 | *
49 | * @param cacheQueue Queue of incoming requests for triage
50 | * @param networkQueue Queue to post requests that require network to
51 | */
52 | public CacheDispatcher(BlockingQueue> cacheQueue, BlockingQueue> networkQueue,IDelivery delivery) {
53 | mCacheQueue = cacheQueue;
54 | mNetworkQueue = networkQueue;
55 | mDelivery=delivery;
56 | }
57 |
58 | /**
59 | * Forces this dispatcher to quit immediately. If any requests are still in
60 | * the queue, they are not guaranteed to be processed.
61 | */
62 | public void quit() {
63 | mQuit = true;
64 | interrupt();
65 | }
66 |
67 | @Override
68 | public void run() {
69 | CLog.v("start new dispatcher");
70 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
71 |
72 | while (true) {
73 | try {
74 | // Get a request from the cache triage queue, blocking until
75 | // at least one is available.
76 | final Request> request = mCacheQueue.take();
77 | CLog.d("cache-queue-take");
78 |
79 | // If the request has been canceled, don't bother dispatching it.
80 | if (request.isCanceled()) {
81 | request.finish();
82 | CLog.e( "cache-discard-canceled-----------cacheKey:"+request.getCacheKey());
83 | continue;
84 | }
85 | // use the cache data always
86 | if (request.getRequestCacheOptions().isUseCacheDataAnyway()) {
87 | CacheData> cacheData = request.getCache(request.getCacheKey());
88 | // Attempt to retrieve this item from cache.
89 | if (cacheData == null) {
90 | CLog.d("cache-miss");
91 | // Cache miss; send off to the network dispatcher.
92 | mNetworkQueue.put(request);
93 | continue;
94 | }
95 |
96 | // We have a cache hit; parse its data for delivery back to the request.
97 | CLog.d("cache-hit");
98 |
99 | //hand in main thread to call "onCacheDataLoadFinish"
100 | /*Message msg = handler.obtainMessage();
101 | HashMap hashMap=new HashMap<>();
102 | hashMap.put("data", cacheData);
103 | hashMap.put("request", request);
104 | msg.obj = hashMap;
105 | handler.sendMessage(msg);*/
106 |
107 | mDelivery.postCacheResponse(request, cacheData);
108 |
109 | mNetworkQueue.put(request);
110 |
111 | continue;
112 | }
113 |
114 | // use the cache data when the cache data is not expired
115 | if (request.getRequestCacheOptions().isUseCacheDataWhenUnexpired()) {
116 | CacheData> cacheData = request.getCache(request.getCacheKey());
117 | // Attempt to retrieve this item from cache.
118 | if ( cacheData == null) {
119 | CLog.d("cache-miss");
120 | // Cache miss; send off to the network dispatcher.
121 | mNetworkQueue.put(request);
122 | continue;
123 | }
124 |
125 | // If it is completely expired, just send it to the network.
126 | if (cacheData.isExpired()) {
127 | CLog.d("cache-hit-expired");
128 | //request.setCacheEntry(entry);
129 | mNetworkQueue.put(request);
130 | continue;
131 | }
132 |
133 | // We have a cache hit; parse its data for delivery back to the request.
134 | CLog.d("cache-hit");
135 |
136 | //hand in main thread to call "onCacheDataLoadFinish"
137 | /*Message msg = handler.obtainMessage();
138 | HashMap hashMap=new HashMap<>();
139 | hashMap.put("data", cacheData);
140 | hashMap.put("request", request);
141 | msg.obj = hashMap;
142 | handler.sendMessage(msg);*/
143 |
144 | mDelivery.postCacheResponse(request, cacheData);
145 |
146 | }else {
147 | mNetworkQueue.put(request);
148 | }
149 |
150 |
151 | } catch (InterruptedException e) {
152 | // We may have been interrupted because it was time to quit.
153 | if (mQuit) {
154 | return;
155 | }
156 | continue;
157 | }
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/ex/DownloadRequest.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network.ex;
2 |
3 | import android.text.TextUtils;
4 |
5 | import net.robinx.lib.http.callback.OnRequestListener;
6 | import net.robinx.lib.http.config.HttpMethod;
7 | import net.robinx.lib.http.config.Priority;
8 | import net.robinx.lib.http.config.RequestCacheOptions;
9 | import net.robinx.lib.http.response.NetworkResponse;
10 | import net.robinx.lib.http.response.Response;
11 | import net.robinx.lib.http.retry.DefaultRetryPolicyImpl;
12 | import net.robinx.lib.http.retry.RetryPolicy;
13 | import net.robinx.lib.http.utils.CLog;
14 |
15 | import java.io.File;
16 | import java.io.FileNotFoundException;
17 | import java.io.FileOutputStream;
18 | import java.io.IOException;
19 |
20 | /**
21 | * Download request
22 | *
23 | * @author Robin
24 | * @since 2016-1-14 18:53:03
25 | */
26 | public class DownloadRequest extends HttpRequest {
27 |
28 | private String mDownloadPath;
29 | private String mFileName;
30 |
31 | private DownloadRequest(Builder builder) {
32 | super();
33 | this.mDownloadPath = builder.downloadPath;
34 | this.mFileName = builder.fileName;
35 |
36 | super.mRequestCacheOptions = builder.requestCacheOptions;
37 | super.retryPolicy = builder.retryPolicy;
38 | super.onRequestListener = builder.onRequestListener;
39 | super.mPriority = builder.priority;
40 | super.httpMethod = builder.httpMethod;
41 | super.cacheKey = builder.cacheKey;
42 | super.mTag = builder.tag;
43 | super.mUrl = builder.url;
44 |
45 | if (builder.requestParams != null) {
46 | super.mRequestParams = builder.requestParams;
47 | }
48 | if (builder.body != null) {
49 | super.mBody = builder.body;
50 | super.mBody.setBytesWriteListener(super.getBytesWriteListener());
51 | }
52 |
53 | //handler default
54 | if (super.cacheKey == null) {
55 | super.cacheKey = builder.url;
56 | }
57 |
58 | if (super.mPriority == null) {
59 | setPriority(Priority.NORMAL);
60 | }
61 |
62 | if (super.retryPolicy == null) {
63 | setRetryPolicy(new DefaultRetryPolicyImpl(DefaultRetryPolicyImpl.DEFAULT_TIMEOUT_MS, DefaultRetryPolicyImpl.DEFAULT_MAX_RETRIES, DefaultRetryPolicyImpl.DEFAULT_BACKOFF_MULT));
64 | }
65 |
66 | if (super.mRequestCacheOptions == null) {
67 | setRequestCacheOptions(RequestCacheOptions.buildDefaultCacheOptions());
68 | }
69 |
70 | if (super.httpMethod == HttpMethod.GET) {
71 | super.mUrl = builder.url+super.mRequestParams.buildQueryParameters();
72 | } else {
73 | super.mUrl = builder.url;
74 | }
75 | }
76 |
77 | @Override
78 | public Response parseNetworkResponse(NetworkResponse response) {
79 | File downloadFile = null;
80 | try {
81 | byte[] data = response.data;
82 | //convert array of bytes into file
83 | File directory = new File(mDownloadPath);
84 | if (!directory.exists()) {
85 | directory.mkdir();
86 | }
87 |
88 | String path = mDownloadPath;
89 | if (!TextUtils.isEmpty(mFileName)) {
90 | path = mDownloadPath + File.separator + mFileName;
91 | }
92 | FileOutputStream fileOuputStream = new FileOutputStream(path);
93 | fileOuputStream.write(data);
94 | fileOuputStream.close();
95 | downloadFile = new File(path);
96 | } catch (FileNotFoundException e) {
97 | e.printStackTrace();
98 | CLog.e("Download directory %s is not exsit", mDownloadPath);
99 | } catch (IOException e) {
100 | e.printStackTrace();
101 | }
102 |
103 | return Response.success(downloadFile, response.headers);
104 | }
105 |
106 |
107 | /*
108 | * =========================================================================
109 | * Inner class
110 | * =========================================================================
111 | */
112 | public static class Builder implements net.robinx.lib.http.base.Builder> {
113 |
114 | //Local property
115 | private final String downloadPath;
116 | private final String fileName;
117 |
118 | //HttpRequest property
119 | private RequestParams requestParams;
120 | private Body body;
121 |
122 | //Request property
123 | private RequestCacheOptions requestCacheOptions;
124 | private RetryPolicy retryPolicy;
125 | private String url;
126 | private String cacheKey;
127 | private Object tag;
128 | private Priority priority;
129 | private OnRequestListener onRequestListener;
130 | private int httpMethod;
131 |
132 | public Builder(String downloadPath, String fileName) {
133 | this.downloadPath = downloadPath;
134 | this.fileName = fileName;
135 | }
136 |
137 | public Builder requestParams(RequestParams requestParams) {
138 | this.requestParams = requestParams;
139 | return this;
140 | }
141 |
142 | public Builder body(Body body) {
143 | this.body = body;
144 | return this;
145 | }
146 |
147 | public Builder requestCacheOptions(RequestCacheOptions requestCacheOptions) {
148 | this.requestCacheOptions = requestCacheOptions;
149 | return this;
150 | }
151 |
152 | public Builder retryPolicy(RetryPolicy retryPolicy) {
153 | this.retryPolicy = retryPolicy;
154 | return this;
155 | }
156 |
157 | public Builder url(String url) {
158 | this.url = url;
159 | return this;
160 | }
161 |
162 | public Builder cacheKey(String cacheKey) {
163 | this.cacheKey = cacheKey;
164 | return this;
165 | }
166 |
167 | public Builder tag(Object tag) {
168 | this.tag = tag;
169 | return this;
170 | }
171 |
172 | public Builder priority(Priority priority) {
173 | this.priority = priority;
174 | return this;
175 | }
176 |
177 | public Builder onRequestListener(OnRequestListener onRequestListener) {
178 | this.onRequestListener = onRequestListener;
179 | return this;
180 | }
181 |
182 | public Builder httpMethod(int httpMethod) {
183 | this.httpMethod = httpMethod;
184 | return this;
185 | }
186 |
187 | @Override
188 | public HttpRequest build() {
189 | return new DownloadRequest(this);
190 | }
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/app/src/main/aidl/com/app/core/example/LogProcessor.java:
--------------------------------------------------------------------------------
1 | package com.app.core.example;/*
2 | * Copyright (C) 2009 Michael Novak
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | */
18 |
19 | import android.annotation.SuppressLint;
20 | import android.app.Service;
21 | import android.content.Intent;
22 | import android.os.IBinder;
23 | import android.os.Handler;
24 | import android.os.Message;
25 | import android.util.Log;
26 |
27 |
28 | import java.io.BufferedReader;
29 | import java.io.File;
30 | import java.io.FileWriter;
31 | import java.io.IOException;
32 | import java.io.InputStreamReader;
33 | import java.util.Vector;
34 |
35 | public class LogProcessor extends Service {
36 |
37 | private static Handler mHandler;
38 | private String mFile;
39 | private String mBuffer = "main";
40 | private Vector mScrollback;
41 | private int mLines;
42 | private int mType;
43 | private String mFilterTag;
44 | private volatile boolean threadKill = false;
45 | private volatile boolean mStatus = false;
46 | public int MAX_LINES = 250;
47 | public static final int MSG_READ_FAIL = 1;
48 | public static final int MSG_LOG_FAIL = 2;
49 | public static final int MSG_NEW_LINE = 3;
50 | public static final int MSG_RESET_LOG = 4;
51 | public static final int MSG_LOG_SAVE = 5;
52 |
53 | @Override
54 | public void onCreate() {
55 | super.onCreate();
56 | }
57 |
58 | @SuppressWarnings("deprecation")
59 | @Override
60 | public void onStart(Intent intent, int startId) {
61 | super.onStart(intent, startId);
62 | Log.i("Logger", "Logger Service has hit the onStart method.");
63 | }
64 |
65 | Runnable worker = new Runnable() {
66 | public void run() {
67 | runLog();
68 | mStatus = true;
69 | Log.d("Logger", "status... " + mStatus);
70 | return;
71 | }
72 | };
73 |
74 | private void runLog() {
75 | Process process = null;
76 |
77 | try {
78 |
79 | if (mType == 0) {
80 | process = Runtime.getRuntime().exec("/system/bin/logcat -b " + mBuffer);
81 | } else if (mType == 1) {
82 | process = Runtime.getRuntime().exec("dmesg -s 1000000");
83 | }
84 |
85 | } catch (IOException e) {
86 | communicate(MSG_LOG_FAIL);
87 | }
88 |
89 | BufferedReader reader = null;
90 |
91 | try {
92 | reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
93 |
94 | String line;
95 |
96 | while (!killRequested()) {
97 | line = reader.readLine();
98 |
99 | logLine(line);
100 |
101 | if (mLines == MAX_LINES) {
102 | mScrollback.removeElementAt(0);
103 | }
104 |
105 | mScrollback.add(line);
106 | mLines++;
107 | }
108 |
109 | Log.i("Logger", "Prepping thread for termination");
110 | reader.close();
111 | process.destroy();
112 | process = null;
113 | reader = null;
114 | mScrollback.removeAllElements();
115 | mScrollback = null;
116 | mLines = 0;
117 | } catch (IOException e) {
118 | communicate(MSG_READ_FAIL);
119 | }
120 |
121 | Log.d("Logger", "Exiting thread...");
122 | return;
123 | }
124 |
125 | private synchronized void requestKill() {
126 | threadKill = true;
127 | }
128 |
129 | private synchronized boolean killRequested() {
130 | return threadKill;
131 | }
132 |
133 | private void communicate(int msg) {
134 | Message.obtain(mHandler, msg, "error").sendToTarget();
135 | }
136 |
137 | private void logLine(String line) {
138 | Message.obtain(mHandler, MSG_NEW_LINE, line).sendToTarget();
139 | }
140 |
141 | public static void setHandler(Handler handler) {
142 | mHandler = handler;
143 | }
144 |
145 | public IBinder onBind(Intent intent) {
146 | return mBinder;
147 | }
148 |
149 | @Override
150 | public boolean onUnbind(Intent intent) {
151 | requestKill();
152 | stopSelf();
153 |
154 | return false;
155 | }
156 |
157 | private final ILogProcessor.Stub mBinder = new ILogProcessor.Stub() {
158 | @SuppressLint("DefaultLocale")
159 | public void reset(String buffer) {
160 | requestKill();
161 |
162 | while (!mStatus) {
163 | try {
164 | Log.d("Logger", "waiting...");
165 | } catch (Exception e) {
166 | Log.d("Logger", "Woot! obj has been interrupted!");
167 | }
168 | }
169 |
170 | threadKill = false;
171 | mBuffer = buffer.toLowerCase();
172 | mLines = 0;
173 | mScrollback = new Vector();
174 | Thread thr = new Thread(worker);
175 | thr.start();
176 | }
177 |
178 | public void run(int type) {
179 | mType = type;
180 | mLines = 0;
181 | mScrollback = new Vector();
182 | Thread thr = new Thread(worker);
183 | thr.start();
184 | }
185 |
186 | public void restart(int type) {
187 | requestKill();
188 |
189 | while(!mStatus) {
190 | try {
191 | Log.d("Logger", "waiting...");
192 | } catch (Exception e) {
193 | Log.d("Logger", "Woot! we have an exception");
194 | }
195 | }
196 |
197 | threadKill = false;
198 | run(type);
199 | }
200 |
201 | public void stop() {
202 | Log.i("Logger", "stop() method called in service.");
203 | requestKill();
204 | stopSelf();
205 | }
206 |
207 | public void write(String file, String tag) {
208 | mFilterTag = tag;
209 | mFile = file;
210 | Thread thr = new Thread(writer);
211 | thr.start();
212 | }
213 | };
214 |
215 | Runnable writer = new Runnable() {
216 | public void run() {
217 | writeLog();
218 | return;
219 | }
220 | };
221 |
222 | @SuppressLint({ "DefaultLocale", "SdCardPath" })
223 | private void writeLog() {
224 |
225 | try {
226 | File f = new File("/sdcard/" + mFile);
227 | FileWriter w = new FileWriter(f);
228 |
229 | for (int i = 0; i < mScrollback.size(); i++) {
230 | String line = mScrollback.elementAt(i);
231 |
232 | if (!mFilterTag.equals("")) {
233 | String tag = line.substring(2, line.indexOf("("));
234 |
235 | if (mFilterTag.toLowerCase().equals(tag.toLowerCase().trim())) {
236 | w.write(line + "\n");
237 | }
238 | } else {
239 | w.write(mScrollback.elementAt(i) + "\n");
240 | }
241 |
242 | i++;
243 | }
244 |
245 | if (!mFile.equals("tmp.log")) {
246 | Message.obtain(mHandler, MSG_LOG_SAVE, "saved").sendToTarget();
247 | } else {
248 | Message.obtain(mHandler, MSG_LOG_SAVE, "attachment").sendToTarget();
249 | }
250 |
251 | w.close();
252 | f = null;
253 | } catch (Exception e) {
254 | Log.e("Logger", "Error writing the log to a file. Exception: " + e.toString());
255 | Message.obtain(mHandler, MSG_LOG_SAVE, "error").sendToTarget();
256 | }
257 |
258 | return;
259 | }
260 |
261 | }
262 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/ex/hurl/HurlStack.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network.ex.hurl;
2 |
3 | import static net.robinx.lib.http.network.ex.hurl.RequestBodyConstants.HEADER_USER_AGENT;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.net.HttpURLConnection;
8 | import java.net.URL;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.Map.Entry;
13 |
14 | import javax.net.ssl.HttpsURLConnection;
15 | import javax.net.ssl.SSLSocketFactory;
16 |
17 | import net.robinx.lib.http.base.Request;
18 | import net.robinx.lib.http.config.HttpMethod;
19 | import net.robinx.lib.http.network.HTTPSTrustManager;
20 | import net.robinx.lib.http.network.HttpResponse;
21 | import net.robinx.lib.http.network.HttpStack;
22 | import net.robinx.lib.http.utils.CLog;
23 |
24 | import android.text.TextUtils;
25 |
26 | /**
27 | * HttpUrlConnection request body
28 | *
29 | * @author Robin
30 | * @since 2015-07-02 16:40:21
31 | */
32 | public class HurlStack implements HttpStack {
33 |
34 | private final UrlRewriter mUrlRewriter;
35 | private final SSLSocketFactory mSslSocketFactory;
36 | private String mUserAgent;
37 |
38 | public interface UrlRewriter {
39 | /**
40 | * Rewrite the URL for the request.
41 | */
42 | public String rewriteUrl(String originalUrl);
43 | }
44 |
45 | public HurlStack() {
46 | this(null);
47 | }
48 |
49 | public HurlStack(UrlRewriter urlRewriter) {
50 | this(urlRewriter, null);
51 | }
52 |
53 | public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
54 | mUrlRewriter = urlRewriter;
55 | mSslSocketFactory = sslSocketFactory;
56 | }
57 |
58 | /**
59 | * @param urlRewriter
60 | * Rewriter to use for request URLs
61 | * @param sslSocketFactory
62 | * SSL factory to use for HTTPS connections
63 | * @param userAgent
64 | * User Agent for HTTPS connections
65 | */
66 | public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory, String userAgent) {
67 |
68 | mUrlRewriter = urlRewriter;
69 | mSslSocketFactory = sslSocketFactory;
70 | mUserAgent = userAgent;
71 | }
72 |
73 | @Override
74 | public HttpResponse performRequest(Request> request, Map additionalHeaders) throws IOException {
75 | CLog.w("Current Stack:%s","HTTP_URL_CONNECTION");
76 | String url = request.getUrl();
77 | HashMap map = new HashMap();
78 | map.putAll(request.getHeaders());
79 | map.putAll(additionalHeaders);
80 |
81 | if (mUrlRewriter != null) {
82 | String rewritten = mUrlRewriter.rewriteUrl(url);
83 | if (rewritten == null) {
84 | throw new IOException("URL blocked by rewriter: " + url);
85 | }
86 | url = rewritten;
87 | }
88 | URL parsedUrl = new URL(url);
89 | HttpURLConnection connection = openConnection(parsedUrl, request);
90 |
91 | if (!TextUtils.isEmpty(mUserAgent)) {
92 | connection.setRequestProperty(HEADER_USER_AGENT, mUserAgent);
93 | }
94 |
95 | for (String headerName : map.keySet()) {
96 | connection.addRequestProperty(headerName, map.get(headerName));
97 | }
98 |
99 | setConnectionParametersForRequest(connection, request);
100 |
101 | HttpResponse response = responseFromConnection(connection);
102 | return response;
103 | }
104 |
105 | private HttpURLConnection openConnection(URL url, Request> request) throws IOException {
106 | HttpURLConnection connection = (HttpURLConnection) url.openConnection();
107 |
108 | // Timeout is actually the timeout of the retry strategy
109 | int timeoutMs = request.getRetryPolicy().getCurrentTimeout();
110 | connection.setConnectTimeout(timeoutMs);
111 | connection.setReadTimeout(timeoutMs);
112 | connection.setUseCaches(false);
113 | connection.setDoInput(true);
114 |
115 | // getContentLength()
116 | // 为“-1”,在2.2版本以上开启了GZIP压缩,导致长度始终为-1,使用如下一行代码禁止GZIP压缩,
117 | // 但是不推荐,建议与服务器端协商,在请求头中添加content length.
118 | // connection .setRequestProperty("Accept-Encoding", "identity");
119 |
120 | // use caller-provided custom SslSocketFactory, if any, for HTTPS
121 | if ("https".equals(url.getProtocol())) {
122 | if (mSslSocketFactory != null) {
123 | ((HttpsURLConnection) connection).setSSLSocketFactory(mSslSocketFactory);
124 | } else {
125 | // Trust all certificates
126 | HTTPSTrustManager.allowAllSSL();
127 | }
128 | }
129 |
130 | return connection;
131 | }
132 |
133 | /**
134 | * Create HttpResponse from a given HttpUrlConnection
135 | */
136 | private HttpResponse responseFromConnection(HttpURLConnection connection) throws IOException {
137 | HttpResponse response = new HttpResponse();
138 | int responseCode = connection.getResponseCode();
139 | if (responseCode == -1) {
140 | throw new IOException("Could not retrieve response code from HttpUrlConnection.");
141 | }
142 | response.setResponseCode(responseCode);
143 | response.setResponseMessage(connection.getResponseMessage());
144 | // contentStream
145 | InputStream inputStream;
146 | try {
147 | inputStream = connection.getInputStream();
148 | } catch (IOException ioe) {
149 | inputStream = connection.getErrorStream();
150 | }
151 | response.setContentStream(inputStream);
152 |
153 | response.setContentLength(connection.getContentLength());
154 | response.setContentEncoding(connection.getContentEncoding());
155 | response.setContentType(connection.getContentType());
156 | // header
157 | Map headerMap = new HashMap();
158 | for (Entry> header : connection.getHeaderFields().entrySet()) {
159 | if (header.getKey() != null) {
160 | String value = "";
161 | for (String v : header.getValue()) {
162 | value += (v + "; ");
163 | }
164 | headerMap.put(header.getKey(), value);
165 | response.setHeaders(headerMap);
166 | }
167 | }
168 | return response;
169 | }
170 |
171 |
172 | private static void setConnectionParametersForRequest(HttpURLConnection connection, Request> request)
173 | throws IOException {
174 | switch (request.getHttpMethod()) {
175 | case HttpMethod.GET:
176 | connection.setRequestMethod("GET");
177 | break;
178 | case HttpMethod.DELETE:
179 | connection.setRequestMethod("DELETE");
180 | break;
181 | case HttpMethod.POST:
182 | connection.setRequestMethod("POST");
183 | addBodyIfExists(connection, request);
184 | break;
185 | case HttpMethod.PUT:
186 | connection.setRequestMethod("PUT");
187 | addBodyIfExists(connection, request);
188 | break;
189 | case HttpMethod.HEAD:
190 | connection.setRequestMethod("HEAD");
191 | break;
192 | case HttpMethod.OPTIONS:
193 | connection.setRequestMethod("OPTIONS");
194 | break;
195 | case HttpMethod.TRACE:
196 | connection.setRequestMethod("TRACE");
197 | break;
198 | case HttpMethod.PATCH:
199 | // connection.setRequestMethod("PATCH");
200 | // If server doesnt support patch uncomment this
201 | connection.setRequestMethod("POST");
202 | connection.setRequestProperty("X-HTTP-Method-Override", "PATCH");
203 | addBodyIfExists(connection, request);
204 | break;
205 | default:
206 | throw new IllegalStateException("Unknown method type.");
207 | }
208 | }
209 |
210 | /**
211 | * If there is body then add
212 | */
213 | private static void addBodyIfExists(HttpURLConnection connection, Request> request) throws IOException {
214 | request.buildBody(connection);
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/cache/disk/StrictLineReader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.robinx.lib.http.cache.disk;
18 |
19 | import java.io.ByteArrayOutputStream;
20 | import java.io.Closeable;
21 | import java.io.EOFException;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 | import java.io.UnsupportedEncodingException;
25 | import java.nio.charset.Charset;
26 |
27 | /**
28 | * Buffers input from an {@link InputStream} for reading lines.
29 | *
30 | * This class is used for buffered reading of lines. For purposes of this class, a line ends
31 | * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
32 | * line at end of input is invalid and will be ignored, the caller may use {@code
33 | * hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
34 | *
35 | *
This class is intended for reading input that strictly consists of lines, such as line-based
36 | * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
37 | * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
38 | * end-of-input reporting and a more restrictive definition of a line.
39 | *
40 | *
This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
41 | * and 10, respectively, and the representation of no other character contains these values.
42 | * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
43 | * The default charset is US_ASCII.
44 | */
45 | class StrictLineReader implements Closeable {
46 | private static final byte CR = (byte) '\r';
47 | private static final byte LF = (byte) '\n';
48 |
49 | private final InputStream in;
50 | private final Charset charset;
51 |
52 | /*
53 | * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
54 | * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
55 | * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
56 | * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
57 | */
58 | private byte[] buf;
59 | private int pos;
60 | private int end;
61 |
62 | /**
63 | * Constructs a new {@code LineReader} with the specified charset and the default capacity.
64 | *
65 | * @param in the {@code InputStream} to read data from.
66 | * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
67 | * supported.
68 | * @throws NullPointerException if {@code in} or {@code charset} is null.
69 | * @throws IllegalArgumentException if the specified charset is not supported.
70 | */
71 | public StrictLineReader(InputStream in, Charset charset) {
72 | this(in, 8192, charset);
73 | }
74 |
75 | /**
76 | * Constructs a new {@code LineReader} with the specified capacity and charset.
77 | *
78 | * @param in the {@code InputStream} to read data from.
79 | * @param capacity the capacity of the buffer.
80 | * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
81 | * supported.
82 | * @throws NullPointerException if {@code in} or {@code charset} is null.
83 | * @throws IllegalArgumentException if {@code capacity} is negative or zero
84 | * or the specified charset is not supported.
85 | */
86 | public StrictLineReader(InputStream in, int capacity, Charset charset) {
87 | if (in == null || charset == null) {
88 | throw new NullPointerException();
89 | }
90 | if (capacity < 0) {
91 | throw new IllegalArgumentException("capacity <= 0");
92 | }
93 | if (!(charset.equals(Util.US_ASCII))) {
94 | throw new IllegalArgumentException("Unsupported encoding");
95 | }
96 |
97 | this.in = in;
98 | this.charset = charset;
99 | buf = new byte[capacity];
100 | }
101 |
102 | /**
103 | * Closes the reader by closing the underlying {@code InputStream} and
104 | * marking this reader as closed.
105 | *
106 | * @throws IOException for errors when closing the underlying {@code InputStream}.
107 | */
108 | public void close() throws IOException {
109 | synchronized (in) {
110 | if (buf != null) {
111 | buf = null;
112 | in.close();
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
119 | * this end of line marker is not included in the result.
120 | *
121 | * @return the next line from the input.
122 | * @throws IOException for underlying {@code InputStream} errors.
123 | * @throws EOFException for the end of source stream.
124 | */
125 | public String readLine() throws IOException {
126 | synchronized (in) {
127 | if (buf == null) {
128 | throw new IOException("LineReader is closed");
129 | }
130 |
131 | // Read more data if we are at the end of the buffered data.
132 | // Though it's an error to read after an exception, we will let {@code fillBuf()}
133 | // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
134 | if (pos >= end) {
135 | fillBuf();
136 | }
137 | // Try to find LF in the buffered data and return the line if successful.
138 | for (int i = pos; i != end; ++i) {
139 | if (buf[i] == LF) {
140 | int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
141 | String res = new String(buf, pos, lineEnd - pos, charset.name());
142 | pos = i + 1;
143 | return res;
144 | }
145 | }
146 |
147 | // Let's anticipate up to 80 characters on top of those already read.
148 | ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
149 | @Override
150 | public String toString() {
151 | int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
152 | try {
153 | return new String(buf, 0, length, charset.name());
154 | } catch (UnsupportedEncodingException e) {
155 | throw new AssertionError(e); // Since we control the charset this will never happen.
156 | }
157 | }
158 | };
159 |
160 | while (true) {
161 | out.write(buf, pos, end - pos);
162 | // Mark unterminated line in case fillBuf throws EOFException or IOException.
163 | end = -1;
164 | fillBuf();
165 | // Try to find LF in the buffered data and return the line if successful.
166 | for (int i = pos; i != end; ++i) {
167 | if (buf[i] == LF) {
168 | if (i != pos) {
169 | out.write(buf, pos, i - pos);
170 | }
171 | pos = i + 1;
172 | return out.toString();
173 | }
174 | }
175 | }
176 | }
177 | }
178 |
179 | public boolean hasUnterminatedLine() {
180 | return end == -1;
181 | }
182 |
183 | /**
184 | * Reads new input data into the buffer. Call only with pos == end or end == -1,
185 | * depending on the desired outcome if the function throws.
186 | */
187 | private void fillBuf() throws IOException {
188 | int result = in.read(buf, 0, buf.length);
189 | if (result == -1) {
190 | throw new EOFException();
191 | }
192 | pos = 0;
193 | end = result;
194 | }
195 | }
196 |
197 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/Network.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.net.MalformedURLException;
6 | import java.net.SocketTimeoutException;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import net.robinx.lib.http.base.Request;
11 | import net.robinx.lib.http.response.NetworkResponse;
12 | import net.robinx.lib.http.retry.RetryPolicy;
13 | import net.robinx.lib.http.utils.CLog;
14 |
15 | /**
16 | * The network requests the HttpStack to use the Request client to initiate a network request and returns a NetworkRespond result.
17 | * @author Robin
18 | * @since 2015-07-02 16:16:57
19 | */
20 | public class Network {
21 | protected final HttpStack mHttpStack;
22 |
23 | public Network(HttpStack httpStack) {
24 | mHttpStack = httpStack;
25 | }
26 |
27 | /**
28 | * Actually executing a request
29 | *
30 | * @param request
31 | * @return A response not to null
32 | * @throws HttpException
33 | */
34 | public NetworkResponse performRequest(Request> request)
35 | throws HttpException {
36 | while (true) {
37 | HttpResponse httpResponse = null;
38 | byte[] responseContents = null;
39 | Map responseHeaders = new HashMap();
40 | try {
41 | Map headers = new HashMap();
42 | httpResponse = mHttpStack.performRequest(request, headers);
43 |
44 | int statusCode = httpResponse.getResponseCode();
45 | responseHeaders = httpResponse.getHeaders();
46 | if (statusCode == HttpStatus.SC_NOT_MODIFIED) { // 304
47 | return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
48 | null,
49 | responseHeaders, true);
50 | }
51 |
52 | if (httpResponse.getContentStream() != null) {
53 | responseContents = responseToBytes(request,httpResponse);
54 | } else {
55 | responseContents = new byte[0];
56 | }
57 |
58 | //instead statusCode > 299
59 | if (statusCode < 200 || statusCode > 405) {
60 | throw new IOException();
61 | }
62 | return new NetworkResponse(statusCode, responseContents,
63 | responseHeaders, false);
64 | } catch (SocketTimeoutException e) {
65 | if (request.getRequestCacheOptions().isRetryWhenRequestFailed()) {
66 | retryOnException( request, new HttpException("socket timeout",HttpError.ERROR_SOCKET_TIMEOUT));
67 | }else {
68 | throw new HttpException("socket timeout",HttpError.ERROR_SOCKET_TIMEOUT);
69 | }
70 | }
71 | /*catch (ConnectTimeoutException e) {
72 | if (request.getCacheConfig().isRetryWhenRequestFailed()) {
73 | retryOnException( request, new HttpException("connect timeout"));
74 | }else {
75 | throw new HttpException(new SocketTimeoutException("connect timeout"));
76 | }
77 | } */
78 | catch (MalformedURLException e) {
79 | throw new RuntimeException("Bad URL " + request.getUrl(), e);
80 | } catch (IOException e) {
81 | int statusCode = 0;
82 | //NetworkResponse networkResponse = null;
83 | if (httpResponse != null) {
84 | statusCode = httpResponse.getResponseCode();
85 | } else {
86 | //throw new HttpException("NoConnection error", e);
87 | throw new HttpException("NoConnection error", HttpError.ERROR_NO_CONNECTION);
88 | //retryOnException( request, new HttpException("NoConnection error",e));
89 | }
90 | CLog.d("Unexpected response code %s for: %s",statusCode,request.getUrl());
91 | if (responseContents != null) {
92 | //networkResponse = new NetworkResponse(statusCode,responseContents, responseHeaders, false);
93 | if (statusCode == HttpStatus.SC_UNAUTHORIZED|| statusCode == HttpStatus.SC_FORBIDDEN) {
94 | if (request.getRequestCacheOptions().isRetryWhenRequestFailed()) {
95 | retryOnException( request, new HttpException("auth error",HttpError.ERROR_UNAUTHORIZED));
96 | } else {
97 | throw new HttpException("auth error",HttpError.ERROR_UNAUTHORIZED);
98 | }
99 | } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
100 | if (request.getRequestCacheOptions().isRetryWhenRequestFailed()) {
101 | retryOnException( request, new HttpException("redirect error",HttpError.ERROR_REDIRECT));
102 | }else {
103 | throw new HttpException("redirect error",HttpError.ERROR_REDIRECT);
104 | }
105 |
106 | } else {
107 | throw new HttpException("server error, Only throw ServerError for 5xx status codes.",HttpError.ERROR_SERVER);
108 | }
109 | } else {
110 | throw new HttpException("responseContents is null",HttpError.ERROR_RESPONSE_NULL);
111 | }
112 | }
113 | }
114 | }
115 |
116 |
117 | /**
118 | * Convert HttpResponse to byte[]
119 | *
120 | * @param request
121 | * @param response
122 | * @return
123 | * @throws IOException
124 | * @throws HttpException
125 | */
126 | private byte[] responseToBytes(Request> request,HttpResponse response) throws IOException,
127 | HttpException {
128 | PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(
129 | ByteArrayPool.get(), (int) response.getContentLength());
130 | byte[] buffer = null;
131 | long totalSize = (int) response.getContentLength();
132 | try {
133 | InputStream in =response.getContentStream();
134 | if (in == null) {
135 | throw new HttpException("server error",HttpError.ERROR_SERVER);
136 | }
137 | buffer = ByteArrayPool.get().getBuf(1024);
138 | int count;
139 | int transferredBytesSize = 0;
140 | while ((count = in.read(buffer)) != -1) {
141 | bytes.write(buffer, 0, count);
142 | transferredBytesSize += count;
143 | //request.getRequestQueue().getDelivery().postRequestDownloadProgress(request,transferredBytesSize, totalSize);
144 | request.onRequestDownloadProgress(transferredBytesSize, totalSize);
145 | }
146 | return bytes.toByteArray();
147 | } finally {
148 | try {
149 | response.getContentStream().close();
150 | } catch (IOException e) {
151 | CLog.d("Error occured when calling consumingContent");
152 | }
153 | ByteArrayPool.get().returnBuf(buffer);
154 | bytes.close();
155 | }
156 | }
157 |
158 | /**
159 | * When an exception occurs to try again
160 | * @param request
161 | * @param exception
162 | * @return
163 | * @throws HttpException
164 | */
165 | private static RetryPolicy retryOnException( Request> request,HttpException exception) throws HttpException {
166 | RetryPolicy retryPolicy = request.getRetryPolicy();
167 | try {
168 | retryPolicy.retry(exception);
169 | } catch (HttpException e) {
170 | throw e;
171 | }
172 |
173 | /*Distribution of retry event*/
174 | request.getRequestQueue().getDelivery().postRequestRetry(request, retryPolicy.getCurrentRetryCount() ,exception);
175 |
176 | return retryPolicy;
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/lib.http/src/main/java/net/robinx/lib/http/network/ex/hurl/HurlRequestBody.java:
--------------------------------------------------------------------------------
1 | package net.robinx.lib.http.network.ex.hurl;
2 |
3 | import net.robinx.lib.http.callback.BytesWriteListener;
4 | import net.robinx.lib.http.network.ex.Body;
5 | import net.robinx.lib.http.network.ex.RequestParams;
6 | import net.robinx.lib.http.utils.CLog;
7 |
8 | import java.io.BufferedInputStream;
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.io.OutputStream;
13 | import java.io.OutputStreamWriter;
14 | import java.io.PrintWriter;
15 | import java.net.HttpURLConnection;
16 |
17 | /**
18 | * Can submit key/value pair, files, key/value pair and files, JSON,
19 | * If the request parameter contains a JSON parameters, send JSON parameters,
20 | * this time even contain key/value pair or file parameter will not be sent
21 | *
22 | * @author Robin
23 | * @since 2016-01-07 18:53:19
24 | */
25 | public class HurlRequestBody implements Body {
26 |
27 | private boolean isFixedStreamingMode;
28 |
29 | private BytesWriteListener mBytesWriteListener;
30 |
31 | /*======================================================
32 | * Override Super
33 | *======================================================
34 | */
35 |
36 | public String buildBodyContentType(RequestParams params, int curTime) {
37 | if (params.hasJsonInParams()) {
38 | return String.format("application/json; charset=%s", "utf-8");
39 | }
40 |
41 | return String.format(RequestBodyConstants.CONTENT_TYPE_MULTIPART, "utf-8", curTime);
42 | }
43 |
44 | @Override
45 | public Object buildBody(RequestParams params, Object... args) {
46 | HttpURLConnection connection = (HttpURLConnection) args[0];
47 | connection.setDoOutput(true);
48 | final String charset = "utf-8";
49 | final int curTime = (int) (System.currentTimeMillis() / 1000);
50 | final String boundary = RequestBodyConstants.BOUNDARY_PREFIX + curTime;
51 | connection.setRequestProperty(RequestBodyConstants.HEADER_CONTENT_TYPE, buildBodyContentType(params, curTime));
52 |
53 | if (isFixedStreamingMode()) {
54 | int contentLength = RequestBodyConstants.getContentLength(boundary, params);
55 | connection.setFixedLengthStreamingMode(contentLength);
56 | } else {
57 | connection.setChunkedStreamingMode(0);
58 | }
59 |
60 | // Write parameters
61 | PrintWriter writer = null;
62 | try {
63 | OutputStream out = connection.getOutputStream();
64 | writer = new PrintWriter(new OutputStreamWriter(out, charset), true);
65 |
66 | if (params.hasJsonInParams()) {
67 | // append json
68 | writer.append(params.buildJsonParams()).flush();
69 |
70 | } else {
71 |
72 | writeMultipartToOutputStream(params, boundary, writer, out);
73 |
74 | // End of multipart/form-data.
75 | writer.append(boundary + RequestBodyConstants.BOUNDARY_PREFIX).append(RequestBodyConstants.CRLF).flush();
76 | }
77 |
78 | } catch (Exception e) {
79 | e.printStackTrace();
80 |
81 | } finally {
82 | if (writer != null) {
83 | writer.close();
84 | }
85 | }
86 |
87 | return null;
88 | }
89 |
90 | private void writeMultipartToOutputStream(RequestParams params, String boundary, PrintWriter writer, OutputStream out) throws IOException {
91 | int currentFileIndex = 1;
92 | for (String key : params.keySet()) {
93 | Object value = params.get(key);
94 | if (value == null) {
95 | continue;
96 | }
97 |
98 | if (value instanceof File) { // add file
99 | currentFileIndex = writeFileToOutputStream(boundary, writer, out, currentFileIndex, key, (File) value);
100 | } else { // add field
101 | writeFieldToOutputStream(boundary, writer, key, value);
102 | }
103 | }
104 | }
105 |
106 | private void writeFieldToOutputStream(String boundary, PrintWriter writer, String key, Object value) {
107 | String param = String.valueOf(value);
108 |
109 | writer.append(boundary).append(RequestBodyConstants.CRLF)
110 | .append(String.format(RequestBodyConstants.HEADER_CONTENT_DISPOSITION + RequestBodyConstants.COLON_SPACE + RequestBodyConstants.FORM_DATA, key)).append(RequestBodyConstants.CRLF)
111 | .append(RequestBodyConstants.HEADER_CONTENT_TYPE + RequestBodyConstants.COLON_SPACE + RequestBodyConstants.CONTENT_TYPE_TEXT)
112 | .append(RequestBodyConstants.CRLF).append(RequestBodyConstants.CRLF)
113 | .append(param).append(RequestBodyConstants.CRLF).flush();
114 | }
115 |
116 | private int writeFileToOutputStream(String boundary, PrintWriter writer, OutputStream out, int currentFileIndex, String key, File value) throws IOException {
117 | File file = value;
118 |
119 | if (!file.exists()) {
120 | CLog.e("File not found: %s", file.getAbsolutePath());
121 | throw new IOException(String.format("File not found: %s", file.getAbsolutePath()));
122 | }
123 |
124 | if (file.isDirectory()) {
125 | CLog.e("File is a directory: %s", file.getAbsolutePath());
126 | throw new IOException(String.format("File is a directory: %s", file.getAbsolutePath()));
127 | }
128 |
129 | writer.append(boundary).append(RequestBodyConstants.CRLF)
130 | .append(String.format(
131 | RequestBodyConstants.HEADER_CONTENT_DISPOSITION + RequestBodyConstants.COLON_SPACE + RequestBodyConstants.FORM_DATA + RequestBodyConstants.SEMICOLON_SPACE + RequestBodyConstants.FILENAME, key,
132 | file.getName()))
133 | .append(RequestBodyConstants.CRLF).append(RequestBodyConstants.HEADER_CONTENT_TYPE + RequestBodyConstants.COLON_SPACE + RequestBodyConstants.CONTENT_TYPE_OCTET_STREAM).append(RequestBodyConstants.CRLF)
134 | .append(RequestBodyConstants.HEADER_CONTENT_TRANSFER_ENCODING + RequestBodyConstants.COLON_SPACE + RequestBodyConstants.BINARY).append(RequestBodyConstants.CRLF).append(RequestBodyConstants.CRLF)
135 | .flush();
136 |
137 | BufferedInputStream input = null;
138 | try {
139 | FileInputStream fis = new FileInputStream(file);
140 | int transferredBytesSize = 0;
141 | int totalSize = (int) file.length();
142 | input = new BufferedInputStream(fis);
143 | int bufferLength = 0;
144 |
145 | byte[] buffer = new byte[1024];
146 | while ((bufferLength = input.read(buffer)) > 0) {
147 | CLog.w(" thread name : %s", Thread.currentThread().getName());
148 | out.write(buffer, 0, bufferLength);
149 | transferredBytesSize += bufferLength;
150 | if (mBytesWriteListener != null) {
151 | mBytesWriteListener.onWrite(transferredBytesSize, totalSize, currentFileIndex, file);
152 | }
153 |
154 | }
155 | // Important! Output cannot be closed. Close of writer will
156 | // close output as well.
157 | out.flush();
158 | } finally {
159 | if (input != null)
160 | try {
161 | input.close();
162 | } catch (IOException ex) {
163 | ex.printStackTrace();
164 | }
165 | }
166 | // CRLF is important! It indicates end of binary boundary.
167 | writer.append(RequestBodyConstants.CRLF).flush();
168 |
169 | currentFileIndex++;
170 | return currentFileIndex;
171 | }
172 |
173 | public boolean isFixedStreamingMode() {
174 | return isFixedStreamingMode;
175 | }
176 |
177 | public void setFixedStreamingMode(boolean isFixedStreamingMode) {
178 | this.isFixedStreamingMode = isFixedStreamingMode;
179 | }
180 |
181 | @Override
182 | public void setBytesWriteListener(BytesWriteListener listener) {
183 | this.mBytesWriteListener = listener;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------