diskSerializer,
58 | int maxDiskSizeBytes,
59 | File diskFolder
60 | ) {
61 | this.appVersion = appVersion;
62 | this.ramMode = ramMode;
63 | this.ramSerializer = ramSerializer;
64 | this.diskMode = diskMode;
65 | this.diskSerializer = diskSerializer;
66 | this.diskCacheFolder = diskFolder;
67 | this.logger = logger;
68 | this.loggerHelper = new LoggerHelper(logger);
69 |
70 | switch (ramMode) {
71 | case ENABLE_WITH_SPECIFIC_SERIALIZER:
72 | this.ramCacheLru = new StringLruCache(maxRamSizeBytes);
73 | break;
74 | case ENABLE_WITH_REFERENCE:
75 | this.ramCacheLru = new ReferenceLruCache<>(maxRamSizeBytes, sizeOf);
76 | break;
77 | default:
78 | this.ramCacheLru = null;
79 | }
80 |
81 | switch (diskMode) {
82 | case ENABLE_WITH_SPECIFIC_SERIALIZER:
83 | this.maxDiskSizeBytes = maxDiskSizeBytes;
84 | try {
85 | openDiskLruCache(diskFolder);
86 | } catch (IOException e) {
87 | logger.logError(e);
88 | }
89 | break;
90 | default:
91 | this.maxDiskSizeBytes = 0;
92 | }
93 | }
94 |
95 | private void openDiskLruCache(File diskFolder) throws IOException {
96 | this.diskLruCache = DiskLruCache.open(
97 | diskFolder,
98 | this.appVersion,
99 | VALUES_PER_CACHE_ENTRY,
100 | this.maxDiskSizeBytes
101 | );
102 | }
103 |
104 | public long getRamUsedInBytes() {
105 | if (ramCacheLru == null) {
106 | return -1;
107 | } else {
108 | return ramCacheLru.size();
109 | }
110 | }
111 |
112 | public long getDiskUsedInBytes() {
113 | if (diskLruCache == null) {
114 | return -1;
115 | } else {
116 | return diskLruCache.size();
117 | }
118 |
119 | }
120 |
121 | /**
122 | * Return the way objects are cached in RAM layer.
123 | *
124 | * @return the way objects are cached in RAM layer.
125 | */
126 | public DualCacheRamMode getRAMMode() {
127 | return ramMode;
128 | }
129 |
130 | /**
131 | * Return the way objects are cached in disk layer.
132 | *
133 | * @return the way objects are cached in disk layer.
134 | */
135 | public DualCacheDiskMode getDiskMode() {
136 | return diskMode;
137 | }
138 |
139 | /**
140 | * Put an object in cache.
141 | *
142 | * @param key is the key of the object.
143 | * @param object is the object to put in cache.
144 | */
145 | public void put(String key, T object) {
146 | // Synchronize put on each entry. Gives concurrent editions on different entries, and atomic
147 | // modification on the same entry.
148 | if (ramMode.equals(DualCacheRamMode.ENABLE_WITH_REFERENCE)) {
149 | ramCacheLru.put(key, object);
150 | }
151 |
152 | String ramSerialized = null;
153 | if (ramMode.equals(DualCacheRamMode.ENABLE_WITH_SPECIFIC_SERIALIZER)) {
154 | ramSerialized = ramSerializer.toString(object);
155 | ramCacheLru.put(key, ramSerialized);
156 | }
157 |
158 | if (diskMode.equals(DualCacheDiskMode.ENABLE_WITH_SPECIFIC_SERIALIZER)) {
159 | try {
160 | dualCacheLock.lockDiskEntryWrite(key);
161 | DiskLruCache.Editor editor = diskLruCache.edit(key);
162 | if (ramSerializer == diskSerializer) {
163 | // Optimization if using same serializer
164 | editor.set(0, ramSerialized);
165 | } else {
166 | editor.set(0, diskSerializer.toString(object));
167 | }
168 | editor.commit();
169 | } catch (IOException e) {
170 | logger.logError(e);
171 | } finally {
172 | dualCacheLock.unLockDiskEntryWrite(key);
173 | }
174 | }
175 | }
176 |
177 | /**
178 | * Return the object of the corresponding key from the cache. In no object is available,
179 | * return null.
180 | *
181 | * @param key is the key of the object.
182 | * @return the object of the corresponding key from the cache. In no object is available,
183 | * return null.
184 | */
185 | public T get(String key) {
186 |
187 | Object ramResult = null;
188 | String diskResult = null;
189 | DiskLruCache.Snapshot snapshotObject = null;
190 |
191 | // Try to get the object from RAM.
192 | boolean isRamSerialized = ramMode.equals(DualCacheRamMode.ENABLE_WITH_SPECIFIC_SERIALIZER);
193 | boolean isRamReferenced = ramMode.equals(DualCacheRamMode.ENABLE_WITH_REFERENCE);
194 | if (isRamSerialized || isRamReferenced) {
195 | ramResult = ramCacheLru.get(key);
196 | }
197 |
198 | if (ramResult == null) {
199 | // Try to get the cached object from disk.
200 | loggerHelper.logEntryForKeyIsNotInRam(key);
201 | if (diskMode.equals(DualCacheDiskMode.ENABLE_WITH_SPECIFIC_SERIALIZER)) {
202 | try {
203 | dualCacheLock.lockDiskEntryWrite(key);
204 | snapshotObject = diskLruCache.get(key);
205 | } catch (IOException e) {
206 | logger.logError(e);
207 | } finally {
208 | dualCacheLock.unLockDiskEntryWrite(key);
209 | }
210 |
211 | if (snapshotObject != null) {
212 | loggerHelper.logEntryForKeyIsOnDisk(key);
213 | try {
214 | diskResult = snapshotObject.getString(0);
215 | } catch (IOException e) {
216 | logger.logError(e);
217 | }
218 | } else {
219 | loggerHelper.logEntryForKeyIsNotOnDisk(key);
220 | }
221 | }
222 |
223 | T objectFromStringDisk = null;
224 |
225 | if (diskResult != null) {
226 | // Load object, no need to check disk configuration since diskresult != null.
227 | objectFromStringDisk = diskSerializer.fromString(diskResult);
228 |
229 | // Refresh object in ram.
230 | if (ramMode.equals(DualCacheRamMode.ENABLE_WITH_REFERENCE)) {
231 | if (diskMode.equals(DualCacheDiskMode.ENABLE_WITH_SPECIFIC_SERIALIZER)) {
232 | ramCacheLru.put(key, objectFromStringDisk);
233 | }
234 | } else if (ramMode.equals(DualCacheRamMode.ENABLE_WITH_SPECIFIC_SERIALIZER)) {
235 | if (diskSerializer == ramSerializer) {
236 | ramCacheLru.put(key, diskResult);
237 | } else {
238 | ramCacheLru.put(key, ramSerializer.toString(objectFromStringDisk));
239 | }
240 | }
241 | return objectFromStringDisk;
242 | }
243 | } else {
244 | loggerHelper.logEntryForKeyIsInRam(key);
245 | if (ramMode.equals(DualCacheRamMode.ENABLE_WITH_REFERENCE)) {
246 | return (T) ramResult;
247 | } else if (ramMode.equals(DualCacheRamMode.ENABLE_WITH_SPECIFIC_SERIALIZER)) {
248 | return ramSerializer.fromString((String) ramResult);
249 | }
250 | }
251 |
252 | // No data is available.
253 | return null;
254 | }
255 |
256 | /**
257 | * Delete the corresponding object in cache.
258 | *
259 | * @param key is the key of the object.
260 | */
261 | public void delete(String key) {
262 | if (!ramMode.equals(DualCacheRamMode.DISABLE)) {
263 | ramCacheLru.remove(key);
264 | }
265 | if (!diskMode.equals(DualCacheDiskMode.DISABLE)) {
266 | try {
267 | dualCacheLock.lockDiskEntryWrite(key);
268 | diskLruCache.remove(key);
269 | } catch (IOException e) {
270 | logger.logError(e);
271 | } finally {
272 | dualCacheLock.unLockDiskEntryWrite(key);
273 | }
274 | }
275 | }
276 |
277 | /**
278 | * Remove all objects from cache (both RAM and disk).
279 | */
280 | public void invalidate() {
281 | invalidateDisk();
282 | invalidateRAM();
283 | }
284 |
285 | /**
286 | * Remove all objects from RAM.
287 | */
288 | public void invalidateRAM() {
289 | if (!ramMode.equals(DualCacheRamMode.DISABLE)) {
290 | ramCacheLru.evictAll();
291 | }
292 | }
293 |
294 | /**
295 | * Remove all objects from Disk.
296 | */
297 | public void invalidateDisk() {
298 | if (!diskMode.equals(DualCacheDiskMode.DISABLE)) {
299 | try {
300 | dualCacheLock.lockFullDiskWrite();
301 | diskLruCache.delete();
302 | openDiskLruCache(diskCacheFolder);
303 | } catch (IOException e) {
304 | logger.logError(e);
305 | } finally {
306 | dualCacheLock.unLockFullDiskWrite();
307 | }
308 | }
309 | }
310 |
311 | /**
312 | * Test if an object is present in cache.
313 | * @param key is the key of the object.
314 | * @return true if the object is present in cache, false otherwise.
315 | */
316 | public boolean contains(String key) {
317 | if (!ramMode.equals(DualCacheRamMode.DISABLE) && ramCacheLru.snapshot().containsKey(key)) {
318 | return true;
319 | }
320 | try {
321 | dualCacheLock.lockDiskEntryWrite(key);
322 | if (!diskMode.equals(DualCacheDiskMode.DISABLE) && diskLruCache.get(key) != null) {
323 | return true;
324 | }
325 | } catch (IOException e) {
326 | logger.logError(e);
327 | } finally {
328 | dualCacheLock.unLockDiskEntryWrite(key);
329 | }
330 | return false;
331 | }
332 |
333 | /**
334 | * Closes the underlying Disk LRU Cache. (if one is in use)
335 | * @throws IOException if an I/O error occurs
336 | * @see DiskLruCache#close()
337 | */
338 | @Override
339 | public void close() throws IOException {
340 | if (diskLruCache != null) {
341 | diskLruCache.close();
342 | }
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/http/src/main/java/org/ithot/android/transmit/cache/RamLruCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 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 org.ithot.android.transmit.cache;
18 |
19 | import java.lang.reflect.InvocationTargetException;
20 | import java.util.LinkedHashMap;
21 | import java.util.Map;
22 |
23 | /**
24 | * A cache that holds strong references to a limited number of values. Each time
25 | * a value is accessed, it is moved to the head of a queue. When a value is
26 | * added to a full cache, the value at the end of that queue is evicted and may
27 | * become eligible for garbage collection.
28 | *
29 | * If your cached values hold resources that need to be explicitly released,
30 | * override {@link #entryRemoved}.
31 | *
32 | *
If a cache miss should be computed on demand for the corresponding keys,
33 | * override {@link #create}. This simplifies the calling code, allowing it to
34 | * assume a value will always be returned, even when there's a cache miss.
35 | *
36 | *
By default, the cache size is measured in the number of entries. Override
37 | * {@link #sizeOf} to size the cache in different units. For example, this cache
38 | * is limited to 4MiB of bitmaps:
39 | *
{@code
40 | * int cacheSize = 4 * 1024 * 1024; // 4MiB
41 | * LruCache bitmapCache = new LruCache(cacheSize) {
42 | * protected int sizeOf(String key, Bitmap value) {
43 | * return value.getByteCount();
44 | * }
45 | * }}
46 | *
47 | * This class is thread-safe. Perform multiple cache operations atomically by
48 | * synchronizing on the cache:
{@code
49 | * synchronized (cache) {
50 | * if (cache.get(key) == null) {
51 | * cache.put(key, value);
52 | * }
53 | * }}
54 | *
55 | * This class does not allow null to be used as a key or value. A return
56 | * value of null from {@link #get}, {@link #put} or {@link #remove} is
57 | * unambiguous: the key was not in the cache.
58 | *
59 | *
This class appeared in Android 3.1 (Honeycomb MR1); it's available as part
60 | * of Android's
61 | * Support Package for earlier releases.
62 | */
63 | class RamLruCache {
64 | private final LinkedHashMap map;
65 |
66 | /** Size of this cache in units. Not necessarily the number of elements. */
67 | private int size;
68 | private int maxSize;
69 |
70 | private int putCount;
71 | private int createCount;
72 | private int evictionCount;
73 | private int hitCount;
74 | private int missCount;
75 |
76 | /**
77 | * @param maxSize for caches that do not override {@link #sizeOf}, this is
78 | * the maximum number of entries in the cache. For all other caches,
79 | * this is the maximum sum of the sizes of the entries in this cache.
80 | */
81 | public RamLruCache(int maxSize) {
82 | if (maxSize <= 0) {
83 | throw new IllegalArgumentException("maxSize <= 0");
84 | }
85 | this.maxSize = maxSize;
86 | this.map = new LinkedHashMap(0, 0.75f, true);
87 | }
88 |
89 | /**
90 | * Sets the size of the cache.
91 | *
92 | * @param maxSize The new maximum size.
93 | */
94 | public void resize(int maxSize) {
95 | if (maxSize <= 0) {
96 | throw new IllegalArgumentException("maxSize <= 0");
97 | }
98 |
99 | synchronized (this) {
100 | this.maxSize = maxSize;
101 | }
102 | trimToSize(maxSize);
103 | }
104 |
105 | /**
106 | * Returns the value for {@code key} if it exists in the cache or can be
107 | * created by {@code #create}. If a value was returned, it is moved to the
108 | * head of the queue. This returns null if a value is not cached and cannot
109 | * be created.
110 | */
111 | public final V get(K key) {
112 | if (key == null) {
113 | throw new NullPointerException("key == null");
114 | }
115 |
116 | V mapValue;
117 | synchronized (this) {
118 | mapValue = map.get(key);
119 | if (mapValue != null) {
120 | hitCount++;
121 | return mapValue;
122 | }
123 | missCount++;
124 | }
125 |
126 | /*
127 | * Attempt to create a value. This may take a long time, and the map
128 | * may be different when create() returns. If a conflicting value was
129 | * added to the map while create() was working, we leave that value in
130 | * the map and release the created value.
131 | */
132 |
133 | V createdValue = create(key);
134 | if (createdValue == null) {
135 | return null;
136 | }
137 |
138 | synchronized (this) {
139 | createCount++;
140 | mapValue = map.put(key, createdValue);
141 |
142 | if (mapValue != null) {
143 | // There was a conflict so undo that last put
144 | map.put(key, mapValue);
145 | } else {
146 | size += safeSizeOf(key, createdValue);
147 | }
148 | }
149 |
150 | if (mapValue != null) {
151 | entryRemoved(false, key, createdValue, mapValue);
152 | return mapValue;
153 | } else {
154 | trimToSize(maxSize);
155 | return createdValue;
156 | }
157 | }
158 |
159 | /**
160 | * Caches {@code value} for {@code key}. The value is moved to the head of
161 | * the queue.
162 | *
163 | * @return the previous value mapped by {@code key}.
164 | */
165 | public final V put(K key, V value) {
166 | if (key == null || value == null) {
167 | throw new NullPointerException("key == null || value == null");
168 | }
169 |
170 | V previous;
171 | synchronized (this) {
172 | putCount++;
173 | size += safeSizeOf(key, value);
174 | previous = map.put(key, value);
175 | if (previous != null) {
176 | size -= safeSizeOf(key, previous);
177 | }
178 | }
179 |
180 | if (previous != null) {
181 | entryRemoved(false, key, previous, value);
182 | }
183 |
184 | trimToSize(maxSize);
185 | return previous;
186 | }
187 |
188 | /**
189 | * Remove the eldest entries until the total of remaining entries is at or
190 | * below the requested size.
191 | *
192 | * @param maxSize the maximum size of the cache before returning. May be -1
193 | * to evict even 0-sized elements.
194 | */
195 | public void trimToSize(int maxSize) {
196 | while (true) {
197 | K key;
198 | V value;
199 | synchronized (this) {
200 | if (size < 0 || (map.isEmpty() && size != 0)) {
201 | throw new IllegalStateException(getClass().getName()
202 | + ".sizeOf() is reporting inconsistent results!");
203 | }
204 |
205 | if (size <= maxSize) {
206 | break;
207 | }
208 | Map.Entry toEvict = null;
209 | try {
210 | toEvict = (Map.Entry) map.getClass().getMethod("eldest").invoke(map);
211 |
212 | } catch (NoSuchMethodException e) {
213 | e.printStackTrace();
214 | } catch (InvocationTargetException e) {
215 | e.printStackTrace();
216 | } catch (IllegalAccessException e) {
217 | e.printStackTrace();
218 | }
219 | if (toEvict == null) {
220 | break;
221 | }
222 | key = toEvict.getKey();
223 | value = toEvict.getValue();
224 | map.remove(key);
225 | size -= safeSizeOf(key, value);
226 | evictionCount++;
227 | }
228 | entryRemoved(true, key, value, null);
229 | }
230 | }
231 |
232 | /**
233 | * Removes the entry for {@code key} if it exists.
234 | *
235 | * @return the previous value mapped by {@code key}.
236 | */
237 | public final V remove(K key) {
238 | if (key == null) {
239 | throw new NullPointerException("key == null");
240 | }
241 |
242 | V previous;
243 | synchronized (this) {
244 | previous = map.remove(key);
245 | if (previous != null) {
246 | size -= safeSizeOf(key, previous);
247 | }
248 | }
249 |
250 | if (previous != null) {
251 | entryRemoved(false, key, previous, null);
252 | }
253 |
254 | return previous;
255 | }
256 |
257 | /**
258 | * Called for entries that have been evicted or removed. This method is
259 | * invoked when a value is evicted to make space, removed by a call to
260 | * {@link #remove}, or replaced by a call to {@link #put}. The default
261 | * implementation does nothing.
262 | *
263 | * The method is called without synchronization: other threads may
264 | * access the cache while this method is executing.
265 | *
266 | * @param evicted true if the entry is being removed to make space, false
267 | * if the removal was caused by a {@link #put} or {@link #remove}.
268 | * @param newValue the new value for {@code key}, if it exists. If non-null,
269 | * this removal was caused by a {@link #put}. Otherwise it was caused by
270 | * an eviction or a {@link #remove}.
271 | */
272 | protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
273 |
274 | /**
275 | * Called after a cache miss to compute a value for the corresponding key.
276 | * Returns the computed value or null if no value can be computed. The
277 | * default implementation returns null.
278 | *
279 | *
The method is called without synchronization: other threads may
280 | * access the cache while this method is executing.
281 | *
282 | *
If a value for {@code key} exists in the cache when this method
283 | * returns, the created value will be released with {@link #entryRemoved}
284 | * and discarded. This can occur when multiple threads request the same key
285 | * at the same time (causing multiple values to be created), or when one
286 | * thread calls {@link #put} while another is creating a value for the same
287 | * key.
288 | */
289 | protected V create(K key) {
290 | return null;
291 | }
292 |
293 | private int safeSizeOf(K key, V value) {
294 | int result = sizeOf(key, value);
295 | if (result < 0) {
296 | throw new IllegalStateException("Negative size: " + key + "=" + value);
297 | }
298 | return result;
299 | }
300 |
301 | /**
302 | * Returns the size of the entry for {@code key} and {@code value} in
303 | * user-defined units. The default implementation returns 1 so that size
304 | * is the number of entries and max size is the maximum number of entries.
305 | *
306 | *
An entry's size must not change while it is in the cache.
307 | */
308 | protected int sizeOf(K key, V value) {
309 | return 1;
310 | }
311 |
312 | /**
313 | * Clear the cache, calling {@link #entryRemoved} on each removed entry.
314 | */
315 | public final void evictAll() {
316 | trimToSize(-1); // -1 will evict 0-sized elements
317 | }
318 |
319 | /**
320 | * For caches that do not override {@link #sizeOf}, this returns the number
321 | * of entries in the cache. For all other caches, this returns the sum of
322 | * the sizes of the entries in this cache.
323 | */
324 | public synchronized final int size() {
325 | return size;
326 | }
327 |
328 | /**
329 | * For caches that do not override {@link #sizeOf}, this returns the maximum
330 | * number of entries in the cache. For all other caches, this returns the
331 | * maximum sum of the sizes of the entries in this cache.
332 | */
333 | public synchronized final int maxSize() {
334 | return maxSize;
335 | }
336 |
337 | /**
338 | * Returns the number of times {@link #get} returned a value that was
339 | * already present in the cache.
340 | */
341 | public synchronized final int hitCount() {
342 | return hitCount;
343 | }
344 |
345 | /**
346 | * Returns the number of times {@link #get} returned null or required a new
347 | * value to be created.
348 | */
349 | public synchronized final int missCount() {
350 | return missCount;
351 | }
352 |
353 | /**
354 | * Returns the number of times {@link #create(Object)} returned a value.
355 | */
356 | public synchronized final int createCount() {
357 | return createCount;
358 | }
359 |
360 | /**
361 | * Returns the number of times {@link #put} was called.
362 | */
363 | public synchronized final int putCount() {
364 | return putCount;
365 | }
366 |
367 | /**
368 | * Returns the number of values that have been evicted.
369 | */
370 | public synchronized final int evictionCount() {
371 | return evictionCount;
372 | }
373 |
374 | /**
375 | * Returns a copy of the current contents of the cache, ordered from least
376 | * recently accessed to most recently accessed.
377 | */
378 | public synchronized final Map snapshot() {
379 | return new LinkedHashMap(map);
380 | }
381 |
382 | @Override public synchronized final String toString() {
383 | int accesses = hitCount + missCount;
384 | int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
385 | return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
386 | maxSize, hitCount, missCount, hitPercent);
387 | }
388 | }
389 |
--------------------------------------------------------------------------------
/http/src/main/java/org/ithot/android/transmit/http/Req.java:
--------------------------------------------------------------------------------
1 | package org.ithot.android.transmit.http;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.net.NetworkInfo;
6 |
7 | import com.loopj.android.http.AsyncHttpClient;
8 | import com.loopj.android.http.FileAsyncHttpResponseHandler;
9 | import com.loopj.android.http.RangeFileAsyncHttpResponseHandler;
10 | import com.loopj.android.http.RequestHandle;
11 | import com.loopj.android.http.RequestParams;
12 |
13 | import org.ithot.android.serializerinterface.JSONSerializer;
14 | import org.ithot.android.transmit.cache.Builder;
15 | import org.ithot.android.transmit.cache.CacheSerializer;
16 | import org.ithot.android.transmit.cache.DualCache;
17 | import org.ithot.android.transmit.cache.StringSerializer;
18 |
19 | import java.io.File;
20 | import java.security.MessageDigest;
21 | import java.util.ArrayList;
22 | import java.util.List;
23 | import java.util.Map;
24 |
25 | import cz.msebera.android.httpclient.Header;
26 | import cz.msebera.android.httpclient.HttpEntity;
27 | import cz.msebera.android.httpclient.entity.StringEntity;
28 | import cz.msebera.android.httpclient.message.BasicHeader;
29 |
30 | /**
31 | * 对httpclient请求二次封装
32 | */
33 | public class Req {
34 |
35 | // 是否开启调试模式
36 | static boolean DEBUG = false;
37 | // 请求的根域名
38 | private static String BASE_URL = "";
39 | // 请求的内容类型
40 | private static String CONTENT_TYPE = "application/json";
41 | // 请求的编码
42 | private static final String CHARSET = "UTF-8";
43 | // async http 实例
44 | static AsyncHttpClient client;
45 | // get请求缓存实例
46 | static DualCache _cache;
47 | // http 生命周期回调函数
48 | static IHTTPHook ihttpHook;
49 | // 缓存key的前缀
50 | private static String CACHE_KEY_PREFIX = "";
51 | // 与序列化库解耦
52 | private static JSONSerializer _json;
53 |
54 | public static JSONSerializer json() {
55 | return _json;
56 | }
57 |
58 | /**
59 | * 懒加载 gson cache
60 | *
61 | * @param context
62 | */
63 | private static void lazy(Context context, JSONSerializer json) {
64 | _json = json;
65 | CacheSerializer stringSerializer = new StringSerializer();
66 | _cache = new Builder("android-sex-http", 1)
67 | .useSerializerInRam(500 * 1024, stringSerializer)
68 | .useSerializerInDisk(10 * 1024 * 1024, true, stringSerializer, context)
69 | .build();
70 | }
71 |
72 | /**
73 | * 设置是否开启调试模式
74 | *
75 | * @param debug
76 | */
77 | public static void debug(boolean debug) {
78 | DEBUG = debug;
79 | }
80 |
81 | /**
82 | * 设置http生命周期回调函数
83 | *
84 | * @param hook
85 | */
86 | public static void hook(IHTTPHook hook) {
87 | ihttpHook = hook;
88 | }
89 |
90 | /**
91 | * 设置根域名
92 | *
93 | * @param baseUrl 例:http://foo.com
94 | */
95 | public static void base(String baseUrl) {
96 | BASE_URL = baseUrl;
97 | }
98 |
99 | /**
100 | * 设置get请求缓存key的前缀
101 | *
102 | * @param prefix
103 | */
104 | public static void prefix(String prefix) {
105 | CACHE_KEY_PREFIX = prefix;
106 | }
107 |
108 | /**
109 | * 初始化 android sex http
110 | *
111 | * @param context
112 | */
113 | public static void init(Context context, JSONSerializer json) {
114 | client = new AsyncHttpClient(true, 80, 443);
115 | lazy(context, json);
116 | }
117 |
118 | /**
119 | * 初始化 android sex http
120 | *
121 | * @param context
122 | * @param http http端口号
123 | */
124 | public static void init(Context context, int http, JSONSerializer json) {
125 | client = new AsyncHttpClient(true, http, 443);
126 | lazy(context, json);
127 | }
128 |
129 | /**
130 | * 初始化 android sex http
131 | *
132 | * @param context
133 | * @param http http端口号
134 | * @param https https端口号
135 | */
136 | public static void init(Context context, int http, int https, JSONSerializer json) {
137 | client = new AsyncHttpClient(true, http, https);
138 | lazy(context, json);
139 | }
140 |
141 | /**
142 | * 取消某个context下的所有请求 建议onDestroy()中调用
143 | *
144 | * @param context
145 | */
146 | public static void cancel(Context context) {
147 | client.cancelRequests(context, true);
148 | }
149 |
150 | /**
151 | * 取消全部请求 建议关闭应用的时候可以调用
152 | */
153 | public static void cancelAll() {
154 | client.cancelAllRequests(true);
155 | }
156 |
157 | // 请求url
158 | private String _url;
159 | // Activity的引用 为了取消请求和回调入UI线程
160 | Context _context;
161 | // 请求头
162 | private List _headers = new ArrayList<>();
163 | // 请求的内容类型 默认为json
164 | private String _contentType = CONTENT_TYPE;
165 | // get请求的缓存策略 默认为缓存远程策略
166 | Policy _cachePolicy = Policy.CacheAndRemote;
167 | // 响应
168 | Res _res;
169 | // 请求方法 默认为get
170 | Method _method = Method.GET;
171 | // 请求体
172 | private HttpEntity _body;
173 | // 内部用的处理类
174 | private HTTPHandler _handler;
175 | // 是否使用跟路由
176 | private boolean _base = true;
177 | // 真正的请求引用
178 | private RequestHandle request;
179 | // 该请求是否使用缓存前缀
180 | private boolean _prefix = true;
181 |
182 | /**
183 | * 调用此方法开启请求链
184 | *
185 | * @return
186 | */
187 | public static Req build() {
188 | Req req = new Req();
189 | if (ihttpHook != null) {
190 | if (null != ihttpHook.headers())
191 | req._headers.addAll(ihttpHook.headers());
192 | }
193 | req._handler = new HTTPHandler(req);
194 | return req;
195 | }
196 |
197 | /**
198 | * 调用此方法开启请求链
199 | *
200 | * @param context Activity引用
201 | * @return
202 | */
203 | public static Req build(Context context) {
204 | Req req = new Req();
205 | req._context = context;
206 | if (ihttpHook != null) {
207 | if (null != ihttpHook.headers())
208 | req._headers.addAll(ihttpHook.headers());
209 | }
210 | req._handler = new HTTPHandler(req);
211 | return req;
212 | }
213 |
214 | /**
215 | * 调用此方法开启请求链
216 | *
217 | * @param context Activity引用
218 | * @param url 请求url
219 | * @return
220 | */
221 | public static Req build(Context context, String url) {
222 | Req req = new Req();
223 | req._context = context;
224 | req._url = url;
225 | if (ihttpHook != null) {
226 | if (null != ihttpHook.headers())
227 | req._headers.addAll(ihttpHook.headers());
228 | }
229 | req._handler = new HTTPHandler(req);
230 | return req;
231 | }
232 |
233 | /**
234 | * 是否使用根域名
235 | *
236 | * @param base
237 | * @return
238 | */
239 | public Req base(boolean base) {
240 | this._base = base;
241 | return this;
242 | }
243 |
244 | /**
245 | * 设置Activity 引用
246 | *
247 | * @param context
248 | * @return
249 | */
250 | public Req context(Context context) {
251 | this._context = context;
252 | return this;
253 | }
254 |
255 | /**
256 | * 设置query参数 ?page=1&size=10
257 | *
258 | * @param kvs 可以使用Pair.build().kvs().go()去构建query
259 | * @return
260 | */
261 | public Req query(Map kvs) {
262 | if (_url != null) {
263 | _url += "?";
264 | int i = 0;
265 | for (Map.Entry entry : kvs.entrySet()) {
266 | if (i == 0) {
267 | _url += String.format("%s=%s", entry.getKey(), entry.getValue().toString());
268 | } else {
269 | _url += String.format("&%s=%s", entry.getKey(), entry.getValue().toString());
270 | }
271 | i++;
272 | }
273 | }
274 | return this;
275 | }
276 |
277 | /**
278 | * 设置请求url
279 | *
280 | * @param url
281 | * @return
282 | */
283 | public Req url(String url) {
284 | if (_base) {
285 | if (BASE_URL == null) {
286 | this._url = url;
287 | } else {
288 | this._url = String.format("%s%s", BASE_URL, url);
289 | }
290 | } else {
291 | this._url = url;
292 | }
293 | return this;
294 | }
295 |
296 | /**
297 | * 设置请求体
298 | *
299 | * @param body
300 | * @return
301 | */
302 | public Req body(HttpEntity body) {
303 | this._body = body;
304 | return this;
305 | }
306 |
307 | /**
308 | * 设置请求体
309 | *
310 | * @param body Java Bean
311 | * @return
312 | */
313 | public Req body(Object body) {
314 | this._body = new StringEntity(json().stringify(body), CHARSET);
315 | return this;
316 | }
317 |
318 | /**
319 | * 设置请求体
320 | *
321 | * @param map 可以使用Pair.build().kvs().go()去构建body
322 | * @return
323 | */
324 | public Req body(Map map) {
325 | String json = json().stringify(map);
326 | this._body = new StringEntity(json, CHARSET);
327 | return this;
328 | }
329 |
330 | /**
331 | * 追加若干请求头
332 | *
333 | * @param headers 可以使用Pair.build().kvs().go()去构建body
334 | * @return
335 | */
336 | public Req headers(Map headers) {
337 | for (Map.Entry header : headers.entrySet()) {
338 | Header h = new BasicHeader(header.getKey(), header.getValue());
339 | this._headers.add(h);
340 | }
341 | return this;
342 | }
343 |
344 | /**
345 | * 追加若干请求头
346 | *
347 | * @param headers 可以使用Pair.build().kvs().go()去构建body
348 | * @param clear 设置true 追加前清空所有请求头
349 | * @return
350 | */
351 | public Req headers(Map headers, boolean clear) {
352 | if (clear) {
353 | this._headers.clear();
354 | }
355 | for (Map.Entry header : headers.entrySet()) {
356 | Header h = new BasicHeader(header.getKey(), header.getValue());
357 | this._headers.add(h);
358 | }
359 | return this;
360 | }
361 |
362 | /**
363 | * 追加一个请求头
364 | *
365 | * @param key
366 | * @param value
367 | * @return
368 | */
369 | public Req header(String key, String value) {
370 | this._headers.add(new BasicHeader(key, value));
371 | return this;
372 | }
373 |
374 | /**
375 | * 追加一个请求头
376 | *
377 | * @param key
378 | * @param value
379 | * @param clear 设置true 追加前清空所有请求头
380 | * @return
381 | */
382 | public Req header(String key, String value, boolean clear) {
383 | this._headers.clear();
384 | this._headers.add(new BasicHeader(key, value));
385 | return this;
386 | }
387 |
388 | /**
389 | * 设置请求内容的类型 默认 json
390 | *
391 | * @param type application/json?
392 | * @return
393 | */
394 | public Req type(String type) {
395 | this._contentType = type;
396 | return this;
397 | }
398 |
399 | /**
400 | * 设置请求方法
401 | *
402 | * @param method
403 | * @return
404 | */
405 | public Req method(Method method) {
406 | this._method = method;
407 | return this;
408 | }
409 |
410 | public Req handler(HTTPHandler handler) {
411 | this._handler = handler;
412 | return this;
413 | }
414 |
415 | /**
416 | * 设置响应 可以设置泛型类型
417 | *
418 | * @param res
419 | * @return
420 | */
421 | public Req res(Res res) {
422 | this._res = res;
423 | return this;
424 | }
425 |
426 | /**
427 | * 取消这个请求
428 | */
429 | public void cancel() {
430 | if (this.request != null) {
431 | request.cancel(true);
432 | }
433 | }
434 |
435 | /**
436 | * 设置get请求缓存策略
437 | *
438 | * @param policy
439 | * @return
440 | */
441 | public Req policy(Policy policy) {
442 | this._cachePolicy = policy;
443 | return this;
444 | }
445 |
446 | /**
447 | * 缓存时是否使用缓存前缀
448 | *
449 | * @param prefix
450 | * @return
451 | */
452 | public Req prefix(boolean prefix) {
453 | this._prefix = prefix;
454 | return this;
455 | }
456 |
457 | /**
458 | * 得到缓存需要的key
459 | *
460 | * @return
461 | */
462 | public String key() {
463 | if (this._prefix)
464 | return md5(CACHE_KEY_PREFIX + _url);
465 | else
466 | return md5(_url);
467 | }
468 |
469 | Header[] _headers_() {
470 | Header[] results = new Header[_headers.size()];
471 | for (int i = 0; i < _headers.size(); i++) {
472 | results[i] = _headers.get(i);
473 | }
474 | return results;
475 | }
476 |
477 | /**
478 | * 发送请求
479 | */
480 | public void go() {
481 | // call pre hook
482 | if (ihttpHook != null) {
483 | ihttpHook.pre(_context);
484 | }
485 | if (_method == Method.GET) {
486 | if (_cachePolicy == Policy.NoCache || _cachePolicy == Policy.IgnoreCache) {
487 | if (isNetworkConnected(_context)) {
488 | request = client.get(_context, _url, _headers_(), null, _handler);
489 | } else {
490 | _res.disconnected(_context);
491 | }
492 | } else if (_cachePolicy == Policy.CacheAndRemote) {
493 | String c = _cache.get(key());
494 | if (c != null) {
495 | _res.cache(c);
496 | if (isNetworkConnected(_context)) {
497 | request = client.get(_context, _url, _headers_(), null, _handler);
498 | } else {
499 | _res.disconnected(_context);
500 | }
501 | } else {
502 | if (isNetworkConnected(_context)) {
503 | request = client.get(_context, _url, _headers_(), null, _handler);
504 | } else {
505 | _res.disconnected(_context);
506 | }
507 | }
508 | } else if (_cachePolicy == Policy.CacheOnly) {
509 | String c = _cache.get(key());
510 | if (c != null) {
511 | _res.cache(c);
512 | if (!isNetworkConnected(_context)) {
513 | _res.disconnected(_context);
514 | } else {
515 | if (ihttpHook != null) {
516 | ihttpHook.post(_context);
517 | }
518 | }
519 | } else {
520 | if (!isNetworkConnected(_context)) {
521 | _res.disconnected(_context);
522 | } else {
523 | request = client.get(_context, _url, _headers_(), null, _handler);
524 | }
525 | }
526 |
527 | } else if (_cachePolicy == Policy.CacheOrRemote) {
528 | String cache = _cache.get(key());
529 | if (isNetworkConnected(_context)) {
530 | request = client.get(_context, _url, _headers_(), null, _handler);
531 | } else {
532 | _res.cache(cache);
533 | _res.disconnected(_context);
534 | }
535 | }
536 | } else {
537 | if (!isNetworkConnected(_context)) {
538 | _res.disconnected(_context);
539 | return;
540 | }
541 | switch (_method) {
542 | case POST:
543 | request = client.post(_context, _url, _headers_(), _body, _contentType, _handler);
544 | break;
545 | case PUT:
546 | request = client.put(_context, _url, _headers_(), _body, _contentType, _handler);
547 | break;
548 | case DELETE:
549 | request = client.delete(_context, _url, _headers_(), _handler);
550 | break;
551 | }
552 | }
553 | }
554 |
555 | public static String md5(String s) {
556 | char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
557 | try {
558 | byte[] btInput = s.getBytes();
559 | MessageDigest mdInst = MessageDigest.getInstance("MD5");
560 | mdInst.update(btInput);
561 | byte[] md = mdInst.digest();
562 | int j = md.length;
563 | char[] str = new char[j * 2];
564 | int k = 0;
565 | for (int i = 0; i < j; i++) {
566 | byte byte0 = md[i];
567 | str[(k++)] = hexDigits[(byte0 >>> 4 & 0xF)];
568 | str[(k++)] = hexDigits[(byte0 & 0xF)];
569 | }
570 | return new String(str);
571 | } catch (Exception e) {
572 | e.printStackTrace();
573 | }
574 | return null;
575 | }
576 |
577 | private static boolean isNetworkConnected(Context context) {
578 | if (context != null) {
579 | ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
580 | NetworkInfo mNetworkInfo = connectivity.getActiveNetworkInfo();
581 | if (mNetworkInfo != null) {
582 | return mNetworkInfo.isAvailable();
583 | }
584 | }
585 | return false;
586 | }
587 |
588 | // new feature
589 |
590 | private RequestParams _params;
591 | private FileRes _fileRes;
592 |
593 | private Req params(RequestParams _params_) {
594 | this._params = _params_;
595 | return this;
596 | }
597 |
598 | public Req res(FileRes fileRes) {
599 | _fileRes = fileRes;
600 | return this;
601 | }
602 |
603 | public void download(File file) {
604 | download(file, false);
605 | }
606 |
607 | public void download(File file, boolean ugly) {
608 |
609 | if (!isNetworkConnected(_context)) {
610 | _fileRes.disconnected(_context);
611 | return;
612 | }
613 |
614 | FileAsyncHttpResponseHandler handler;
615 | if (ugly) {
616 | handler = new FileAsyncHttpResponseHandler(file) {
617 | @Override
618 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
619 | _fileRes.undone();
620 | }
621 |
622 | @Override
623 | public void onSuccess(int statusCode, Header[] headers, File file) {
624 | _fileRes.done(file);
625 | }
626 |
627 | @Override
628 | public void onProgress(long bytesWritten, long totalSize) {
629 | _fileRes.progress(Utils.progress(bytesWritten, totalSize));
630 | }
631 | };
632 | } else {
633 | handler = new RangeFileAsyncHttpResponseHandler(file) {
634 | @Override
635 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
636 | _fileRes.undone();
637 | }
638 |
639 | @Override
640 | public void onSuccess(int statusCode, Header[] headers, File file) {
641 | _fileRes.done(file);
642 | }
643 |
644 | @Override
645 | public void onProgress(long bytesWritten, long totalSize) {
646 | _fileRes.progress(Utils.progress(bytesWritten, totalSize));
647 | }
648 | };
649 | }
650 |
651 | client.get(_context, _url, _headers_(), _params, handler);
652 | }
653 |
654 | }
655 |
--------------------------------------------------------------------------------