(2);
112 | }
113 | orderByList.add(new OrderBy(columnName, desc));
114 | return this;
115 | }
116 |
117 | public Selector limit(int limit) {
118 | this.limit = limit;
119 | return this;
120 | }
121 |
122 | public Selector offset(int offset) {
123 | this.offset = offset;
124 | return this;
125 | }
126 |
127 | @Override
128 | public String toString() {
129 | StringBuilder result = new StringBuilder();
130 | result.append("SELECT ");
131 | result.append("*");
132 | result.append(" FROM ").append(tableName);
133 | if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {
134 | result.append(" WHERE ").append(whereBuilder.toString());
135 | }
136 | if (orderByList != null) {
137 | for (int i = 0; i < orderByList.size(); i++) {
138 | result.append(" ORDER BY ").append(orderByList.get(i).toString());
139 | }
140 | }
141 | if (limit > 0) {
142 | result.append(" LIMIT ").append(limit);
143 | result.append(" OFFSET ").append(offset);
144 | }
145 | return result.toString();
146 | }
147 |
148 | public Class> getEntityType() {
149 | return entityType;
150 | }
151 |
152 | protected class OrderBy {
153 | private String columnName;
154 | private boolean desc;
155 |
156 | public OrderBy(String columnName) {
157 | this.columnName = columnName;
158 | }
159 |
160 | public OrderBy(String columnName, boolean desc) {
161 | this.columnName = columnName;
162 | this.desc = desc;
163 | }
164 |
165 | @Override
166 | public String toString() {
167 | return columnName + (desc ? " DESC" : " ASC");
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/com/android/volley/db/sqlite/DbModelSelector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)
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 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package com.android.volley.db.sqlite;
17 |
18 | import android.text.TextUtils;
19 |
20 | /**
21 | * Author: wyouflf
22 | * Date: 13-8-10
23 | * Time: 下午2:15
24 | */
25 | public class DbModelSelector {
26 |
27 | private String[] columnExpressions;
28 | private String groupByColumnName;
29 | private WhereBuilder having;
30 |
31 | private Selector selector;
32 |
33 | private DbModelSelector(Class> entityType) {
34 | selector = Selector.from(entityType);
35 | }
36 |
37 | protected DbModelSelector(Selector selector, String groupByColumnName) {
38 | this.selector = selector;
39 | this.groupByColumnName = groupByColumnName;
40 | }
41 |
42 | protected DbModelSelector(Selector selector, String[] columnExpressions) {
43 | this.selector = selector;
44 | this.columnExpressions = columnExpressions;
45 | }
46 |
47 | public static DbModelSelector from(Class> entityType) {
48 | return new DbModelSelector(entityType);
49 | }
50 |
51 | public DbModelSelector where(WhereBuilder whereBuilder) {
52 | selector.where(whereBuilder);
53 | return this;
54 | }
55 |
56 | public DbModelSelector where(String columnName, String op, Object value) {
57 | selector.where(columnName, op, value);
58 | return this;
59 | }
60 |
61 | public DbModelSelector and(String columnName, String op, Object value) {
62 | selector.and(columnName, op, value);
63 | return this;
64 | }
65 |
66 | public DbModelSelector and(WhereBuilder where) {
67 | selector.and(where);
68 | return this;
69 | }
70 |
71 | public DbModelSelector or(String columnName, String op, Object value) {
72 | selector.or(columnName, op, value);
73 | return this;
74 | }
75 |
76 | public DbModelSelector or(WhereBuilder where) {
77 | selector.or(where);
78 | return this;
79 | }
80 |
81 | public DbModelSelector expr(String expr) {
82 | selector.expr(expr);
83 | return this;
84 | }
85 |
86 | public DbModelSelector expr(String columnName, String op, Object value) {
87 | selector.expr(columnName, op, value);
88 | return this;
89 | }
90 |
91 | public DbModelSelector groupBy(String columnName) {
92 | this.groupByColumnName = columnName;
93 | return this;
94 | }
95 |
96 | public DbModelSelector having(WhereBuilder whereBuilder) {
97 | this.having = whereBuilder;
98 | return this;
99 | }
100 |
101 | public DbModelSelector select(String... columnExpressions) {
102 | this.columnExpressions = columnExpressions;
103 | return this;
104 | }
105 |
106 | public DbModelSelector orderBy(String columnName) {
107 | selector.orderBy(columnName);
108 | return this;
109 | }
110 |
111 | public DbModelSelector orderBy(String columnName, boolean desc) {
112 | selector.orderBy(columnName, desc);
113 | return this;
114 | }
115 |
116 | public DbModelSelector limit(int limit) {
117 | selector.limit(limit);
118 | return this;
119 | }
120 |
121 | public DbModelSelector offset(int offset) {
122 | selector.offset(offset);
123 | return this;
124 | }
125 |
126 | public Class> getEntityType() {
127 | return selector.getEntityType();
128 | }
129 |
130 | @Override
131 | public String toString() {
132 | StringBuffer result = new StringBuffer();
133 | result.append("SELECT ");
134 | if (columnExpressions != null && columnExpressions.length > 0) {
135 | for (int i = 0; i < columnExpressions.length; i++) {
136 | result.append(columnExpressions[i]);
137 | result.append(",");
138 | }
139 | result.deleteCharAt(result.length() - 1);
140 | } else {
141 | if (!TextUtils.isEmpty(groupByColumnName)) {
142 | result.append(groupByColumnName);
143 | } else {
144 | result.append("*");
145 | }
146 | }
147 | result.append(" FROM ").append(selector.tableName);
148 | if (selector.whereBuilder != null && selector.whereBuilder.getWhereItemSize() > 0) {
149 | result.append(" WHERE ").append(selector.whereBuilder.toString());
150 | }
151 | if (!TextUtils.isEmpty(groupByColumnName)) {
152 | result.append(" GROUP BY ").append(groupByColumnName);
153 | if (having != null && having.getWhereItemSize() > 0) {
154 | result.append(" HAVING ").append(having.toString());
155 | }
156 | }
157 | if (selector.orderByList != null) {
158 | for (int i = 0; i < selector.orderByList.size(); i++) {
159 | result.append(" ORDER BY ").append(selector.orderByList.get(i).toString());
160 | }
161 | }
162 | if (selector.limit > 0) {
163 | result.append(" LIMIT ").append(selector.limit);
164 | result.append(" OFFSET ").append(selector.offset);
165 | }
166 | return result.toString();
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/com/android/volley/toolbox/ByteArrayPool.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 com.android.volley.toolbox;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Collections;
21 | import java.util.Comparator;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 |
25 | /**
26 | * ByteArrayPool is a source and repository of byte[] objects. Its purpose is to
27 | * supply those buffers to consumers who need to use them for a short period of time and then
28 | * dispose of them. Simply creating and disposing such buffers in the conventional manner can
29 | * considerable heap churn and garbage collection delays on Android, which lacks good management of
30 | * short-lived heap objects. It may be advantageous to trade off some memory in the form of a
31 | * permanently allocated pool of buffers in order to gain heap performance improvements; that is
32 | * what this class does.
33 | *
34 | * A good candidate user for this class is something like an I/O system that uses large temporary
35 | * byte[] buffers to copy data around. In these use cases, often the consumer wants
36 | * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
37 | * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
38 | * account and also to maximize the odds of being able to reuse a recycled buffer, this class is
39 | * free to return buffers larger than the requested size. The caller needs to be able to gracefully
40 | * deal with getting buffers any size over the minimum.
41 | *
42 | * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
43 | * class will allocate a new buffer and return it.
44 | *
45 | * This class has no special ownership of buffers it creates; the caller is free to take a buffer
46 | * it receives from this pool, use it permanently, and never return it to the pool; additionally,
47 | * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
48 | * are no other lingering references to it.
49 | *
50 | * This class ensures that the total size of the buffers in its recycling pool never exceeds a
51 | * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
52 | * least-recently-used buffers are disposed.
53 | */
54 | public class ByteArrayPool {
55 | /** The buffer pool, arranged both by last use and by buffer size */
56 | private List mBuffersByLastUse = new LinkedList();
57 | private List mBuffersBySize = new ArrayList(64);
58 |
59 | /** The total size of the buffers in the pool */
60 | private int mCurrentSize = 0;
61 |
62 | /**
63 | * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
64 | * under this limit.
65 | */
66 | private final int mSizeLimit;
67 |
68 | /** Compares buffers by size */
69 | protected static final Comparator BUF_COMPARATOR = new Comparator() {
70 | @Override
71 | public int compare(byte[] lhs, byte[] rhs) {
72 | return lhs.length - rhs.length;
73 | }
74 | };
75 |
76 | /**
77 | * @param sizeLimit the maximum size of the pool, in bytes
78 | */
79 | public ByteArrayPool(int sizeLimit) {
80 | mSizeLimit = sizeLimit;
81 | }
82 |
83 | /**
84 | * Returns a buffer from the pool if one is available in the requested size, or allocates a new
85 | * one if a pooled one is not available.
86 | *
87 | * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
88 | * larger.
89 | * @return a byte[] buffer is always returned.
90 | */
91 | public synchronized byte[] getBuf(int len) {
92 | for (int i = 0; i < mBuffersBySize.size(); i++) {
93 | byte[] buf = mBuffersBySize.get(i);
94 | if (buf.length >= len) {
95 | mCurrentSize -= buf.length;
96 | mBuffersBySize.remove(i);
97 | mBuffersByLastUse.remove(buf);
98 | return buf;
99 | }
100 | }
101 | return new byte[len];
102 | }
103 |
104 | /**
105 | * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
106 | * size.
107 | *
108 | * @param buf the buffer to return to the pool.
109 | */
110 | public synchronized void returnBuf(byte[] buf) {
111 | if (buf == null || buf.length > mSizeLimit) {
112 | return;
113 | }
114 | mBuffersByLastUse.add(buf);
115 | int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
116 | if (pos < 0) {
117 | pos = -pos - 1;
118 | }
119 | mBuffersBySize.add(pos, buf);
120 | mCurrentSize += buf.length;
121 | trim();
122 | }
123 |
124 | /**
125 | * Removes buffers from the pool until it is under its size limit.
126 | */
127 | private synchronized void trim() {
128 | while (mCurrentSize > mSizeLimit) {
129 | byte[] buf = mBuffersByLastUse.remove(0);
130 | mBuffersBySize.remove(buf);
131 | mCurrentSize -= buf.length;
132 | }
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/com/android/volley/db/sqlite/CursorUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)
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 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package com.android.volley.db.sqlite;
17 |
18 | import java.util.HashMap;
19 | import java.util.Map.Entry;
20 | import java.util.concurrent.ConcurrentHashMap;
21 |
22 | import android.database.Cursor;
23 |
24 | import com.android.volley.VolleyLog;
25 | import com.android.volley.db.table.Column;
26 | import com.android.volley.db.table.DbModel;
27 | import com.android.volley.db.table.Finder;
28 | import com.android.volley.db.table.Id;
29 | import com.android.volley.db.table.Table;
30 | import com.android.volley.ext.tools.DbTools;
31 |
32 | public class CursorUtils {
33 |
34 | public static T getEntity(final DbTools db, final Cursor cursor, Class entityType, long findCacheSequence) {
35 | if (db == null || cursor == null) return null;
36 |
37 | EntityTempCache.setSeq(findCacheSequence);
38 | try {
39 | Table table = Table.get(db, entityType);
40 | Id id = table.id;
41 | String idColumnName = id.getColumnName();
42 | int idIndex = id.getIndex();
43 | if (idIndex < 0) {
44 | idIndex = cursor.getColumnIndex(idColumnName);
45 | }
46 | Object idValue = id.getColumnConverter().getFieldValue(cursor, idIndex);
47 | T entity = EntityTempCache.get(entityType, idValue);
48 | if (entity == null) {
49 | entity = entityType.newInstance();
50 | id.setValue2Entity(entity, cursor, idIndex);
51 | EntityTempCache.put(entityType, idValue, entity);
52 | } else {
53 | return entity;
54 | }
55 | int columnCount = cursor.getColumnCount();
56 | for (int i = 0; i < columnCount; i++) {
57 | String columnName = cursor.getColumnName(i);
58 | Column column = table.columnMap.get(columnName);
59 | if (column != null) {
60 | column.setValue2Entity(entity, cursor, i);
61 | }
62 | }
63 |
64 | // init finder
65 | for (Finder finder : table.finderMap.values()) {
66 | finder.setValue2Entity(entity, null, 0);
67 | }
68 | return entity;
69 | } catch (Throwable e) {
70 | VolleyLog.e(e.getMessage(), e);
71 | }
72 |
73 | return null;
74 | }
75 |
76 | public static T dbModel2Entity(final DbTools db, DbModel dbModel,Class> clazz){
77 | if(dbModel!=null){
78 | HashMap dataMap = dbModel.getDataMap();
79 | try {
80 | @SuppressWarnings("unchecked")
81 | T entity = (T) clazz.newInstance();
82 | for(Entry entry : dataMap.entrySet()){
83 | String columnKey = entry.getKey();
84 | Table table = Table.get(db, clazz);
85 | Column column = table.columnMap.get(columnKey);
86 | if (column != null) {
87 | column.setValue(entity, entry.getValue()==null?null:entry.getValue().toString());
88 | }
89 | }
90 | return entity;
91 | } catch (Exception e) {
92 | e.printStackTrace();
93 | }
94 | }
95 |
96 | return null;
97 | }
98 |
99 | public static DbModel getDbModel(final Cursor cursor) {
100 | DbModel result = null;
101 | if (cursor != null) {
102 | result = new DbModel();
103 | int columnCount = cursor.getColumnCount();
104 | for (int i = 0; i < columnCount; i++) {
105 | result.add(cursor.getColumnName(i), cursor.getString(i));
106 | }
107 | }
108 | return result;
109 | }
110 |
111 | public static class FindCacheSequence {
112 | private FindCacheSequence() {
113 | }
114 |
115 | private static long seq = 0;
116 | private static final String FOREIGN_LAZY_LOADER_CLASS_NAME = ForeignLazyLoader.class.getName();
117 | private static final String FINDER_LAZY_LOADER_CLASS_NAME = FinderLazyLoader.class.getName();
118 |
119 | public static long getSeq() {
120 | String findMethodCaller = Thread.currentThread().getStackTrace()[4].getClassName();
121 | if (!findMethodCaller.equals(FOREIGN_LAZY_LOADER_CLASS_NAME) && !findMethodCaller.equals(FINDER_LAZY_LOADER_CLASS_NAME)) {
122 | ++seq;
123 | }
124 | return seq;
125 | }
126 | }
127 |
128 | private static class EntityTempCache {
129 | private EntityTempCache() {
130 | }
131 |
132 | private static final ConcurrentHashMap cache = new ConcurrentHashMap();
133 |
134 | private static long seq = 0;
135 |
136 | public static void put(Class entityType, Object idValue, Object entity) {
137 | cache.put(entityType.getName() + "#" + idValue, entity);
138 | }
139 |
140 | @SuppressWarnings("unchecked")
141 | public static T get(Class entityType, Object idValue) {
142 | return (T) cache.get(entityType.getName() + "#" + idValue);
143 | }
144 |
145 | public static void setSeq(long seq) {
146 | if (EntityTempCache.seq != seq) {
147 | cache.clear();
148 | EntityTempCache.seq = seq;
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/com/android/volley/db/table/Foreign.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)
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 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package com.android.volley.db.table;
17 |
18 | import java.lang.reflect.Field;
19 | import java.util.List;
20 |
21 | import android.database.Cursor;
22 |
23 | import com.android.volley.VolleyLog;
24 | import com.android.volley.db.DbException;
25 | import com.android.volley.db.converter.ColumnConverter;
26 | import com.android.volley.db.converter.ColumnConverterFactory;
27 | import com.android.volley.db.sqlite.ColumnDbType;
28 | import com.android.volley.db.sqlite.ForeignLazyLoader;
29 |
30 | public class Foreign extends Column {
31 |
32 | private final String foreignColumnName;
33 | private final ColumnConverter foreignColumnConverter;
34 |
35 | /* package */ Foreign(Class> entityType, Field field) {
36 | super(entityType, field);
37 |
38 | foreignColumnName = ColumnUtils.getForeignColumnNameByField(field);
39 | Class> foreignColumnType =
40 | TableUtils.getColumnOrId(getForeignEntityType(), foreignColumnName).columnField.getType();
41 | foreignColumnConverter = ColumnConverterFactory.getColumnConverter(foreignColumnType);
42 | }
43 |
44 | public String getForeignColumnName() {
45 | return foreignColumnName;
46 | }
47 |
48 | public Class> getForeignEntityType() {
49 | return ColumnUtils.getForeignEntityType(this);
50 | }
51 |
52 | @SuppressWarnings("unchecked")
53 | @Override
54 | public void setValue2Entity(Object entity, Cursor cursor, int index) {
55 | Object fieldValue = foreignColumnConverter.getFieldValue(cursor, index);
56 | if (fieldValue == null) return;
57 |
58 | Object value = null;
59 | Class> columnType = columnField.getType();
60 | if (columnType.equals(ForeignLazyLoader.class)) {
61 | value = new ForeignLazyLoader(this, fieldValue);
62 | } else if (columnType.equals(List.class)) {
63 | try {
64 | value = new ForeignLazyLoader(this, fieldValue).getAllFromDb();
65 | } catch (DbException e) {
66 | VolleyLog.e(e.getMessage(), e);
67 | }
68 | } else {
69 | try {
70 | value = new ForeignLazyLoader(this, fieldValue).getFirstFromDb();
71 | } catch (DbException e) {
72 | VolleyLog.e(e.getMessage(), e);
73 | }
74 | }
75 |
76 | if (setMethod != null) {
77 | try {
78 | setMethod.invoke(entity, value);
79 | } catch (Throwable e) {
80 | VolleyLog.e(e.getMessage(), e);
81 | }
82 | } else {
83 | try {
84 | this.columnField.setAccessible(true);
85 | this.columnField.set(entity, value);
86 | } catch (Throwable e) {
87 | VolleyLog.e(e.getMessage(), e);
88 | }
89 | }
90 | }
91 |
92 | @SuppressWarnings("unchecked")
93 | @Override
94 | public Object getColumnValue(Object entity) {
95 | Object fieldValue = getFieldValue(entity);
96 | Object columnValue = null;
97 |
98 | if (fieldValue != null) {
99 | Class> columnType = columnField.getType();
100 | if (columnType.equals(ForeignLazyLoader.class)) {
101 | columnValue = ((ForeignLazyLoader) fieldValue).getColumnValue();
102 | } else if (columnType.equals(List.class)) {
103 | try {
104 | List> foreignEntities = (List>) fieldValue;
105 | if (foreignEntities.size() > 0) {
106 |
107 | Class> foreignEntityType = ColumnUtils.getForeignEntityType(this);
108 | Column column = TableUtils.getColumnOrId(foreignEntityType, foreignColumnName);
109 | columnValue = column.getColumnValue(foreignEntities.get(0));
110 |
111 | // 仅自动关联外
112 | Table table = this.getTable();
113 | if (table != null && column instanceof Id) {
114 | for (Object foreignObj : foreignEntities) {
115 | Object idValue = column.getColumnValue(foreignObj);
116 | if (idValue == null) {
117 | table.db.saveOrUpdate(foreignObj);
118 | }
119 | }
120 | }
121 |
122 | columnValue = column.getColumnValue(foreignEntities.get(0));
123 | }
124 | } catch (Throwable e) {
125 | VolleyLog.e(e.getMessage(), e);
126 | }
127 | } else {
128 | try {
129 | Column column = TableUtils.getColumnOrId(columnType, foreignColumnName);
130 | columnValue = column.getColumnValue(fieldValue);
131 |
132 | Table table = this.getTable();
133 | if (table != null && columnValue == null && column instanceof Id) {
134 | table.db.saveOrUpdate(fieldValue);
135 | }
136 |
137 | columnValue = column.getColumnValue(fieldValue);
138 | } catch (Throwable e) {
139 | VolleyLog.e(e.getMessage(), e);
140 | }
141 | }
142 | }
143 |
144 | return columnValue;
145 | }
146 |
147 | @Override
148 | public ColumnDbType getColumnDbType() {
149 | return foreignColumnConverter.getColumnDbType();
150 | }
151 |
152 | /**
153 | * It always return null.
154 | *
155 | * @return null
156 | */
157 | @Override
158 | public Object getDefaultValue() {
159 | return null;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/com/android/volley/NetworkDispatcher.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 com.android.volley;
18 |
19 | import java.util.concurrent.BlockingQueue;
20 |
21 | import android.annotation.TargetApi;
22 | import android.net.TrafficStats;
23 | import android.os.Build;
24 | import android.os.Process;
25 |
26 | /**
27 | * Provides a thread for performing network dispatch from a queue of requests.
28 | *
29 | * Requests added to the specified queue are processed from the network via a
30 | * specified {@link Network} interface. Responses are committed to cache, if
31 | * eligible, using a specified {@link Cache} interface. Valid responses and
32 | * errors are posted back to the caller via a {@link ResponseDelivery}.
33 | */
34 | public class NetworkDispatcher extends Thread {
35 | /** The queue of requests to service. */
36 | private final BlockingQueue> mQueue;
37 | /** The network interface for processing requests. */
38 | private final Network mNetwork;
39 | /** The cache to write to. */
40 | private final Cache mCache;
41 | /** For posting responses and errors. */
42 | private final ResponseDelivery mDelivery;
43 | /** Used for telling us to die. */
44 | private volatile boolean mQuit = false;
45 |
46 | private volatile boolean mPause = false;
47 | private Object mPauseLock = new Object();
48 |
49 | /**
50 | * Creates a new network dispatcher thread. You must call {@link #start()}
51 | * in order to begin processing.
52 | *
53 | * @param queue Queue of incoming requests for triage
54 | * @param network Network interface to use for performing requests
55 | * @param cache Cache interface to use for writing responses to cache
56 | * @param delivery Delivery interface to use for posting responses
57 | */
58 | public NetworkDispatcher(BlockingQueue> queue,
59 | Network network, Cache cache,
60 | ResponseDelivery delivery) {
61 | mQueue = queue;
62 | mNetwork = network;
63 | mCache = cache;
64 | mDelivery = delivery;
65 | }
66 |
67 | /**
68 | * Forces this dispatcher to quit immediately. If any requests are still in
69 | * the queue, they are not guaranteed to be processed.
70 | */
71 | public void quit() {
72 | resumeTask();
73 | mQuit = true;
74 | interrupt();
75 | }
76 |
77 | // chenbo add start
78 | public void resumeTask() {
79 | mPause = false;
80 | synchronized (mPauseLock) {
81 | mPauseLock.notifyAll();
82 | }
83 | }
84 |
85 | public void pauseTask() {
86 | mPause = true;
87 | }
88 |
89 | // end
90 |
91 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
92 | private void addTrafficStatsTag(Request> request) {
93 | // Tag the request (if API >= 14)
94 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
95 | TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
96 | }
97 | }
98 |
99 | @Override
100 | public void run() {
101 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
102 | while (true) {
103 | Request> request;
104 | try {
105 | if (mPause) {
106 | synchronized (mPauseLock) {
107 | mPauseLock.wait();
108 | }
109 | }
110 | // Take a request from the queue.
111 | request = mQueue.take();
112 | } catch (InterruptedException e) {
113 | // We may have been interrupted because it was time to quit.
114 | if (mQuit) {
115 | return;
116 | }
117 | continue;
118 | }
119 | try {
120 | request.addMarker("network-queue-take");
121 |
122 | // If the request was cancelled already, do not perform the
123 | // network request.
124 | if (request.isCanceled()) {
125 | request.finish("network-discard-cancelled");
126 | continue;
127 | }
128 | addTrafficStatsTag(request);
129 |
130 | // Perform the network request.
131 | NetworkResponse networkResponse = mNetwork.performRequest(mDelivery, request);
132 | request.addMarker("network-http-complete");
133 |
134 | // If the server returned 304 AND we delivered a response already,
135 | // we're done -- don't deliver a second identical response.
136 | if (networkResponse.notModified && request.hasHadResponseDelivered()) {
137 | request.finish("not-modified");
138 | continue;
139 | }
140 |
141 | // Parse the response here on the worker thread.
142 | Response> response = request.parseNetworkResponse(networkResponse);
143 | request.addMarker("network-parse-complete");
144 |
145 | // Write to cache if applicable.
146 | // TODO: Only update cache metadata instead of entire record for 304s.
147 | if (request.shouldCache() && response.cacheEntry != null) {
148 | mCache.put(request.getCacheKey(), response.cacheEntry);
149 | request.addMarker("network-cache-written");
150 | }
151 |
152 | // Post the response back.
153 | request.markDelivered();
154 | mDelivery.postResponse(request, response);
155 | } catch (VolleyError volleyError) {
156 | parseAndDeliverNetworkError(request, volleyError);
157 | } catch (Exception e) {
158 | VolleyLog.e(e, "Unhandled exception %s", e.toString());
159 | mDelivery.postError(request, new VolleyError(e));
160 | }
161 | }
162 | }
163 |
164 | private void parseAndDeliverNetworkError(Request> request, VolleyError error) {
165 | error = request.parseNetworkError(error);
166 | mDelivery.postError(request, error);
167 | }
168 | }
169 |
--------------------------------------------------------------------------------