├── demo ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── styles.xml │ │ │ │ └── dimens.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ └── layout │ │ │ │ └── activity_main.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── jude │ │ │ │ └── requestvolly │ │ │ │ ├── Person.java │ │ │ │ ├── callback │ │ │ │ ├── LinkCallback.java │ │ │ │ └── DataCallback.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── jude │ │ └── requestvolly │ │ └── ApplicationTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── requestvolley ├── .gitignore ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── jude │ │ │ ├── http │ │ │ ├── RequestListener.java │ │ │ ├── LoadListener.java │ │ │ ├── LoadController.java │ │ │ ├── ByteArrayLoadController.java │ │ │ ├── ByteArrayRequest.java │ │ │ ├── NetworkImageCache.java │ │ │ ├── RequestManager.java │ │ │ └── RequestMap.java │ │ │ └── volley │ │ │ ├── TimeoutError.java │ │ │ ├── ServerError.java │ │ │ ├── NoConnectionError.java │ │ │ ├── ParseError.java │ │ │ ├── Network.java │ │ │ ├── NetworkError.java │ │ │ ├── toolbox │ │ │ ├── Authenticator.java │ │ │ ├── NoCache.java │ │ │ ├── HttpStack.java │ │ │ ├── ClearCacheRequest.java │ │ │ ├── Volley.java │ │ │ ├── PoolingByteArrayOutputStream.java │ │ │ ├── AndroidAuthenticator.java │ │ │ ├── RequestFuture.java │ │ │ ├── HttpHeaderParser.java │ │ │ ├── ByteArrayPool.java │ │ │ ├── HttpClientStack.java │ │ │ ├── NetworkImageView.java │ │ │ ├── ImageRequest.java │ │ │ ├── HurlStack.java │ │ │ ├── BasicNetwork.java │ │ │ └── DiskBasedCache.java │ │ │ ├── ResponseDelivery.java │ │ │ ├── RetryPolicy.java │ │ │ ├── VolleyError.java │ │ │ ├── AuthFailureError.java │ │ │ ├── NetworkResponse.java │ │ │ ├── Response.java │ │ │ ├── Cache.java │ │ │ ├── DefaultRetryPolicy.java │ │ │ ├── ExecutorDelivery.java │ │ │ ├── NetworkDispatcher.java │ │ │ ├── CacheDispatcher.java │ │ │ ├── VolleyLog.java │ │ │ └── RequestQueue.java │ │ └── AndroidManifest.xml └── build.gradle ├── log.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo', 'requestvolley' 2 | -------------------------------------------------------------------------------- /requestvolley/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | signing.properties 3 | -------------------------------------------------------------------------------- /log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jude95/RequestVolley/HEAD/log.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jude95/RequestVolley/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jude95/RequestVolley/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jude95/RequestVolley/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jude95/RequestVolley/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jude95/RequestVolley/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/RequestListener.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | public interface RequestListener { 4 | void onRequest(); 5 | void onSuccess(String response); 6 | void onError(String errorMsg); 7 | } 8 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RequestVolly 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /requestvolley/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/LoadListener.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | /** 4 | * LoadListener special for ByteArrayLoadControler 5 | * 6 | * @author steven-pan 7 | * 8 | */ 9 | public interface LoadListener { 10 | 11 | void onStart(); 12 | 13 | void onSuccess(byte[] data, String url); 14 | 15 | void onError(String errorMsg, String url); 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo/src/androidTest/java/com/jude/requestvolly/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jude.requestvolly; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .gradle 3 | .DS_Store 4 | /captures 5 | 6 | # built application files 7 | *.apk 8 | 9 | # files for the dex VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # generated files 16 | bin/ 17 | gen/ 18 | build 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Eclipse project files 24 | .classpath 25 | .project 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Intellij project files 31 | *.iml 32 | *.ipr 33 | *.iws 34 | .idea 35 | 36 | -------------------------------------------------------------------------------- /demo/src/main/java/com/jude/requestvolly/Person.java: -------------------------------------------------------------------------------- 1 | package com.jude.requestvolly; 2 | 3 | /** 4 | * Created by Mr.Jude on 2015/7/17. 5 | */ 6 | public class Person { 7 | private String name; 8 | private int age; 9 | 10 | public String getName() { 11 | return name; 12 | } 13 | 14 | public void setName(String name) { 15 | this.name = name; 16 | } 17 | 18 | public int getAge() { 19 | return age; 20 | } 21 | 22 | public void setAge(int age) { 23 | this.age = age; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\software\android-sdk_r24.0.2-windows\android-sdk-windows/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.jude.requestvolly" 9 | minSdkVersion 15 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | compile 'com.android.support:appcompat-v7:22.2.0' 25 | compile 'com.google.code.gson:gson:2.3.1' 26 | compile project(':requestvolley') 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /demo/src/main/java/com/jude/requestvolly/callback/LinkCallback.java: -------------------------------------------------------------------------------- 1 | package com.jude.requestvolly.callback; 2 | 3 | 4 | import com.jude.http.RequestListener; 5 | 6 | /** 7 | * Created by Mr.Jude on 2015/6/13. 8 | */ 9 | class LinkCallback implements RequestListener { 10 | private LinkCallback link; 11 | public LinkCallback add(LinkCallback other){ 12 | other.setLink(this); 13 | return other; 14 | } 15 | private void setLink(LinkCallback link){ 16 | this.link = link; 17 | } 18 | 19 | @Override 20 | public void onRequest() { 21 | if (link != null) 22 | link.onRequest(); 23 | } 24 | 25 | @Override 26 | public void onSuccess(String s) { 27 | if (link != null) 28 | link.onSuccess(s); 29 | } 30 | 31 | @Override 32 | public void onError(String s) { 33 | if (link != null) 34 | link.onError(s); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/TimeoutError.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.jude.volley; 18 | 19 | /** 20 | * Indicates that the connection or the socket timed out. 21 | */ 22 | @SuppressWarnings("serial") 23 | public class TimeoutError extends VolleyError { } 24 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/LoadController.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | import com.jude.volley.Request; 4 | 5 | /** 6 | * LoadControler for Request 7 | * 8 | * @author steven pan 9 | * 10 | */ 11 | public interface LoadController { 12 | void cancel(); 13 | Request getmRequest(); 14 | } 15 | 16 | /** 17 | * Abstract LoaderControler that implements LoadControler 18 | * 19 | * @author steven pan 20 | * 21 | */ 22 | class AbsLoadController implements LoadController { 23 | 24 | protected Request mRequest; 25 | 26 | public Request getmRequest() { 27 | return mRequest; 28 | } 29 | 30 | public void bindRequest(Request request) { 31 | this.mRequest = request; 32 | } 33 | 34 | @Override 35 | public void cancel() { 36 | if (this.mRequest != null) { 37 | this.mRequest.cancel(); 38 | } 39 | } 40 | 41 | protected String getOriginUrl() { 42 | return this.mRequest.getOriginUrl(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/ServerError.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.jude.volley; 18 | 19 | /** 20 | * Indicates that the error responded with an error response. 21 | */ 22 | @SuppressWarnings("serial") 23 | public class ServerError extends VolleyError { 24 | public ServerError(NetworkResponse networkResponse) { 25 | super(networkResponse); 26 | } 27 | 28 | public ServerError() { 29 | super(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/NoConnectionError.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.jude.volley; 18 | 19 | /** 20 | * Error indicating that no connection could be established when performing a Volley request. 21 | */ 22 | @SuppressWarnings("serial") 23 | public class NoConnectionError extends NetworkError { 24 | public NoConnectionError() { 25 | super(); 26 | } 27 | 28 | public NoConnectionError(Throwable reason) { 29 | super(reason); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/ParseError.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.jude.volley; 18 | 19 | /** 20 | * Indicates that the server's response could not be parsed. 21 | */ 22 | @SuppressWarnings("serial") 23 | public class ParseError extends VolleyError { 24 | public ParseError() { } 25 | 26 | public ParseError(NetworkResponse networkResponse) { 27 | super(networkResponse); 28 | } 29 | 30 | public ParseError(Throwable cause) { 31 | super(cause); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/Network.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.jude.volley; 18 | 19 | /** 20 | * An interface for performing requests. 21 | */ 22 | public interface Network { 23 | /** 24 | * Performs the specified request. 25 | * @param request Request to process 26 | * @return A {@link NetworkResponse} with data and caching metadata; will never be null 27 | * @throws VolleyError on errors 28 | */ 29 | public NetworkResponse performRequest(Request request) throws VolleyError; 30 | } 31 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/ByteArrayLoadController.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | import com.jude.volley.VolleyError; 4 | import com.jude.volley.Response; 5 | 6 | /** 7 | * ByteArrayLoadControler implements Volley Listener & ErrorListener 8 | * 9 | * @author steven pan 10 | * 11 | */ 12 | class ByteArrayLoadController extends AbsLoadController implements Response.Listener, Response.ErrorListener { 13 | 14 | private LoadListener mOnLoadListener; 15 | 16 | 17 | public ByteArrayLoadController(LoadListener requestListener) { 18 | this.mOnLoadListener = requestListener; 19 | } 20 | 21 | @Override 22 | public void onErrorResponse(VolleyError error) { 23 | String errorMsg = null; 24 | if (error.getMessage() != null) { 25 | errorMsg = error.getMessage(); 26 | } else { 27 | try { 28 | errorMsg = "Server Response Error (" + error.networkResponse.statusCode + ")"; 29 | } catch (Exception e) { 30 | errorMsg = "Server Response Error"; 31 | } 32 | } 33 | this.mOnLoadListener.onError(errorMsg, getOriginUrl()); 34 | } 35 | 36 | @Override 37 | public void onResponse(byte[] response) { 38 | this.mOnLoadListener.onSuccess(response, getOriginUrl()); 39 | } 40 | } -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/NetworkError.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.jude.volley; 18 | 19 | /** 20 | * Indicates that there was a network error when performing a Volley request. 21 | */ 22 | @SuppressWarnings("serial") 23 | public class NetworkError extends VolleyError { 24 | public NetworkError() { 25 | super(); 26 | } 27 | 28 | public NetworkError(Throwable cause) { 29 | super(cause); 30 | } 31 | 32 | public NetworkError(NetworkResponse networkResponse) { 33 | super(networkResponse); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/Authenticator.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.AuthFailureError; 20 | 21 | /** 22 | * An interface for interacting with auth tokens. 23 | */ 24 | public interface Authenticator { 25 | /** 26 | * Synchronously retrieves an auth token. 27 | * 28 | * @throws AuthFailureError If authentication did not succeed 29 | */ 30 | public String getAuthToken() throws AuthFailureError; 31 | 32 | /** 33 | * Invalidates the provided auth token. 34 | */ 35 | public void invalidateAuthToken(String authToken); 36 | } 37 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/ResponseDelivery.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.jude.volley; 18 | 19 | public interface ResponseDelivery { 20 | /** 21 | * Parses a response from the network or cache and delivers it. 22 | */ 23 | public void postResponse(Request request, Response response); 24 | 25 | /** 26 | * Parses a response from the network or cache and delivers it. The provided 27 | * Runnable will be executed after delivery. 28 | */ 29 | public void postResponse(Request request, Response response, Runnable runnable); 30 | 31 | /** 32 | * Posts an error for the given request. 33 | */ 34 | public void postError(Request request, VolleyError error); 35 | } 36 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/NoCache.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.Cache; 20 | 21 | /** 22 | * A cache that doesn't. 23 | */ 24 | public class NoCache implements Cache { 25 | @Override 26 | public void clear() { 27 | } 28 | 29 | @Override 30 | public Entry get(String key) { 31 | return null; 32 | } 33 | 34 | @Override 35 | public void put(String key, Entry entry) { 36 | } 37 | 38 | @Override 39 | public void invalidate(String key, boolean fullExpire) { 40 | } 41 | 42 | @Override 43 | public void remove(String key) { 44 | } 45 | 46 | @Override 47 | public void initialize() { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/RetryPolicy.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.jude.volley; 18 | 19 | /** 20 | * Retry policy for a request. 21 | */ 22 | public interface RetryPolicy { 23 | 24 | /** 25 | * Returns the current timeout (used for logging). 26 | */ 27 | public int getCurrentTimeout(); 28 | 29 | /** 30 | * Returns the current retry count (used for logging). 31 | */ 32 | public int getCurrentRetryCount(); 33 | 34 | /** 35 | * Prepares for the next retry by applying a backoff to the timeout. 36 | * @param error The error code of the last attempt. 37 | * @throws VolleyError In the event that the retry could not be performed (for example if we 38 | * ran out of attempts), the passed in error is thrown. 39 | */ 40 | public void retry(VolleyError error) throws VolleyError; 41 | } 42 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/VolleyError.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.jude.volley; 18 | 19 | /** 20 | * Exception style class encapsulating Volley errors 21 | */ 22 | @SuppressWarnings("serial") 23 | public class VolleyError extends Exception { 24 | public final NetworkResponse networkResponse; 25 | 26 | public VolleyError() { 27 | networkResponse = null; 28 | } 29 | 30 | public VolleyError(NetworkResponse response) { 31 | networkResponse = response; 32 | } 33 | 34 | public VolleyError(String exceptionMessage) { 35 | super(exceptionMessage); 36 | networkResponse = null; 37 | } 38 | 39 | public VolleyError(String exceptionMessage, Throwable reason) { 40 | super(exceptionMessage, reason); 41 | networkResponse = null; 42 | } 43 | 44 | public VolleyError(Throwable cause) { 45 | super(cause); 46 | networkResponse = null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /requestvolley/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 12 9 | targetSdkVersion 22 10 | } 11 | 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 16 | } 17 | } 18 | sourceSets { 19 | main { 20 | jniLibs.srcDirs = ['libs'] 21 | } 22 | } 23 | } 24 | dependencies { 25 | compile fileTree(include: ['*.jar'], dir: 'libs') 26 | } 27 | 28 | ext { 29 | bintrayRepo = 'maven'////bintray上的仓库名,一般为maven 30 | bintrayName = 'requestvolley'//bintray上的项目名 31 | 32 | publishedGroupId = 'com.jude'//JCenter的GroupId 33 | artifact = 'requestvolley'//JCenter的ArtifactId 34 | 35 | siteUrl = 'https://github.com/Jude95/RequestVolley' 36 | gitUrl = 'https://github.com/Jude95/RequestVolley' 37 | 38 | libraryVersion = '1.0.4'//版本号 39 | libraryName = 'requestvolley'//项目名字,没什么用 40 | libraryDescription = 'A tool for Android'//项目描述,没什么用 41 | 42 | //开发者信息 43 | developerId = 'jude95' 44 | developerName = 'jude95' 45 | developerEmail = 'jude@helloworld.moe' 46 | 47 | //以上所有信息自行修改,以下不变 48 | 49 | licenseName = 'The Apache Software License, Version 2.0' 50 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 51 | allLicenses = ["Apache-2.0"] 52 | } 53 | apply from:'https://raw.githubusercontent.com/Jude95/JCenter/master/install.gradle' 54 | apply from:'https://raw.githubusercontent.com/Jude95/JCenter/master/bintray.gradle' -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/HttpStack.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.AuthFailureError; 20 | import com.jude.volley.Request; 21 | 22 | import org.apache.http.HttpResponse; 23 | 24 | import java.io.IOException; 25 | import java.util.Map; 26 | 27 | /** 28 | * An HTTP stack abstraction. 29 | */ 30 | public interface HttpStack { 31 | /** 32 | * Performs an HTTP request with the given parameters. 33 | * 34 | *

A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, 35 | * and the Content-Type header is set to request.getPostBodyContentType().

36 | * 37 | * @param request the request to perform 38 | * @param additionalHeaders additional headers to be sent together with 39 | * {@link Request#getHeaders()} 40 | * @return the HTTP response 41 | */ 42 | public HttpResponse performRequest(Request request, Map additionalHeaders) 43 | throws IOException, AuthFailureError; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/AuthFailureError.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.jude.volley; 18 | 19 | import android.content.Intent; 20 | 21 | /** 22 | * Error indicating that there was an authentication failure when performing a Request. 23 | */ 24 | @SuppressWarnings("serial") 25 | public class AuthFailureError extends VolleyError { 26 | /** An intent that can be used to resolve this exception. (Brings up the password dialog.) */ 27 | private Intent mResolutionIntent; 28 | 29 | public AuthFailureError() { } 30 | 31 | public AuthFailureError(Intent intent) { 32 | mResolutionIntent = intent; 33 | } 34 | 35 | public AuthFailureError(NetworkResponse response) { 36 | super(response); 37 | } 38 | 39 | public AuthFailureError(String message) { 40 | super(message); 41 | } 42 | 43 | public AuthFailureError(String message, Exception reason) { 44 | super(message, reason); 45 | } 46 | 47 | public Intent getResolutionIntent() { 48 | return mResolutionIntent; 49 | } 50 | 51 | @Override 52 | public String getMessage() { 53 | if (mResolutionIntent != null) { 54 | return "User needs to (re)enter credentials."; 55 | } 56 | return super.getMessage(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demo/src/main/java/com/jude/requestvolly/callback/DataCallback.java: -------------------------------------------------------------------------------- 1 | package com.jude.requestvolly.callback; 2 | 3 | 4 | import com.google.gson.Gson; 5 | 6 | import org.json.JSONObject; 7 | 8 | import java.lang.reflect.ParameterizedType; 9 | 10 | /** 11 | * Created by zhuchenxi on 15/5/11. 12 | */ 13 | public abstract class DataCallback extends LinkCallback { 14 | 15 | @Override 16 | public void onRequest() { 17 | super.onRequest(); 18 | } 19 | 20 | @Override 21 | public void onSuccess(String s) { 22 | JSONObject jsonObject; 23 | int status = 0; 24 | String info = ""; 25 | T data = null; 26 | try { 27 | jsonObject = new JSONObject(s); 28 | status = jsonObject.getInt("status"); 29 | info = jsonObject.getString("info"); 30 | if (status == 200){ 31 | Gson gson = new Gson(); 32 | data = gson.fromJson(jsonObject.getString("data"), ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); 33 | } 34 | } catch (Exception e) { 35 | error("数据解析错误"); 36 | return ; 37 | } 38 | result(status, info); 39 | if (status == 200){ 40 | success(info,data); 41 | }else if (status == 400){ 42 | authorizationFailure(); 43 | }else if (status == 0){ 44 | failure(info); 45 | }else{ 46 | error(info); 47 | } 48 | super.onSuccess(s); 49 | } 50 | 51 | @Override 52 | public void onError(String s) { 53 | result(-1,"网络错误"); 54 | error("网络错误"); 55 | super.onError(s); 56 | } 57 | 58 | public void result(int status, String info){} 59 | public abstract void success(String info,T data); 60 | public void failure(String info){ 61 | 62 | } 63 | public void authorizationFailure(){} 64 | public void error(String errorInfo){ 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/NetworkResponse.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.jude.volley; 18 | 19 | import org.apache.http.HttpStatus; 20 | 21 | import java.util.Collections; 22 | import java.util.Map; 23 | 24 | /** 25 | * Data and headers returned from {@link Network#performRequest(Request)}. 26 | */ 27 | public class NetworkResponse { 28 | /** 29 | * Creates a new network response. 30 | * @param statusCode the HTTP status code 31 | * @param data Response body 32 | * @param headers Headers returned with this response, or null for none 33 | * @param notModified True if the server returned a 304 and the data was already in cache 34 | */ 35 | public NetworkResponse(int statusCode, byte[] data, Map headers, 36 | boolean notModified) { 37 | this.statusCode = statusCode; 38 | this.data = data; 39 | this.headers = headers; 40 | this.notModified = notModified; 41 | } 42 | 43 | public NetworkResponse(byte[] data) { 44 | this(HttpStatus.SC_OK, data, Collections.emptyMap(), false); 45 | } 46 | 47 | public NetworkResponse(byte[] data, Map headers) { 48 | this(HttpStatus.SC_OK, data, headers, false); 49 | } 50 | 51 | /** The HTTP status code. */ 52 | public final int statusCode; 53 | 54 | /** Raw data from this response. */ 55 | public final byte[] data; 56 | 57 | /** Response headers. */ 58 | public final Map headers; 59 | 60 | /** True if the server returned a 304 (Not Modified). */ 61 | public final boolean notModified; 62 | } -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/ClearCacheRequest.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.Cache; 20 | import com.jude.volley.NetworkResponse; 21 | import com.jude.volley.Request; 22 | import com.jude.volley.Response; 23 | 24 | import android.os.Handler; 25 | import android.os.Looper; 26 | 27 | /** 28 | * A synthetic request used for clearing the cache. 29 | */ 30 | public class ClearCacheRequest extends Request { 31 | private final Cache mCache; 32 | private final Runnable mCallback; 33 | 34 | /** 35 | * Creates a synthetic request for clearing the cache. 36 | * @param cache Cache to clear 37 | * @param callback Callback to make on the main thread once the cache is clear, 38 | * or null for none 39 | */ 40 | public ClearCacheRequest(Cache cache, Runnable callback) { 41 | super(Method.GET, null, null); 42 | mCache = cache; 43 | mCallback = callback; 44 | } 45 | 46 | @Override 47 | public boolean isCanceled() { 48 | // This is a little bit of a hack, but hey, why not. 49 | mCache.clear(); 50 | if (mCallback != null) { 51 | Handler handler = new Handler(Looper.getMainLooper()); 52 | handler.postAtFrontOfQueue(mCallback); 53 | } 54 | return true; 55 | } 56 | 57 | @Override 58 | public Priority getPriority() { 59 | return Priority.IMMEDIATE; 60 | } 61 | 62 | @Override 63 | protected Response parseNetworkResponse(NetworkResponse response) { 64 | return null; 65 | } 66 | 67 | @Override 68 | protected void deliverResponse(Object response) { 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RequestVolley 2 | 基于volley的网络请求库 3 | 4 | 因为volley用起来比较麻烦。封装一下函数式调用更加方便。是基于某个库(= =,已找不到出处)修改的。 5 | 功能都集中在`RequestManager`里面 6 | 7 | ##依赖 8 | `compile 'com.jude:requestvolley:1.0.4'` 9 | 10 | 在APP入口处调用 11 | `RequestManager.getInstance().init(this);` 12 | 建议这样设置调试模式,所有的网络请求都将排上序号输出。调试模式神好用!!! 13 | `RequestManager.getInstance().setDebugMode(BuildConfig.DEBUG,"net");` 14 | ![log.png](log.png) 15 | 调试模式会将所有PostGet请求参数与结果输出出来(Post会转成Get格式)。 16 | 17 | ##get/post简单调用 18 | `LoadController get(String url, RequestListener requestListener)` 19 | `LoadController post(final String url, Object data, final RequestListener requestListener)` 20 | post第二个参数,将调用`data.toString()`来获取post数据。 21 | 提供RequestMap来供添加文本与文件作为表单。 22 | 23 | RequestMap params = new RequestMap(); 24 | params.put("id","123"); 25 | params.put("id","123"); 26 | params.put("file",new File("xxx")); 27 | RequestManager.getInstance().post(url,params,callback); 28 | 可以重复添加相同key的文本。一次只能上传一个文件。 29 | 可以通过LoadController取消本次请求。 30 | 31 | ##图片请求 32 | `ImageLoader.ImageContainer img(final String url,final ImageView imageView)` 33 | `ImageLoader.ImageContainer img(final String url,final ImageView imageView, final int resError)` 34 | `ImageLoader.ImageContainer img(final String url,final ImageLoader.ImageListener imageListener)` 35 | 很方便的图片请求,10M内存缓存与永久的文件缓存。将自动处理。 36 | 不过,都去用Fresco吧。这个比起来弱爆了。 37 | 38 | ##全局设置 39 | `void setHeader(HashMap header)` 40 | `void setCacheEnable(boolean isCache)` 41 | `void setRetryTimes(int times)` 42 | `void setTimeOut(int time)` 43 | 注意默认是不开启缓存。避免缓存导致调试问题。这些为全局设置。get与post均允许发送特殊请求。 44 | 45 | ##缓存机制 46 | 对volley的缓存机制作了些修改,volley源码中这一部分本来也是这个意思,但不知道为什么并没有完成,阉割了? 47 | 根据响应头头的`Cache-Control`字段中。`max-age=`表示有效时间,`soft-age=`表示新鲜时间。 48 | 在新鲜时间之前。只会读取缓存。 49 | 在新鲜时间与有效时间之间。会先读取缓存再网络请求。会返回2次。 50 | 超过有效时间。直接网络请求。 51 | 52 | **另外貌似volley也支持https。也被阉割了。后期再试试完善https部分吧。** 53 | 54 | ##解析 55 | demo中的DataCallback负责解析。用法很优雅。实现在demo里,本来想放进库里,但与项目耦合太高。网络库也并不负责解析。 56 | 57 | RequestManager.getInstance().post("https://apiview.com/test/408/RequestVolley/getPerson", null, new DataCallback() { 58 | @Override 59 | public void success(String info, Person data) { 60 | tvPerson.setText(data.getName()+":"+data.getAge()); 61 | } 62 | }); 63 | 64 | 65 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /demo/src/main/java/com/jude/requestvolly/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jude.requestvolly; 2 | 3 | import android.support.v7.app.ActionBarActivity; 4 | import android.os.Bundle; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.jude.http.RequestListener; 11 | import com.jude.http.RequestManager; 12 | import com.jude.http.RequestMap; 13 | import com.jude.requestvolly.callback.DataCallback; 14 | 15 | 16 | public class MainActivity extends ActionBarActivity { 17 | private TextView text; 18 | private TextView tvPerson; 19 | private ImageView image; 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | text = (TextView) findViewById(R.id.text); 25 | tvPerson = (TextView) findViewById(R.id.tv_person); 26 | image = (ImageView) findViewById(R.id.image); 27 | RequestManager.getInstance().init(this); 28 | RequestManager.getInstance().get("https://apiview.com/test/408/RequestVolly/getPerson", new RequestListener() { 29 | @Override 30 | public void onRequest() { 31 | 32 | } 33 | 34 | @Override 35 | public void onSuccess(String response) { 36 | text.setText(response); 37 | } 38 | 39 | @Override 40 | public void onError(String errorMsg) { 41 | 42 | } 43 | }); 44 | RequestMap params = new RequestMap(); 45 | params.put("id","213"); 46 | RequestManager.getInstance().post("https://apiview.com/test/408/RequestVolly/getPerson", params, new DataCallback() { 47 | @Override 48 | public void success(String info, Person data) { 49 | tvPerson.setText(data.getName()+":"+data.getAge()); 50 | } 51 | }); 52 | RequestManager.getInstance().img("http://img2.imgtn.bdimg.com/it/u=2660800756,2021530274&fm=21&gp=0.jpg",image); 53 | 54 | } 55 | 56 | @Override 57 | public boolean onCreateOptionsMenu(Menu menu) { 58 | // Inflate the menu; this adds items to the action bar if it is present. 59 | getMenuInflater().inflate(R.menu.menu_main, menu); 60 | return true; 61 | } 62 | 63 | @Override 64 | public boolean onOptionsItemSelected(MenuItem item) { 65 | // Handle action bar item clicks here. The action bar will 66 | // automatically handle clicks on the Home/Up button, so long 67 | // as you specify a parent activity in AndroidManifest.xml. 68 | int id = item.getItemId(); 69 | 70 | //noinspection SimplifiableIfStatement 71 | if (id == R.id.action_settings) { 72 | return true; 73 | } 74 | 75 | return super.onOptionsItemSelected(item); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/Response.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.jude.volley; 18 | 19 | /** 20 | * Encapsulates a parsed response for delivery. 21 | * 22 | * @param Parsed type of this response 23 | */ 24 | public class Response { 25 | 26 | /** Callback interface for delivering parsed responses. */ 27 | public interface Listener { 28 | /** Called when a response is received. */ 29 | public void onResponse(T response); 30 | } 31 | 32 | /** Callback interface for delivering error responses. */ 33 | public interface ErrorListener { 34 | /** 35 | * Callback method that an error has been occurred with the 36 | * provided error code and optional user-readable message. 37 | */ 38 | public void onErrorResponse(VolleyError error); 39 | } 40 | 41 | /** Returns a successful response containing the parsed result. */ 42 | public static Response success(T result, Cache.Entry cacheEntry) { 43 | return new Response(result, cacheEntry); 44 | } 45 | 46 | /** 47 | * Returns a failed response containing the given error code and an optional 48 | * localized message displayed to the user. 49 | */ 50 | public static Response error(VolleyError error) { 51 | return new Response(error); 52 | } 53 | 54 | /** Parsed response, or null in the case of error. */ 55 | public final T result; 56 | 57 | /** Cache metadata for this response, or null in the case of error. */ 58 | public final Cache.Entry cacheEntry; 59 | 60 | /** Detailed error information if errorCode != OK. */ 61 | public final VolleyError error; 62 | 63 | /** True if this response was a soft-expired one and a second one MAY be coming. */ 64 | public boolean intermediate = false; 65 | 66 | /** 67 | * Returns whether this response is considered successful. 68 | */ 69 | public boolean isSuccess() { 70 | return error == null; 71 | } 72 | 73 | 74 | private Response(T result, Cache.Entry cacheEntry) { 75 | this.result = result; 76 | this.cacheEntry = cacheEntry; 77 | this.error = null; 78 | } 79 | 80 | private Response(VolleyError error) { 81 | this.result = null; 82 | this.cacheEntry = null; 83 | this.error = error; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/Cache.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.jude.volley; 18 | 19 | import java.util.Collections; 20 | import java.util.Map; 21 | 22 | /** 23 | * An interface for a cache keyed by a String with a byte array as data. 24 | */ 25 | public interface Cache { 26 | /** 27 | * Retrieves an entry from the cache. 28 | * @param key Cache key 29 | * @return An {@link Entry} or null in the event of a cache miss 30 | */ 31 | public Entry get(String key); 32 | 33 | /** 34 | * Adds or replaces an entry to the cache. 35 | * @param key Cache key 36 | * @param entry Data to store and metadata for cache coherency, TTL, etc. 37 | */ 38 | public void put(String key, Entry entry); 39 | 40 | /** 41 | * Performs any potentially long-running actions needed to initialize the cache; 42 | * will be called from a worker thread. 43 | */ 44 | public void initialize(); 45 | 46 | /** 47 | * Invalidates an entry in the cache. 48 | * @param key Cache key 49 | * @param fullExpire True to fully expire the entry, false to soft expire 50 | */ 51 | public void invalidate(String key, boolean fullExpire); 52 | 53 | /** 54 | * Removes an entry from the cache. 55 | * @param key Cache key 56 | */ 57 | public void remove(String key); 58 | 59 | /** 60 | * Empties the cache. 61 | */ 62 | public void clear(); 63 | 64 | /** 65 | * Data and metadata for an entry returned by the cache. 66 | */ 67 | public static class Entry { 68 | /** The data returned from cache. */ 69 | public byte[] data; 70 | 71 | /** ETag for cache coherency. */ 72 | public String etag; 73 | 74 | /** Date of this response as reported by the server. */ 75 | public long serverDate; 76 | 77 | /** TTL for this record. */ 78 | public long ttl; 79 | 80 | /** Soft TTL for this record. */ 81 | public long softTtl; 82 | 83 | /** Immutable response headers as received from server; must be non-null. */ 84 | public Map responseHeaders = Collections.emptyMap(); 85 | 86 | /** True if the entry is expired. */ 87 | public boolean isExpired() { 88 | return this.ttl < System.currentTimeMillis(); 89 | } 90 | 91 | /** True if a refresh is needed from the original data source. */ 92 | public boolean refreshNeeded() { 93 | return this.softTtl < System.currentTimeMillis(); 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/Volley.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.jude.volley.toolbox; 18 | 19 | import android.content.Context; 20 | import android.content.pm.PackageInfo; 21 | import android.content.pm.PackageManager.NameNotFoundException; 22 | import android.net.http.AndroidHttpClient; 23 | import android.os.Build; 24 | 25 | import com.jude.volley.Network; 26 | import com.jude.volley.RequestQueue; 27 | 28 | import java.io.File; 29 | 30 | public class Volley { 31 | 32 | /** Default on-disk cache directory. */ 33 | private static final String DEFAULT_CACHE_DIR = "volley"; 34 | 35 | /** 36 | * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. 37 | * 38 | * @param context A {@link Context} to use for creating the cache dir. 39 | * @param stack An {@link HttpStack} to use for the network, or null for default. 40 | * @return A started {@link RequestQueue} instance. 41 | */ 42 | public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 43 | File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); 44 | 45 | String userAgent = "volley/0"; 46 | try { 47 | String packageName = context.getPackageName(); 48 | PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); 49 | userAgent = packageName + "/" + info.versionCode; 50 | } catch (NameNotFoundException e) { 51 | } 52 | 53 | if (stack == null) { 54 | if (Build.VERSION.SDK_INT >= 9) { 55 | stack = new HurlStack(); 56 | } else { 57 | // Prior to Gingerbread, HttpUrlConnection was unreliable. 58 | // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html 59 | stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); 60 | } 61 | } 62 | 63 | Network network = new BasicNetwork(stack); 64 | 65 | RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); 66 | queue.start(); 67 | 68 | return queue; 69 | } 70 | 71 | /** 72 | * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. 73 | * 74 | * @param context A {@link Context} to use for creating the cache dir. 75 | * @return A started {@link RequestQueue} instance. 76 | */ 77 | public static RequestQueue newRequestQueue(Context context) { 78 | return newRequestQueue(context, null); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/DefaultRetryPolicy.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.jude.volley; 18 | 19 | /** 20 | * Default retry policy for requests. 21 | */ 22 | public class DefaultRetryPolicy implements RetryPolicy { 23 | /** The current timeout in milliseconds. */ 24 | private int mCurrentTimeoutMs; 25 | 26 | /** The current retry count. */ 27 | private int mCurrentRetryCount; 28 | 29 | /** The maximum number of attempts. */ 30 | private final int mMaxNumRetries; 31 | 32 | /** The backoff multiplier for for the policy. */ 33 | private final float mBackoffMultiplier; 34 | 35 | /** The default socket timeout in milliseconds */ 36 | public static final int DEFAULT_TIMEOUT_MS = 2500; 37 | 38 | /** The default number of retries */ 39 | public static final int DEFAULT_MAX_RETRIES = 1; 40 | 41 | /** The default backoff multiplier */ 42 | public static final float DEFAULT_BACKOFF_MULT = 1f; 43 | 44 | /** 45 | * Constructs a new retry policy using the default timeouts. 46 | */ 47 | public DefaultRetryPolicy() { 48 | this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT); 49 | } 50 | 51 | /** 52 | * Constructs a new retry policy. 53 | * @param initialTimeoutMs The initial timeout for the policy. 54 | * @param maxNumRetries The maximum number of retries. 55 | * @param backoffMultiplier Backoff multiplier for the policy. 56 | */ 57 | public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) { 58 | mCurrentTimeoutMs = initialTimeoutMs; 59 | mMaxNumRetries = maxNumRetries; 60 | mBackoffMultiplier = backoffMultiplier; 61 | } 62 | 63 | /** 64 | * Returns the current timeout. 65 | */ 66 | @Override 67 | public int getCurrentTimeout() { 68 | return mCurrentTimeoutMs; 69 | } 70 | 71 | /** 72 | * Returns the current retry count. 73 | */ 74 | @Override 75 | public int getCurrentRetryCount() { 76 | return mCurrentRetryCount; 77 | } 78 | 79 | /** 80 | * Prepares for the next retry by applying a backoff to the timeout. 81 | * @param error The error code of the last attempt. 82 | */ 83 | @Override 84 | public void retry(VolleyError error) throws VolleyError { 85 | mCurrentRetryCount++; 86 | mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); 87 | if (!hasAttemptRemaining()) { 88 | throw error; 89 | } 90 | } 91 | 92 | /** 93 | * Returns true if this policy has attempts remaining, false otherwise. 94 | */ 95 | protected boolean hasAttemptRemaining() { 96 | return mCurrentRetryCount <= mMaxNumRetries; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/PoolingByteArrayOutputStream.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.jude.volley.toolbox; 18 | 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | 22 | /** 23 | * A variation of {@link java.io.ByteArrayOutputStream} that uses a pool of byte[] buffers instead 24 | * of always allocating them fresh, saving on heap churn. 25 | */ 26 | public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { 27 | /** 28 | * If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is 29 | * the default size to which the underlying byte array is initialized. 30 | */ 31 | private static final int DEFAULT_SIZE = 256; 32 | 33 | private final ByteArrayPool mPool; 34 | 35 | /** 36 | * Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written 37 | * to this instance, the underlying byte array will expand. 38 | */ 39 | public PoolingByteArrayOutputStream(ByteArrayPool pool) { 40 | this(pool, DEFAULT_SIZE); 41 | } 42 | 43 | /** 44 | * Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If 45 | * more than {@code size} bytes are written to this instance, the underlying byte array will 46 | * expand. 47 | * 48 | * @param size initial size for the underlying byte array. The value will be pinned to a default 49 | * minimum size. 50 | */ 51 | public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) { 52 | mPool = pool; 53 | buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE)); 54 | } 55 | 56 | @Override 57 | public void close() throws IOException { 58 | mPool.returnBuf(buf); 59 | buf = null; 60 | super.close(); 61 | } 62 | 63 | @Override 64 | public void finalize() { 65 | mPool.returnBuf(buf); 66 | } 67 | 68 | /** 69 | * Ensures there is enough space in the buffer for the given number of additional bytes. 70 | */ 71 | private void expand(int i) { 72 | /* Can the buffer handle @i more bytes, if not expand it */ 73 | if (count + i <= buf.length) { 74 | return; 75 | } 76 | byte[] newbuf = mPool.getBuf((count + i) * 2); 77 | System.arraycopy(buf, 0, newbuf, 0, count); 78 | mPool.returnBuf(buf); 79 | buf = newbuf; 80 | } 81 | 82 | @Override 83 | public synchronized void write(byte[] buffer, int offset, int len) { 84 | expand(len); 85 | super.write(buffer, offset, len); 86 | } 87 | 88 | @Override 89 | public synchronized void write(int oneByte) { 90 | expand(1); 91 | super.write(oneByte); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/ByteArrayRequest.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.UnsupportedEncodingException; 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.apache.http.HttpEntity; 11 | 12 | import com.jude.volley.AuthFailureError; 13 | import com.jude.volley.NetworkResponse; 14 | import com.jude.volley.Request; 15 | import com.jude.volley.Response; 16 | import com.jude.volley.toolbox.HttpHeaderParser; 17 | 18 | /** 19 | * ByteArrayRequest override getBody() and getParams() 20 | * 21 | * @author steven pan 22 | * 23 | */ 24 | class ByteArrayRequest extends Request { 25 | 26 | private final Response.Listener mListener; 27 | 28 | private Object mPostBody = null; 29 | 30 | private HttpEntity httpEntity =null; 31 | 32 | private Map header =null ; 33 | 34 | public ByteArrayRequest(int method, String url,Object postBody, Map header,Response.Listener listener, Response.ErrorListener errorListener) { 35 | super(method, url, errorListener); 36 | this.mPostBody = postBody; 37 | this.mListener = listener; 38 | 39 | if (this.mPostBody != null && this.mPostBody instanceof RequestMap) {// contains file 40 | this.httpEntity = ((RequestMap) this.mPostBody).getEntity(); 41 | } 42 | this.header = header; 43 | } 44 | 45 | @Override 46 | public String getCacheKey() { 47 | if(mPostBody!=null) 48 | return getUrl()+mPostBody.toString(); 49 | else 50 | return getUrl(); 51 | } 52 | 53 | /** 54 | * mPostBody is null or Map, then execute this method 55 | */ 56 | @SuppressWarnings("unchecked") 57 | protected Map getParams() throws AuthFailureError { 58 | if (this.httpEntity == null && this.mPostBody != null && this.mPostBody instanceof Map) { 59 | return ((Map) this.mPostBody);//common Map 60 | } 61 | return null;//process as json, xml or MultipartRequestParams 62 | } 63 | 64 | @Override 65 | public Map getHeaders() throws AuthFailureError { 66 | Map headers = super.getHeaders(); 67 | if (null == headers || headers.equals(Collections.emptyMap())) { 68 | headers = new HashMap(); 69 | } 70 | if(header!=null) 71 | headers.putAll(header); 72 | return headers; 73 | } 74 | 75 | @Override 76 | public String getBodyContentType() { 77 | if (httpEntity != null) { 78 | return httpEntity.getContentType().getValue(); 79 | } 80 | return null; 81 | } 82 | 83 | @Override 84 | public byte[] getBody() throws AuthFailureError { 85 | if (this.mPostBody != null && this.mPostBody instanceof String) {//process as json or xml 86 | String postString = (String) mPostBody; 87 | if (postString.length() != 0) { 88 | try { 89 | return postString.getBytes("UTF-8"); 90 | } catch (UnsupportedEncodingException e) { 91 | e.printStackTrace(); 92 | } 93 | } else { 94 | return null; 95 | } 96 | } 97 | if (this.httpEntity != null) {//process as MultipartRequestParams 98 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 99 | try { 100 | httpEntity.writeTo(baos); 101 | } catch (IOException e) { 102 | e.printStackTrace(); 103 | return null; 104 | } 105 | return baos.toByteArray(); 106 | } 107 | return super.getBody();// mPostBody is null or Map 108 | } 109 | 110 | @Override 111 | protected Response parseNetworkResponse(NetworkResponse response) { 112 | return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response)); 113 | } 114 | 115 | @Override 116 | protected void deliverResponse(byte[] response) { 117 | this.mListener.onResponse(response); 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/NetworkImageCache.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager.NameNotFoundException; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.util.LruCache; 9 | 10 | import com.jude.http.DiskLruCache.Editor; 11 | import com.jude.volley.toolbox.ImageLoader; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.security.MessageDigest; 16 | import java.security.NoSuchAlgorithmException; 17 | 18 | 19 | public class NetworkImageCache extends LruCache implements 20 | ImageLoader.ImageCache { 21 | private DiskLruCache mDiskLruCache; 22 | private static final int DiskCacheSize = 100 * 1024 * 1024;//硬盘缓存大小100M 23 | 24 | public NetworkImageCache(Context ctx) { 25 | this(ctx,getDefaultLruCacheSize()); 26 | 27 | } 28 | 29 | public NetworkImageCache(Context ctx,int sizeInKiloBytes) { 30 | super(sizeInKiloBytes); 31 | mDiskLruCache = null; 32 | try { 33 | File cacheDir = getDiskCacheDir(ctx, "bitmap"); 34 | if (!cacheDir.exists()) { 35 | cacheDir.mkdirs(); 36 | } 37 | mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(ctx), 1, DiskCacheSize); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | @Override 44 | protected int sizeOf(String key, Bitmap value) { 45 | return value.getRowBytes() * value.getHeight() / 1024; 46 | } 47 | 48 | @Override 49 | public Bitmap getBitmap(String url) { 50 | return get(url); 51 | } 52 | 53 | @Override 54 | public void putBitmap(String url, Bitmap bitmap) { 55 | put(url, bitmap); 56 | try { 57 | Editor editor = mDiskLruCache.edit(hashKeyForDisk(url)); 58 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, editor.newOutputStream(0)); 59 | editor.commit(); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | public static int getDefaultLruCacheSize() { 66 | final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 67 | final int cacheSize = maxMemory / 8; 68 | return cacheSize; 69 | } 70 | 71 | @Override 72 | protected Bitmap create(String url) { 73 | String key = hashKeyForDisk(url); 74 | Bitmap bitmap = null; 75 | try { 76 | DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); 77 | if(snapShot!=null){ 78 | bitmap = BitmapFactory.decodeStream(snapShot.getInputStream(0)); 79 | } 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | } 83 | return bitmap; 84 | } 85 | 86 | public File getDiskCacheDir(Context context, String uniqueName) { 87 | String cachePath; 88 | 89 | cachePath = context.getCacheDir().getPath(); 90 | 91 | return new File(cachePath + File.separator + uniqueName); 92 | } 93 | 94 | public int getAppVersion(Context context) { 95 | try { 96 | PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 97 | return info.versionCode; 98 | } catch (NameNotFoundException e) { 99 | e.printStackTrace(); 100 | } 101 | return 1; 102 | } 103 | 104 | public String hashKeyForDisk(String key) { 105 | String cacheKey; 106 | try { 107 | final MessageDigest mDigest = MessageDigest.getInstance("MD5"); 108 | mDigest.update(key.getBytes()); 109 | cacheKey = bytesToHexString(mDigest.digest()); 110 | } catch (NoSuchAlgorithmException e) { 111 | cacheKey = String.valueOf(key.hashCode()); 112 | } 113 | return cacheKey; 114 | } 115 | 116 | private String bytesToHexString(byte[] bytes) { 117 | StringBuilder sb = new StringBuilder(); 118 | for (int i = 0; i < bytes.length; i++) { 119 | String hex = Integer.toHexString(0xFF & bytes[i]); 120 | if (hex.length() == 1) { 121 | sb.append('0'); 122 | } 123 | sb.append(hex); 124 | } 125 | return sb.toString(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/AndroidAuthenticator.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.AuthFailureError; 20 | 21 | import android.accounts.Account; 22 | import android.accounts.AccountManager; 23 | import android.accounts.AccountManagerFuture; 24 | import android.content.Context; 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | 28 | /** 29 | * An Authenticator that uses {@link AccountManager} to get auth 30 | * tokens of a specified type for a specified account. 31 | */ 32 | public class AndroidAuthenticator implements Authenticator { 33 | private final Context mContext; 34 | private final Account mAccount; 35 | private final String mAuthTokenType; 36 | private final boolean mNotifyAuthFailure; 37 | 38 | /** 39 | * Creates a new authenticator. 40 | * @param context Context for accessing AccountManager 41 | * @param account Account to authenticate as 42 | * @param authTokenType Auth token type passed to AccountManager 43 | */ 44 | public AndroidAuthenticator(Context context, Account account, String authTokenType) { 45 | this(context, account, authTokenType, false); 46 | } 47 | 48 | /** 49 | * Creates a new authenticator. 50 | * @param context Context for accessing AccountManager 51 | * @param account Account to authenticate as 52 | * @param authTokenType Auth token type passed to AccountManager 53 | * @param notifyAuthFailure Whether to raise a notification upon auth failure 54 | */ 55 | public AndroidAuthenticator(Context context, Account account, String authTokenType, 56 | boolean notifyAuthFailure) { 57 | mContext = context; 58 | mAccount = account; 59 | mAuthTokenType = authTokenType; 60 | mNotifyAuthFailure = notifyAuthFailure; 61 | } 62 | 63 | /** 64 | * Returns the Account being used by this authenticator. 65 | */ 66 | public Account getAccount() { 67 | return mAccount; 68 | } 69 | 70 | // TODO: Figure out what to do about notifyAuthFailure 71 | @SuppressWarnings("deprecation") 72 | @Override 73 | public String getAuthToken() throws AuthFailureError { 74 | final AccountManager accountManager = AccountManager.get(mContext); 75 | AccountManagerFuture future = accountManager.getAuthToken(mAccount, 76 | mAuthTokenType, mNotifyAuthFailure, null, null); 77 | Bundle result; 78 | try { 79 | result = future.getResult(); 80 | } catch (Exception e) { 81 | throw new AuthFailureError("Error while retrieving auth token", e); 82 | } 83 | String authToken = null; 84 | if (future.isDone() && !future.isCancelled()) { 85 | if (result.containsKey(AccountManager.KEY_INTENT)) { 86 | Intent intent = result.getParcelable(AccountManager.KEY_INTENT); 87 | throw new AuthFailureError(intent); 88 | } 89 | authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 90 | } 91 | if (authToken == null) { 92 | throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType); 93 | } 94 | 95 | return authToken; 96 | } 97 | 98 | @Override 99 | public void invalidateAuthToken(String authToken) { 100 | AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/ExecutorDelivery.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.jude.volley; 18 | 19 | import android.os.Handler; 20 | 21 | import java.util.concurrent.Executor; 22 | 23 | /** 24 | * Delivers responses and errors. 25 | */ 26 | public class ExecutorDelivery implements ResponseDelivery { 27 | /** Used for posting responses, typically to the main thread. */ 28 | private final Executor mResponsePoster; 29 | 30 | /** 31 | * Creates a new response delivery interface. 32 | * @param handler {@link Handler} to post responses on 33 | */ 34 | public ExecutorDelivery(final Handler handler) { 35 | // Make an Executor that just wraps the handler. 36 | mResponsePoster = new Executor() { 37 | @Override 38 | public void execute(Runnable command) { 39 | handler.post(command); 40 | } 41 | }; 42 | } 43 | 44 | /** 45 | * Creates a new response delivery interface, mockable version 46 | * for testing. 47 | * @param executor For running delivery tasks 48 | */ 49 | public ExecutorDelivery(Executor executor) { 50 | mResponsePoster = executor; 51 | } 52 | 53 | @Override 54 | public void postResponse(Request request, Response response) { 55 | postResponse(request, response, null); 56 | } 57 | 58 | @Override 59 | public void postResponse(Request request, Response response, Runnable runnable) { 60 | request.markDelivered(); 61 | request.addMarker("post-response"); 62 | mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); 63 | } 64 | 65 | @Override 66 | public void postError(Request request, VolleyError error) { 67 | request.addMarker("post-error"); 68 | Response response = Response.error(error); 69 | mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); 70 | } 71 | 72 | /** 73 | * A Runnable used for delivering network responses to a listener on the 74 | * main thread. 75 | */ 76 | @SuppressWarnings("rawtypes") 77 | private class ResponseDeliveryRunnable implements Runnable { 78 | private final Request mRequest; 79 | private final Response mResponse; 80 | private final Runnable mRunnable; 81 | 82 | public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) { 83 | mRequest = request; 84 | mResponse = response; 85 | mRunnable = runnable; 86 | } 87 | 88 | @SuppressWarnings("unchecked") 89 | @Override 90 | public void run() { 91 | // If this request has canceled, finish it and don't deliver. 92 | if (mRequest.isCanceled()) { 93 | mRequest.finish("canceled-at-delivery"); 94 | return; 95 | } 96 | 97 | // Deliver a normal response or error, depending. 98 | if (mResponse.isSuccess()) { 99 | mRequest.deliverResponse(mResponse.result); 100 | } else { 101 | mRequest.deliverError(mResponse.error); 102 | } 103 | 104 | // If this is an intermediate response, add a marker, otherwise we're done 105 | // and the request can be finished. 106 | if (mResponse.intermediate) { 107 | mRequest.addMarker("intermediate-response"); 108 | } else { 109 | mRequest.finish("done"); 110 | } 111 | 112 | // If we have been provided a post-delivery runnable, run it. 113 | if (mRunnable != null) { 114 | mRunnable.run(); 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/RequestFuture.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.Request; 20 | import com.jude.volley.Response; 21 | import com.jude.volley.VolleyError; 22 | 23 | import java.util.concurrent.ExecutionException; 24 | import java.util.concurrent.Future; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.TimeoutException; 27 | 28 | /** 29 | * A Future that represents a Volley request. 30 | * 31 | * Used by providing as your response and error listeners. For example: 32 | *
 33 |  * RequestFuture<JSONObject> future = RequestFuture.newFuture();
 34 |  * MyRequest request = new MyRequest(URL, future, future);
 35 |  *
 36 |  * // If you want to be able to cancel the request:
 37 |  * future.setRequest(requestQueue.add(request));
 38 |  *
 39 |  * // Otherwise:
 40 |  * requestQueue.add(request);
 41 |  *
 42 |  * try {
 43 |  *   JSONObject response = future.get();
 44 |  *   // do something with response
 45 |  * } catch (InterruptedException e) {
 46 |  *   // handle the error
 47 |  * } catch (ExecutionException e) {
 48 |  *   // handle the error
 49 |  * }
 50 |  * 
51 | * 52 | * @param The type of parsed response this future expects. 53 | */ 54 | public class RequestFuture implements Future, Response.Listener, 55 | Response.ErrorListener { 56 | private Request mRequest; 57 | private boolean mResultReceived = false; 58 | private T mResult; 59 | private VolleyError mException; 60 | 61 | public static RequestFuture newFuture() { 62 | return new RequestFuture(); 63 | } 64 | 65 | private RequestFuture() {} 66 | 67 | public void setRequest(Request request) { 68 | mRequest = request; 69 | } 70 | 71 | @Override 72 | public synchronized boolean cancel(boolean mayInterruptIfRunning) { 73 | if (mRequest == null) { 74 | return false; 75 | } 76 | 77 | if (!isDone()) { 78 | mRequest.cancel(); 79 | return true; 80 | } else { 81 | return false; 82 | } 83 | } 84 | 85 | @Override 86 | public T get() throws InterruptedException, ExecutionException { 87 | try { 88 | return doGet(null); 89 | } catch (TimeoutException e) { 90 | throw new AssertionError(e); 91 | } 92 | } 93 | 94 | @Override 95 | public T get(long timeout, TimeUnit unit) 96 | throws InterruptedException, ExecutionException, TimeoutException { 97 | return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit)); 98 | } 99 | 100 | private synchronized T doGet(Long timeoutMs) 101 | throws InterruptedException, ExecutionException, TimeoutException { 102 | if (mException != null) { 103 | throw new ExecutionException(mException); 104 | } 105 | 106 | if (mResultReceived) { 107 | return mResult; 108 | } 109 | 110 | if (timeoutMs == null) { 111 | wait(0); 112 | } else if (timeoutMs > 0) { 113 | wait(timeoutMs); 114 | } 115 | 116 | if (mException != null) { 117 | throw new ExecutionException(mException); 118 | } 119 | 120 | if (!mResultReceived) { 121 | throw new TimeoutException(); 122 | } 123 | 124 | return mResult; 125 | } 126 | 127 | @Override 128 | public boolean isCancelled() { 129 | if (mRequest == null) { 130 | return false; 131 | } 132 | return mRequest.isCanceled(); 133 | } 134 | 135 | @Override 136 | public synchronized boolean isDone() { 137 | return mResultReceived || mException != null || isCancelled(); 138 | } 139 | 140 | @Override 141 | public synchronized void onResponse(T response) { 142 | mResultReceived = true; 143 | mResult = response; 144 | notifyAll(); 145 | } 146 | 147 | @Override 148 | public synchronized void onErrorResponse(VolleyError error) { 149 | mException = error; 150 | notifyAll(); 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/HttpHeaderParser.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.Cache; 20 | import com.jude.volley.NetworkResponse; 21 | 22 | import org.apache.http.impl.cookie.DateParseException; 23 | import org.apache.http.impl.cookie.DateUtils; 24 | import org.apache.http.protocol.HTTP; 25 | 26 | import java.util.Map; 27 | 28 | /** 29 | * Utility methods for parsing HTTP headers. 30 | */ 31 | public class HttpHeaderParser { 32 | 33 | /** 34 | * Extracts a {@link Cache.Entry} from a {@link NetworkResponse}. 35 | * 36 | * @param response The network response to parse headers from 37 | * @return a cache entry for the given response, or null if the response is not cacheable. 38 | */ 39 | public static Cache.Entry parseCacheHeaders(NetworkResponse response) { 40 | long now = System.currentTimeMillis(); 41 | 42 | Map headers = response.headers; 43 | 44 | long serverDate = 0; 45 | long serverExpires = 0; 46 | long softExpire = 0; 47 | long expire = 0; 48 | long softAge = 0; 49 | long maxAge = 0; 50 | boolean hasCacheControl = false; 51 | boolean hasSoftAge = false; 52 | String serverEtag = null; 53 | String headerValue; 54 | 55 | headerValue = headers.get("Date"); 56 | if (headerValue != null) { 57 | serverDate = parseDateAsEpoch(headerValue); 58 | } 59 | 60 | headerValue = headers.get("Cache-Control"); 61 | if (headerValue != null) { 62 | hasCacheControl = true; 63 | String[] tokens = headerValue.split(","); 64 | for (int i = 0; i < tokens.length; i++) { 65 | String token = tokens[i].trim(); 66 | if (token.equals("no-cache") || token.equals("no-store")) { 67 | return null; 68 | } else if (token.startsWith("max-age=")) { 69 | try { 70 | maxAge = Long.parseLong(token.substring(8)); 71 | } catch (Exception e) { 72 | } 73 | } else if (token.startsWith("soft-age=")) { 74 | try { 75 | softAge = Long.parseLong(token.substring(9)); 76 | hasSoftAge = true; 77 | } catch (Exception e) { 78 | } 79 | } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { 80 | maxAge = 0; 81 | } 82 | } 83 | } 84 | headerValue = headers.get("Expires"); 85 | if (headerValue != null) { 86 | serverExpires = parseDateAsEpoch(headerValue); 87 | } 88 | 89 | serverEtag = headers.get("ETag"); 90 | 91 | // Cache-Control takes precedence over an Expires header, even if both exist and Expires 92 | // is more restrictive. 93 | if (hasCacheControl) { 94 | expire = now + maxAge * 1000; 95 | if (!hasSoftAge){ 96 | softExpire = expire; 97 | }else{ 98 | softExpire = now + softAge * 1000; 99 | } 100 | 101 | } else if (serverDate > 0 && serverExpires >= serverDate) { 102 | // Default semantic for Expire header in HTTP specification is softExpire. 103 | expire = now + (serverExpires - serverDate); 104 | } 105 | 106 | Cache.Entry entry = new Cache.Entry(); 107 | entry.data = response.data; 108 | entry.etag = serverEtag; 109 | entry.softTtl = softExpire; 110 | entry.ttl = expire; 111 | entry.serverDate = serverDate; 112 | entry.responseHeaders = headers; 113 | 114 | return entry; 115 | } 116 | 117 | /** 118 | * Parse date in RFC1123 format, and return its value as epoch 119 | */ 120 | public static long parseDateAsEpoch(String dateStr) { 121 | try { 122 | // Parse date in RFC1123 format if this header contains one 123 | return DateUtils.parseDate(dateStr).getTime(); 124 | } catch (DateParseException e) { 125 | // Date in invalid format, fallback to 0 126 | return 0; 127 | } 128 | } 129 | 130 | /** 131 | * Returns the charset specified in the Content-Type of this header, 132 | * or the HTTP default (ISO-8859-1) if none can be found. 133 | */ 134 | public static String parseCharset(Map headers) { 135 | String contentType = headers.get(HTTP.CONTENT_TYPE); 136 | if (contentType != null) { 137 | String[] params = contentType.split(";"); 138 | for (int i = 1; i < params.length; i++) { 139 | String[] pair = params[i].trim().split("="); 140 | if (pair.length == 2) { 141 | if (pair[0].equals("charset")) { 142 | return pair[1]; 143 | } 144 | } 145 | } 146 | } 147 | 148 | return HTTP.DEFAULT_CONTENT_CHARSET; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/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.jude.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 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/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.jude.volley; 18 | 19 | import android.annotation.TargetApi; 20 | import android.net.TrafficStats; 21 | import android.os.Build; 22 | import android.os.Process; 23 | 24 | import java.util.concurrent.BlockingQueue; 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 | /** 47 | * Creates a new network dispatcher thread. You must call {@link #start()} 48 | * in order to begin processing. 49 | * 50 | * @param queue Queue of incoming requests for triage 51 | * @param network Network interface to use for performing requests 52 | * @param cache Cache interface to use for writing responses to cache 53 | * @param delivery Delivery interface to use for posting responses 54 | */ 55 | public NetworkDispatcher(BlockingQueue> queue, 56 | Network network, Cache cache, 57 | ResponseDelivery delivery) { 58 | mQueue = queue; 59 | mNetwork = network; 60 | mCache = cache; 61 | mDelivery = delivery; 62 | } 63 | 64 | /** 65 | * Forces this dispatcher to quit immediately. If any requests are still in 66 | * the queue, they are not guaranteed to be processed. 67 | */ 68 | public void quit() { 69 | mQuit = true; 70 | interrupt(); 71 | } 72 | 73 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 74 | private void addTrafficStatsTag(Request request) { 75 | // Tag the request (if API >= 14) 76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 77 | TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); 78 | } 79 | } 80 | 81 | @Override 82 | public void run() { 83 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 84 | Request request; 85 | while (true) { 86 | try { 87 | // Take a request from the queue. 88 | request = mQueue.take(); 89 | } catch (InterruptedException e) { 90 | // We may have been interrupted because it was time to quit. 91 | if (mQuit) { 92 | return; 93 | } 94 | continue; 95 | } 96 | 97 | try { 98 | request.addMarker("network-queue-take"); 99 | 100 | // If the request was cancelled already, do not perform the 101 | // network request. 102 | if (request.isCanceled()) { 103 | request.finish("network-discard-cancelled"); 104 | continue; 105 | } 106 | 107 | addTrafficStatsTag(request); 108 | 109 | // Perform the network request. 110 | NetworkResponse networkResponse = mNetwork.performRequest(request); 111 | request.addMarker("network-http-complete"); 112 | 113 | // If the server returned 304 AND we delivered a response already, 114 | // we're done -- don't deliver a second identical response. 115 | if (networkResponse.notModified && request.hasHadResponseDelivered()) { 116 | request.finish("not-modified"); 117 | continue; 118 | } 119 | 120 | // Parse the response here on the worker thread. 121 | Response response = request.parseNetworkResponse(networkResponse); 122 | request.addMarker("network-parse-complete"); 123 | 124 | // Write to cache if applicable. 125 | // TODO: Only update cache metadata instead of entire record for 304s. 126 | if (request.shouldCache() && response.cacheEntry != null) { 127 | mCache.put(request.getCacheKey(), response.cacheEntry); 128 | request.addMarker("network-cache-written"); 129 | }else{ 130 | if (request.shouldCache()){ 131 | request.addMarker("network-shouldn't cache"); 132 | } 133 | if (response.cacheEntry != null){ 134 | request.addMarker("network-hasn't cacheEntry"); 135 | } 136 | } 137 | 138 | // Post the response back. 139 | request.markDelivered(); 140 | mDelivery.postResponse(request, response); 141 | } catch (VolleyError volleyError) { 142 | parseAndDeliverNetworkError(request, volleyError); 143 | } catch (Exception e) { 144 | VolleyLog.e(e, "Unhandled exception %s", e.toString()); 145 | mDelivery.postError(request, new VolleyError(e)); 146 | } 147 | } 148 | } 149 | 150 | private void parseAndDeliverNetworkError(Request request, VolleyError error) { 151 | error = request.parseNetworkError(error); 152 | mDelivery.postError(request, error); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/CacheDispatcher.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.jude.volley; 18 | 19 | import android.os.Process; 20 | 21 | import java.util.concurrent.BlockingQueue; 22 | 23 | /** 24 | * Provides a thread for performing cache triage on a queue of requests. 25 | * 26 | * Requests added to the specified cache queue are resolved from cache. 27 | * Any deliverable response is posted back to the caller via a 28 | * {@link ResponseDelivery}. Cache misses and responses that require 29 | * refresh are enqueued on the specified network queue for processing 30 | * by a {@link NetworkDispatcher}. 31 | */ 32 | public class CacheDispatcher extends Thread { 33 | 34 | private static final boolean DEBUG = VolleyLog.DEBUG; 35 | 36 | /** The queue of requests coming in for triage. */ 37 | private final BlockingQueue> mCacheQueue; 38 | 39 | /** The queue of requests going out to the network. */ 40 | private final BlockingQueue> mNetworkQueue; 41 | 42 | /** The cache to read from. */ 43 | private final Cache mCache; 44 | 45 | /** For posting responses. */ 46 | private final ResponseDelivery mDelivery; 47 | 48 | /** Used for telling us to die. */ 49 | private volatile boolean mQuit = false; 50 | 51 | /** 52 | * Creates a new cache triage dispatcher thread. You must call {@link #start()} 53 | * in order to begin processing. 54 | * 55 | * @param cacheQueue Queue of incoming requests for triage 56 | * @param networkQueue Queue to post requests that require network to 57 | * @param cache Cache interface to use for resolution 58 | * @param delivery Delivery interface to use for posting responses 59 | */ 60 | public CacheDispatcher( 61 | BlockingQueue> cacheQueue, BlockingQueue> networkQueue, 62 | Cache cache, ResponseDelivery delivery) { 63 | mCacheQueue = cacheQueue; 64 | mNetworkQueue = networkQueue; 65 | mCache = cache; 66 | mDelivery = delivery; 67 | } 68 | 69 | /** 70 | * Forces this dispatcher to quit immediately. If any requests are still in 71 | * the queue, they are not guaranteed to be processed. 72 | */ 73 | public void quit() { 74 | mQuit = true; 75 | interrupt(); 76 | } 77 | 78 | @Override 79 | public void run() { 80 | if (DEBUG) VolleyLog.v("start new dispatcher"); 81 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 82 | 83 | // Make a blocking call to initialize the cache. 84 | mCache.initialize(); 85 | 86 | while (true) { 87 | try { 88 | // Get a request from the cache triage queue, blocking until 89 | // at least one is available. 90 | final Request request = mCacheQueue.take(); 91 | request.addMarker("cache-queue-take"); 92 | 93 | // If the request has been canceled, don't bother dispatching it. 94 | if (request.isCanceled()) { 95 | request.finish("cache-discard-canceled"); 96 | continue; 97 | } 98 | 99 | // Attempt to retrieve this item from cache. 100 | Cache.Entry entry = mCache.get(request.getCacheKey()); 101 | if (entry == null) { 102 | request.addMarker("cache-miss"); 103 | // Cache miss; send off to the network dispatcher. 104 | mNetworkQueue.put(request); 105 | continue; 106 | } 107 | 108 | // If it is completely expired, just send it to the network. 109 | if (entry.isExpired()) { 110 | request.addMarker("cache-hit-expired"); 111 | request.setCacheEntry(entry); 112 | mNetworkQueue.put(request); 113 | continue; 114 | } 115 | 116 | // We have a cache hit; parse its data for delivery back to the request. 117 | request.addMarker("cache-hit"); 118 | Response response = request.parseNetworkResponse( 119 | new NetworkResponse(entry.data, entry.responseHeaders)); 120 | request.addMarker("cache-hit-parsed"); 121 | 122 | if (!entry.refreshNeeded()) { 123 | // Completely unexpired cache hit. Just deliver the response. 124 | mDelivery.postResponse(request, response); 125 | } else { 126 | // Soft-expired cache hit. We can deliver the cached response, 127 | // but we need to also send the request to the network for 128 | // refreshing. 129 | request.addMarker("cache-hit-refresh-needed"); 130 | request.setCacheEntry(entry); 131 | 132 | // Mark the response as intermediate. 133 | response.intermediate = true; 134 | 135 | // Post the intermediate response back to the user and have 136 | // the delivery then forward the request along to the network. 137 | mDelivery.postResponse(request, response, new Runnable() { 138 | @Override 139 | public void run() { 140 | try { 141 | mNetworkQueue.put(request); 142 | } catch (InterruptedException e) { 143 | // Not much we can do about this. 144 | } 145 | } 146 | }); 147 | } 148 | 149 | } catch (InterruptedException e) { 150 | // We may have been interrupted because it was time to quit. 151 | if (mQuit) { 152 | return; 153 | } 154 | continue; 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/VolleyLog.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.jude.volley; 18 | 19 | import android.os.SystemClock; 20 | import android.util.Log; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Locale; 25 | 26 | /** Logging helper class. */ 27 | public class VolleyLog { 28 | public static String TAG = "Volley"; 29 | 30 | public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); 31 | 32 | /** 33 | * Customize the log tag for your application, so that other apps 34 | * using Volley don't mix their logs with yours. 35 | * Enable the log property for your tag before starting your app: 36 | * {@code adb shell setprop log.tag.<tag>} 37 | */ 38 | public static void setTag(String tag) { 39 | d("Changing log tag to %s", tag); 40 | TAG = tag; 41 | 42 | // Reinitialize the DEBUG "constant" 43 | DEBUG = Log.isLoggable(TAG, Log.VERBOSE); 44 | } 45 | 46 | public static void v(String format, Object... args) { 47 | if (DEBUG) { 48 | Log.v(TAG, buildMessage(format, args)); 49 | } 50 | } 51 | 52 | public static void d(String format, Object... args) { 53 | Log.d(TAG, buildMessage(format, args)); 54 | } 55 | 56 | public static void e(String format, Object... args) { 57 | Log.e(TAG, buildMessage(format, args)); 58 | } 59 | 60 | public static void e(Throwable tr, String format, Object... args) { 61 | Log.e(TAG, buildMessage(format, args), tr); 62 | } 63 | 64 | public static void wtf(String format, Object... args) { 65 | Log.wtf(TAG, buildMessage(format, args)); 66 | } 67 | 68 | public static void wtf(Throwable tr, String format, Object... args) { 69 | Log.wtf(TAG, buildMessage(format, args), tr); 70 | } 71 | 72 | /** 73 | * Formats the caller's provided message and prepends useful info like 74 | * calling thread ID and method name. 75 | */ 76 | private static String buildMessage(String format, Object... args) { 77 | String msg = (args == null) ? format : String.format(Locale.US, format, args); 78 | StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace(); 79 | 80 | String caller = ""; 81 | // Walk up the stack looking for the first caller outside of VolleyLog. 82 | // It will be at least two frames up, so start there. 83 | for (int i = 2; i < trace.length; i++) { 84 | Class clazz = trace[i].getClass(); 85 | if (!clazz.equals(VolleyLog.class)) { 86 | String callingClass = trace[i].getClassName(); 87 | callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1); 88 | callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1); 89 | 90 | caller = callingClass + "." + trace[i].getMethodName(); 91 | break; 92 | } 93 | } 94 | return String.format(Locale.US, "[%d] %s: %s", 95 | Thread.currentThread().getId(), caller, msg); 96 | } 97 | 98 | /** 99 | * A simple event log with records containing a name, thread ID, and timestamp. 100 | */ 101 | static class MarkerLog { 102 | public static final boolean ENABLED = VolleyLog.DEBUG; 103 | 104 | /** Minimum duration from first marker to last in an marker log to warrant logging. */ 105 | private static final long MIN_DURATION_FOR_LOGGING_MS = 0; 106 | 107 | private static class Marker { 108 | public final String name; 109 | public final long thread; 110 | public final long time; 111 | 112 | public Marker(String name, long thread, long time) { 113 | this.name = name; 114 | this.thread = thread; 115 | this.time = time; 116 | } 117 | } 118 | 119 | private final List mMarkers = new ArrayList(); 120 | private boolean mFinished = false; 121 | 122 | /** Adds a marker to this log with the specified name. */ 123 | public synchronized void add(String name, long threadId) { 124 | if (mFinished) { 125 | throw new IllegalStateException("Marker added to finished log"); 126 | } 127 | 128 | mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime())); 129 | } 130 | 131 | /** 132 | * Closes the log, dumping it to logcat if the time difference between 133 | * the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}. 134 | * @param header Header string to print above the marker log. 135 | */ 136 | public synchronized void finish(String header) { 137 | mFinished = true; 138 | 139 | long duration = getTotalDuration(); 140 | if (duration <= MIN_DURATION_FOR_LOGGING_MS) { 141 | return; 142 | } 143 | 144 | long prevTime = mMarkers.get(0).time; 145 | d("(%-4d ms) %s", duration, header); 146 | for (Marker marker : mMarkers) { 147 | long thisTime = marker.time; 148 | d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name); 149 | prevTime = thisTime; 150 | } 151 | } 152 | 153 | @Override 154 | protected void finalize() throws Throwable { 155 | // Catch requests that have been collected (and hence end-of-lifed) 156 | // but had no debugging output printed for them. 157 | if (!mFinished) { 158 | finish("Request on the loose"); 159 | e("Marker log finalized without finish() - uncaught exit point for request"); 160 | } 161 | } 162 | 163 | /** Returns the time difference between the first and last events in this log. */ 164 | private long getTotalDuration() { 165 | if (mMarkers.size() == 0) { 166 | return 0; 167 | } 168 | 169 | long first = mMarkers.get(0).time; 170 | long last = mMarkers.get(mMarkers.size() - 1).time; 171 | return last - first; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/RequestManager.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.widget.ImageView; 6 | 7 | import com.jude.volley.AuthFailureError; 8 | import com.jude.volley.DefaultRetryPolicy; 9 | import com.jude.volley.Request; 10 | import com.jude.volley.Request.Method; 11 | import com.jude.volley.RequestQueue; 12 | import com.jude.volley.RetryPolicy; 13 | import com.jude.volley.VolleyError; 14 | import com.jude.volley.toolbox.ImageLoader; 15 | import com.jude.volley.toolbox.Volley; 16 | 17 | import java.io.UnsupportedEncodingException; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | 22 | public class RequestManager { 23 | 24 | private static final String CHARSET_UTF_8 = "UTF-8"; 25 | 26 | private int TIMEOUT_COUNT = 10 * 1000; 27 | 28 | private int RETRY_TIMES = 1; 29 | 30 | private boolean SHOULD_CACHE = false; 31 | 32 | private HashMap HEADER; 33 | 34 | private volatile static RequestManager instance = null; 35 | 36 | private RequestQueue mRequestQueue = null; 37 | 38 | private NetworkImageCache mImageCache = null; 39 | 40 | private ImageLoader mImageLoader = null; 41 | 42 | private int times = 0; 43 | 44 | private boolean Debug = false; 45 | private String DebugTag; 46 | 47 | 48 | private RequestManager() { 49 | 50 | } 51 | 52 | public void setDebugMode(boolean isDebug,String DebugTag){ 53 | this.Debug = isDebug; 54 | this.DebugTag = DebugTag; 55 | } 56 | 57 | public void init(Context context) { 58 | this.mRequestQueue = Volley.newRequestQueue(context); 59 | mImageCache = new NetworkImageCache(context); 60 | mImageLoader = new ImageLoader(RequestManager.getInstance() 61 | .getRequestQueue(), mImageCache); 62 | } 63 | 64 | public static RequestManager getInstance() { 65 | if (null == instance) { 66 | synchronized (RequestManager.class) { 67 | if (null == instance) { 68 | instance = new RequestManager(); 69 | } 70 | } 71 | } 72 | return instance; 73 | } 74 | 75 | public RequestQueue getRequestQueue() { 76 | return this.mRequestQueue; 77 | } 78 | 79 | public void setTimeOut(int time){ 80 | TIMEOUT_COUNT = time; 81 | } 82 | 83 | public void setRetryTimes(int times){ 84 | RETRY_TIMES = times; 85 | } 86 | 87 | public void setCacheEnable(boolean isCache){ 88 | SHOULD_CACHE = isCache; 89 | } 90 | 91 | public void setHeader(HashMap header){ 92 | HEADER = header; 93 | } 94 | 95 | public LoadController get(String url, RequestListener requestListener) { 96 | return this.request(Method.GET, url, null , null, requestListener, SHOULD_CACHE, TIMEOUT_COUNT, RETRY_TIMES); 97 | } 98 | 99 | public LoadController get(String url, HashMap header,RequestListener requestListener, boolean shouldCache) { 100 | return this.request(Method.GET, url, null, header, requestListener, shouldCache, TIMEOUT_COUNT, RETRY_TIMES); 101 | } 102 | 103 | 104 | public LoadController post(final String url, Object data, final RequestListener requestListener) { 105 | return this.post(url, data, HEADER,requestListener, SHOULD_CACHE, TIMEOUT_COUNT, RETRY_TIMES); 106 | } 107 | 108 | public LoadController post(final String url, Object data, boolean checkCache,final RequestListener requestListener) { 109 | return this.post(url, data, HEADER,requestListener, checkCache, TIMEOUT_COUNT, RETRY_TIMES); 110 | } 111 | 112 | public void invalidate(String url, Object data){ 113 | mRequestQueue.getCache().invalidate(url+data.toString(),true); 114 | } 115 | 116 | 117 | public LoadController post(final String url, Object data,HashMap header, final RequestListener requestListener, boolean shouldCache, 118 | int timeoutCount, int retryTimes) { 119 | return request(Method.POST, url, data,header, requestListener, shouldCache, timeoutCount, retryTimes); 120 | } 121 | 122 | 123 | public ImageLoader.ImageContainer img(final String url,final ImageView imageView){ 124 | return mImageLoader.get(url, new ImageLoader.ImageListener() { 125 | @Override 126 | public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) { 127 | if (response.getBitmap()!=null){ 128 | imageView.setImageBitmap(response.getBitmap()); 129 | } 130 | } 131 | 132 | @Override 133 | public void onErrorResponse(VolleyError error) { 134 | } 135 | }); 136 | } 137 | 138 | public ImageLoader.ImageContainer img(final String url,final ImageView imageView, final int resError){ 139 | return mImageLoader.get(url, new ImageLoader.ImageListener() { 140 | @Override 141 | public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) { 142 | if (response.getBitmap()!=null){ 143 | imageView.setImageBitmap(response.getBitmap()); 144 | } 145 | } 146 | 147 | @Override 148 | public void onErrorResponse(VolleyError error) { 149 | imageView.setImageResource(resError); 150 | } 151 | }); 152 | } 153 | 154 | public ImageLoader.ImageContainer img(final String url,final ImageLoader.ImageListener imageListener){ 155 | return mImageLoader.get(url, imageListener); 156 | } 157 | 158 | public ImageLoader.ImageContainer img(final String url,final ImageLoader.ImageListener imageListener,int maxWidth,int maxHeight){ 159 | return mImageLoader.get(url, imageListener, maxWidth, maxHeight); 160 | } 161 | 162 | 163 | public LoadController request(int method, final String url, final Object data, final Map headers, 164 | final RequestListener requestListener, boolean shouldCache, int timeoutCount, int retryTimes) { 165 | final int curIndex = this.times++; 166 | return this.sendRequest(method, url, data, headers, new LoadListener() { 167 | @Override 168 | public void onStart() { 169 | if(requestListener!=null) 170 | requestListener.onRequest(); 171 | if(Debug)Log.i(DebugTag, curIndex+"Times-Params:"+url+(data==null?"":data.toString())); 172 | } 173 | 174 | @Override 175 | public void onSuccess(byte[] data, String url) { 176 | 177 | String parsed = null; 178 | try { 179 | parsed = new String(data, CHARSET_UTF_8); 180 | } catch (UnsupportedEncodingException e) { 181 | e.printStackTrace(); 182 | } 183 | if(Debug)Log.i(DebugTag, curIndex+"Times-Response:"+parsed); 184 | if(requestListener!=null) 185 | requestListener.onSuccess(parsed); 186 | } 187 | 188 | @Override 189 | public void onError(String errorMsg, String url) { 190 | if(Debug)Log.i(DebugTag, curIndex+"Times-Error:"+errorMsg); 191 | if(requestListener!=null) 192 | requestListener.onError(errorMsg); 193 | } 194 | }, shouldCache, timeoutCount, retryTimes); 195 | } 196 | 197 | 198 | public LoadController sendRequest(int method, final String url, Object data,final Map headers, 199 | final LoadListener requestListener, boolean shouldCache, int timeoutCount, int retryTimes) { 200 | if (requestListener == null) 201 | throw new NullPointerException(); 202 | 203 | final ByteArrayLoadController loadControler = new ByteArrayLoadController(requestListener); 204 | 205 | Request request = null; 206 | request = new ByteArrayRequest(method, url, data, headers,loadControler, loadControler); 207 | request.setShouldCache(shouldCache); 208 | 209 | 210 | if (headers != null && !headers.isEmpty()) {// add headers if not empty 211 | try { 212 | request.getHeaders().putAll(headers); 213 | } catch (AuthFailureError e) { 214 | e.printStackTrace(); 215 | } 216 | } 217 | 218 | RetryPolicy retryPolicy = new DefaultRetryPolicy(timeoutCount, retryTimes, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT); 219 | request.setRetryPolicy(retryPolicy); 220 | 221 | loadControler.bindRequest(request); 222 | 223 | if (this.mRequestQueue == null) 224 | throw new NullPointerException(); 225 | requestListener.onStart(); 226 | this.mRequestQueue.add(request); 227 | 228 | return loadControler; 229 | } 230 | 231 | 232 | 233 | 234 | } 235 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/HttpClientStack.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.AuthFailureError; 20 | import com.jude.volley.Request; 21 | import com.jude.volley.Request.Method; 22 | 23 | import org.apache.http.HttpEntity; 24 | import org.apache.http.HttpResponse; 25 | import org.apache.http.NameValuePair; 26 | import org.apache.http.client.HttpClient; 27 | import org.apache.http.client.methods.HttpDelete; 28 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 29 | import org.apache.http.client.methods.HttpGet; 30 | import org.apache.http.client.methods.HttpHead; 31 | import org.apache.http.client.methods.HttpOptions; 32 | import org.apache.http.client.methods.HttpPost; 33 | import org.apache.http.client.methods.HttpPut; 34 | import org.apache.http.client.methods.HttpTrace; 35 | import org.apache.http.client.methods.HttpUriRequest; 36 | import org.apache.http.entity.ByteArrayEntity; 37 | import org.apache.http.message.BasicNameValuePair; 38 | import org.apache.http.params.HttpConnectionParams; 39 | import org.apache.http.params.HttpParams; 40 | 41 | import java.io.IOException; 42 | import java.net.URI; 43 | import java.util.ArrayList; 44 | import java.util.List; 45 | import java.util.Map; 46 | 47 | /** 48 | * An HttpStack that performs request over an {@link HttpClient}. 49 | */ 50 | public class HttpClientStack implements HttpStack { 51 | protected final HttpClient mClient; 52 | 53 | private final static String HEADER_CONTENT_TYPE = "Content-Type"; 54 | 55 | public HttpClientStack(HttpClient client) { 56 | mClient = client; 57 | } 58 | 59 | private static void addHeaders(HttpUriRequest httpRequest, Map headers) { 60 | for (String key : headers.keySet()) { 61 | httpRequest.setHeader(key, headers.get(key)); 62 | } 63 | } 64 | 65 | @SuppressWarnings("unused") 66 | private static List getPostParameterPairs(Map postParams) { 67 | List result = new ArrayList(postParams.size()); 68 | for (String key : postParams.keySet()) { 69 | result.add(new BasicNameValuePair(key, postParams.get(key))); 70 | } 71 | return result; 72 | } 73 | 74 | @Override 75 | public HttpResponse performRequest(Request request, Map additionalHeaders) 76 | throws IOException, AuthFailureError { 77 | HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); 78 | addHeaders(httpRequest, additionalHeaders); 79 | addHeaders(httpRequest, request.getHeaders()); 80 | onPrepareRequest(httpRequest); 81 | HttpParams httpParams = httpRequest.getParams(); 82 | int timeoutMs = request.getTimeoutMs(); 83 | // TODO: Reevaluate this connection timeout based on more wide-scale 84 | // data collection and possibly different for wifi vs. 3G. 85 | HttpConnectionParams.setConnectionTimeout(httpParams, 5000); 86 | HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); 87 | return mClient.execute(httpRequest); 88 | } 89 | 90 | /** 91 | * Creates the appropriate subclass of HttpUriRequest for passed in request. 92 | */ 93 | @SuppressWarnings("deprecation") 94 | /* protected */ static HttpUriRequest createHttpRequest(Request request, 95 | Map additionalHeaders) throws AuthFailureError { 96 | switch (request.getMethod()) { 97 | case Method.DEPRECATED_GET_OR_POST: { 98 | // This is the deprecated way that needs to be handled for backwards compatibility. 99 | // If the request's post body is null, then the assumption is that the request is 100 | // GET. Otherwise, it is assumed that the request is a POST. 101 | byte[] postBody = request.getPostBody(); 102 | if (postBody != null) { 103 | HttpPost postRequest = new HttpPost(request.getUrl()); 104 | postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); 105 | HttpEntity entity; 106 | entity = new ByteArrayEntity(postBody); 107 | postRequest.setEntity(entity); 108 | return postRequest; 109 | } else { 110 | return new HttpGet(request.getUrl()); 111 | } 112 | } 113 | case Method.GET: 114 | return new HttpGet(request.getUrl()); 115 | case Method.DELETE: 116 | return new HttpDelete(request.getUrl()); 117 | case Method.POST: { 118 | HttpPost postRequest = new HttpPost(request.getUrl()); 119 | postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); 120 | setEntityIfNonEmptyBody(postRequest, request); 121 | return postRequest; 122 | } 123 | case Method.PUT: { 124 | HttpPut putRequest = new HttpPut(request.getUrl()); 125 | putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); 126 | setEntityIfNonEmptyBody(putRequest, request); 127 | return putRequest; 128 | } 129 | case Method.HEAD: 130 | return new HttpHead(request.getUrl()); 131 | case Method.OPTIONS: 132 | return new HttpOptions(request.getUrl()); 133 | case Method.TRACE: 134 | return new HttpTrace(request.getUrl()); 135 | case Method.PATCH: { 136 | HttpPatch patchRequest = new HttpPatch(request.getUrl()); 137 | patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); 138 | setEntityIfNonEmptyBody(patchRequest, request); 139 | return patchRequest; 140 | } 141 | default: 142 | throw new IllegalStateException("Unknown request method."); 143 | } 144 | } 145 | 146 | private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest, 147 | Request request) throws AuthFailureError { 148 | byte[] body = request.getBody(); 149 | if (body != null) { 150 | HttpEntity entity = new ByteArrayEntity(body); 151 | httpRequest.setEntity(entity); 152 | } 153 | } 154 | 155 | /** 156 | * Called before the request is executed using the underlying HttpClient. 157 | * 158 | *

Overwrite in subclasses to augment the request.

159 | */ 160 | protected void onPrepareRequest(HttpUriRequest request) throws IOException { 161 | // Nothing. 162 | } 163 | 164 | /** 165 | * The HttpPatch class does not exist in the Android framework, so this has been defined here. 166 | */ 167 | public static final class HttpPatch extends HttpEntityEnclosingRequestBase { 168 | 169 | public final static String METHOD_NAME = "PATCH"; 170 | 171 | public HttpPatch() { 172 | super(); 173 | } 174 | 175 | public HttpPatch(final URI uri) { 176 | super(); 177 | setURI(uri); 178 | } 179 | 180 | /** 181 | * @throws IllegalArgumentException if the uri is invalid. 182 | */ 183 | public HttpPatch(final String uri) { 184 | super(); 185 | setURI(URI.create(uri)); 186 | } 187 | 188 | @Override 189 | public String getMethod() { 190 | return METHOD_NAME; 191 | } 192 | 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/NetworkImageView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 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 | package com.jude.volley.toolbox; 17 | 18 | import android.content.Context; 19 | import android.graphics.drawable.Drawable; 20 | import android.text.TextUtils; 21 | import android.util.AttributeSet; 22 | import android.view.ViewGroup.LayoutParams; 23 | import android.widget.ImageView; 24 | 25 | import com.jude.volley.VolleyError; 26 | 27 | /** 28 | * Handles fetching an image from a URL as well as the life-cycle of the 29 | * associated request. 30 | */ 31 | public class NetworkImageView extends ImageView { 32 | /** The URL of the network image to load */ 33 | private String mUrl; 34 | 35 | /** 36 | * Resource ID of the image to be used as a placeholder until the network 37 | * image is loaded. 38 | */ 39 | private int mDefaultImageId; 40 | 41 | /** 42 | * Resource ID of the image to be used if the network response fails. 43 | */ 44 | private int mErrorImageId; 45 | 46 | /** 47 | * Resource ID of the image to be used if the network response fails. 48 | */ 49 | private Drawable mErrorImageDrawable; 50 | 51 | /** Local copy of the ImageLoader. */ 52 | private ImageLoader mImageLoader; 53 | 54 | /** Current ImageContainer. (either in-flight or finished) */ 55 | private ImageLoader.ImageContainer mImageContainer; 56 | 57 | public NetworkImageView(Context context) { 58 | this(context, null); 59 | } 60 | 61 | public NetworkImageView(Context context, AttributeSet attrs) { 62 | this(context, attrs, 0); 63 | } 64 | 65 | public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { 66 | super(context, attrs, defStyle); 67 | } 68 | 69 | /** 70 | * Sets URL of the image that should be loaded into this view. Note that 71 | * calling this will immediately either set the cached image (if available) 72 | * or the default image specified by 73 | * {@link NetworkImageView#setDefaultImageResId(int)} on the view. 74 | * 75 | * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} 76 | * and {@link NetworkImageView#setErrorImageResId(int)} should be called 77 | * prior to calling this function. 78 | * 79 | * @param url 80 | * The URL that should be loaded into this ImageView. 81 | * @param imageLoader 82 | * ImageLoader that will be used to make the request. 83 | */ 84 | public void setImageUrl(String url, ImageLoader imageLoader) { 85 | mUrl = url; 86 | mImageLoader = imageLoader; 87 | // The URL has potentially changed. See if we need to load it. 88 | loadImageIfNecessary(false); 89 | } 90 | 91 | /** 92 | * Sets the default image resource ID to be used for this view until the 93 | * attempt to load it completes. 94 | */ 95 | public void setDefaultImageResId(int defaultImage) { 96 | mDefaultImageId = defaultImage; 97 | } 98 | 99 | /** 100 | * Sets the default image resource Drawable to be used for this view until 101 | * the attempt to load it completes. 102 | */ 103 | public void setDefaultImageDrawable(Drawable imageDrawable) { 104 | mErrorImageDrawable = imageDrawable; 105 | } 106 | 107 | /** 108 | * Sets the error image resource ID to be used for this view in the event 109 | * that the image requested fails to load. 110 | */ 111 | public void setErrorImageResId(int errorImage) { 112 | mErrorImageId = errorImage; 113 | } 114 | 115 | /** 116 | * Loads the image for the view if it isn't already loaded. 117 | * 118 | * @param isInLayoutPass 119 | * True if this was invoked from a layout pass, false otherwise. 120 | */ 121 | void loadImageIfNecessary(final boolean isInLayoutPass) { 122 | int width = getWidth(); 123 | int height = getHeight(); 124 | 125 | boolean wrapWidth = false, wrapHeight = false; 126 | if (getLayoutParams() != null) { 127 | wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; 128 | wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; 129 | } 130 | 131 | // if the view's bounds aren't known yet, and this is not a 132 | // wrap-content/wrap-content 133 | // view, hold off on loading the image. 134 | boolean isFullyWrapContent = wrapWidth && wrapHeight; 135 | if (width == 0 && height == 0 && !isFullyWrapContent) { 136 | return; 137 | } 138 | 139 | // if the URL to be loaded in this view is empty, cancel any old 140 | // requests and clear the 141 | // currently loaded image. 142 | if (TextUtils.isEmpty(mUrl)) { 143 | if (mImageContainer != null) { 144 | mImageContainer.cancelRequest(); 145 | mImageContainer = null; 146 | } 147 | setDefaultImageOrNull(); 148 | return; 149 | } 150 | 151 | // if there was an old request in this view, check if it needs to be 152 | // canceled. 153 | if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { 154 | if (mImageContainer.getRequestUrl().equals(mUrl)) { 155 | // if the request is from the same URL, return. 156 | return; 157 | } else { 158 | // if there is a pre-existing request, cancel it if it's 159 | // fetching a different URL. 160 | mImageContainer.cancelRequest(); 161 | setDefaultImageOrNull(); 162 | } 163 | } 164 | 165 | // Calculate the max image width / height to use while ignoring 166 | // WRAP_CONTENT dimens. 167 | int maxWidth = wrapWidth ? 0 : width; 168 | int maxHeight = wrapHeight ? 0 : height; 169 | 170 | // The pre-existing content of this view didn't match the current URL. 171 | // Load the new image 172 | // from the network. 173 | ImageLoader.ImageContainer newContainer = mImageLoader.get(mUrl, 174 | new ImageLoader.ImageListener() { 175 | @Override 176 | public void onErrorResponse(VolleyError error) { 177 | if (mErrorImageId != 0) { 178 | setImageResource(mErrorImageId); 179 | } else if (mErrorImageDrawable != null) { 180 | setImageDrawable(mErrorImageDrawable); 181 | } 182 | } 183 | 184 | @Override 185 | public void onResponse(final ImageLoader.ImageContainer response, 186 | boolean isImmediate) { 187 | // If this was an immediate response that was delivered 188 | // inside of a layout 189 | // pass do not set the image immediately as it will 190 | // trigger a requestLayout 191 | // inside of a layout. Instead, defer setting the image 192 | // by posting back to 193 | // the main thread. 194 | if (isImmediate && isInLayoutPass) { 195 | post(new Runnable() { 196 | @Override 197 | public void run() { 198 | onResponse(response, false); 199 | } 200 | }); 201 | return; 202 | } 203 | 204 | if (response.getBitmap() != null) { 205 | setImageBitmap(response.getBitmap()); 206 | } else if (mDefaultImageId != 0) { 207 | setImageResource(mDefaultImageId); 208 | } else if (mErrorImageDrawable != null) { 209 | setImageDrawable(mErrorImageDrawable); 210 | } 211 | } 212 | }, maxWidth, maxHeight); 213 | 214 | // update the ImageContainer to be the new bitmap container. 215 | mImageContainer = newContainer; 216 | } 217 | 218 | private void setDefaultImageOrNull() { 219 | if (mDefaultImageId != 0) { 220 | setImageResource(mDefaultImageId); 221 | } else if (mErrorImageDrawable != null) { 222 | setImageDrawable(mErrorImageDrawable); 223 | } else { 224 | setImageBitmap(null); 225 | } 226 | } 227 | 228 | @Override 229 | protected void onLayout(boolean changed, int left, int top, int right, 230 | int bottom) { 231 | super.onLayout(changed, left, top, right, bottom); 232 | loadImageIfNecessary(true); 233 | } 234 | 235 | @Override 236 | protected void onDetachedFromWindow() { 237 | if (mImageContainer != null) { 238 | // If the view was bound to an image request, cancel it and clear 239 | // out the image from the view. 240 | mImageContainer.cancelRequest(); 241 | setImageBitmap(null); 242 | // also clear out the container so we can reload the image if 243 | // necessary. 244 | mImageContainer = null; 245 | } 246 | super.onDetachedFromWindow(); 247 | } 248 | 249 | @Override 250 | protected void drawableStateChanged() { 251 | super.drawableStateChanged(); 252 | invalidate(); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/http/RequestMap.java: -------------------------------------------------------------------------------- 1 | package com.jude.http; 2 | 3 | import org.apache.http.Header; 4 | import org.apache.http.HttpEntity; 5 | import org.apache.http.client.entity.UrlEncodedFormEntity; 6 | import org.apache.http.message.BasicHeader; 7 | import org.apache.http.message.BasicNameValuePair; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.FileNotFoundException; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.OutputStream; 17 | import java.io.UnsupportedEncodingException; 18 | import java.util.AbstractMap; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Random; 23 | 24 | 25 | public class RequestMap { 26 | private static String ENCODING = "UTF-8"; 27 | 28 | protected ArrayList> urlParams; 29 | 30 | protected ArrayList> fileParams; 31 | 32 | private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 33 | .toCharArray(); 34 | 35 | public RequestMap() { 36 | init(); 37 | } 38 | 39 | public RequestMap(String key, String value) { 40 | init(); 41 | put(key, value); 42 | } 43 | 44 | public RequestMap(String key, File value) { 45 | init(); 46 | put(key, value); 47 | } 48 | 49 | private void init() { 50 | urlParams = new ArrayList(); 51 | fileParams = new ArrayList(); 52 | } 53 | 54 | 55 | public void put(String key, String value) { 56 | if (key != null && value != null) { 57 | urlParams.add(new AbstractMap.SimpleEntry<>(key, value)); 58 | } 59 | } 60 | 61 | 62 | public void put(String key, File file) { 63 | try { 64 | put(key, new FileInputStream(file), file.getName()); 65 | } catch (FileNotFoundException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | 71 | public void put(String key, InputStream stream, String fileName) { 72 | put(key, stream, fileName, null); 73 | } 74 | 75 | 76 | public void put(String key, InputStream stream, String fileName, String contentType) { 77 | if (key != null && stream != null) { 78 | fileParams.add(new AbstractMap.SimpleEntry(key, new FileWrapper(stream, fileName, contentType))); 79 | } 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | String params = "?"; 85 | for (Map.Entry entry : urlParams) { 86 | if(!params.equals("?")){ 87 | params+="&"; 88 | } 89 | params+=entry.getKey()+"="+entry.getValue(); 90 | } 91 | for (Map.Entry entry : fileParams) { 92 | if(!params.equals("?")){ 93 | params+="&"; 94 | } 95 | params+=entry.getKey()+"=File:{"+entry.getValue().fileName+"}"; 96 | } 97 | return params; 98 | } 99 | 100 | public HttpEntity getEntity() { 101 | HttpEntity entity = null; 102 | if (!fileParams.isEmpty()) { 103 | MultipartEntity multipartEntity = new MultipartEntity(); 104 | for (Map.Entry entry : urlParams) {// Add string params 105 | multipartEntity.addPart(entry.getKey(), entry.getValue()); 106 | } 107 | int currentIndex = 0; 108 | int lastIndex = fileParams.size() - 1; 109 | for (Map.Entry entry : fileParams) {//Add file params 110 | FileWrapper file = entry.getValue(); 111 | if (file.inputStream != null) { 112 | boolean isLast = currentIndex == lastIndex; 113 | if (file.contentType != null) { 114 | multipartEntity.addPart(entry.getKey(), file.getFileName(), file.inputStream, file.contentType, 115 | isLast); 116 | } else { 117 | multipartEntity.addPart(entry.getKey(), file.getFileName(), file.inputStream, isLast); 118 | } 119 | } 120 | currentIndex++; 121 | } 122 | entity = multipartEntity; 123 | } else { 124 | try { 125 | entity = new UrlEncodedFormEntity(getParamsList(), ENCODING); 126 | } catch (UnsupportedEncodingException e) { 127 | e.printStackTrace(); 128 | } 129 | } 130 | return entity; 131 | } 132 | 133 | protected List getParamsList() { 134 | List lparams = new ArrayList(); 135 | for (Map.Entry entry : urlParams) { 136 | lparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); 137 | } 138 | return lparams; 139 | } 140 | 141 | private static class FileWrapper { 142 | public InputStream inputStream; 143 | public String fileName; 144 | public String contentType; 145 | 146 | public FileWrapper(InputStream inputStream, String fileName, String contentType) { 147 | this.inputStream = inputStream; 148 | this.fileName = fileName; 149 | this.contentType = contentType; 150 | } 151 | 152 | public String getFileName() { 153 | if (fileName != null) { 154 | return fileName; 155 | } else { 156 | return "nofilename"; 157 | } 158 | } 159 | } 160 | 161 | class MultipartEntity implements HttpEntity { 162 | private String boundary = null; 163 | 164 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 165 | 166 | boolean isSetLast = false; 167 | 168 | boolean isSetFirst = false; 169 | 170 | public MultipartEntity() { 171 | final StringBuffer buf = new StringBuffer(); 172 | final Random rand = new Random(); 173 | for (int i = 0; i < 30; i++) { 174 | buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); 175 | } 176 | this.boundary = buf.toString(); 177 | } 178 | 179 | public void writeFirstBoundaryIfNeeds() { 180 | if (!isSetFirst) { 181 | try { 182 | out.write(("--" + boundary + "\r\n").getBytes()); 183 | } catch (final IOException e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | isSetFirst = true; 188 | } 189 | 190 | public void writeLastBoundaryIfNeeds() { 191 | if (isSetLast) { 192 | return; 193 | } 194 | try { 195 | out.write(("\r\n--" + boundary + "--\r\n").getBytes()); 196 | } catch (final IOException e) { 197 | e.printStackTrace(); 198 | } 199 | isSetLast = true; 200 | } 201 | 202 | public void addPart(final String key, final String value) { 203 | writeFirstBoundaryIfNeeds(); 204 | try { 205 | out.write(("Content-Disposition: form-data; name=\"" + key + "\"\r\n\r\n").getBytes()); 206 | out.write(value.getBytes()); 207 | out.write(("\r\n--" + boundary + "\r\n").getBytes()); 208 | } catch (final IOException e) { 209 | e.printStackTrace(); 210 | } 211 | } 212 | 213 | public void addPart(final String key, final String fileName, final InputStream fin, final boolean isLast) { 214 | addPart(key, fileName, fin, "application/octet-stream", isLast); 215 | } 216 | 217 | public void addPart(final String key, final String fileName, final InputStream fin, String type, final boolean isLast) { 218 | writeFirstBoundaryIfNeeds(); 219 | try { 220 | type = "Content-Type: " + type + "\r\n"; 221 | out.write(("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + fileName + "\"\r\n") 222 | .getBytes()); 223 | out.write(type.getBytes()); 224 | out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes()); 225 | 226 | final byte[] tmp = new byte[4096]; 227 | int l = 0; 228 | while ((l = fin.read(tmp)) != -1) { 229 | out.write(tmp, 0, l); 230 | } 231 | if (!isLast) 232 | out.write(("\r\n--" + boundary + "\r\n").getBytes()); 233 | else { 234 | writeLastBoundaryIfNeeds(); 235 | } 236 | out.flush(); 237 | } catch (final IOException e) { 238 | e.printStackTrace(); 239 | } finally { 240 | try { 241 | fin.close(); 242 | } catch (final IOException e) { 243 | e.printStackTrace(); 244 | } 245 | } 246 | } 247 | 248 | public void addPart(final String key, final File value, final boolean isLast) { 249 | try { 250 | addPart(key, value.getName(), new FileInputStream(value), isLast); 251 | } catch (final FileNotFoundException e) { 252 | e.printStackTrace(); 253 | } 254 | } 255 | 256 | @Override 257 | public long getContentLength() { 258 | writeLastBoundaryIfNeeds(); 259 | return out.toByteArray().length; 260 | } 261 | 262 | @Override 263 | public Header getContentType() { 264 | return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + boundary); 265 | } 266 | 267 | @Override 268 | public boolean isChunked() { 269 | return false; 270 | } 271 | 272 | @Override 273 | public boolean isRepeatable() { 274 | return false; 275 | } 276 | 277 | @Override 278 | public boolean isStreaming() { 279 | return false; 280 | } 281 | 282 | @Override 283 | public void writeTo(final OutputStream outstream) throws IOException { 284 | outstream.write(out.toByteArray()); 285 | } 286 | 287 | @Override 288 | public Header getContentEncoding() { 289 | return null; 290 | } 291 | 292 | @Override 293 | public void consumeContent() throws IOException, UnsupportedOperationException { 294 | if (isStreaming()) { 295 | throw new UnsupportedOperationException("Streaming entity does not implement #consumeContent()"); 296 | } 297 | } 298 | 299 | @Override 300 | public InputStream getContent() throws IOException, UnsupportedOperationException { 301 | return new ByteArrayInputStream(out.toByteArray()); 302 | } 303 | } 304 | 305 | } -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/ImageRequest.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.DefaultRetryPolicy; 20 | import com.jude.volley.NetworkResponse; 21 | import com.jude.volley.ParseError; 22 | import com.jude.volley.Request; 23 | import com.jude.volley.Response; 24 | import com.jude.volley.VolleyLog; 25 | 26 | import android.graphics.Bitmap; 27 | import android.graphics.Bitmap.Config; 28 | import android.graphics.BitmapFactory; 29 | 30 | /** 31 | * A canned request for getting an image at a given URL and calling 32 | * back with a decoded Bitmap. 33 | */ 34 | public class ImageRequest extends Request { 35 | /** Socket timeout in milliseconds for image requests */ 36 | private static final int IMAGE_TIMEOUT_MS = 1000; 37 | 38 | /** Default number of retries for image requests */ 39 | private static final int IMAGE_MAX_RETRIES = 2; 40 | 41 | /** Default backoff multiplier for image requests */ 42 | private static final float IMAGE_BACKOFF_MULT = 2f; 43 | 44 | private final Response.Listener mListener; 45 | private final Config mDecodeConfig; 46 | private final int mMaxWidth; 47 | private final int mMaxHeight; 48 | 49 | /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */ 50 | private static final Object sDecodeLock = new Object(); 51 | 52 | /** 53 | * Creates a new image request, decoding to a maximum specified width and 54 | * height. If both width and height are zero, the image will be decoded to 55 | * its natural size. If one of the two is nonzero, that dimension will be 56 | * clamped and the other one will be set to preserve the image's aspect 57 | * ratio. If both width and height are nonzero, the image will be decoded to 58 | * be fit in the rectangle of dimensions width x height while keeping its 59 | * aspect ratio. 60 | * 61 | * @param url URL of the image 62 | * @param listener Listener to receive the decoded bitmap 63 | * @param maxWidth Maximum width to decode this bitmap to, or zero for none 64 | * @param maxHeight Maximum height to decode this bitmap to, or zero for 65 | * none 66 | * @param decodeConfig Format to decode the bitmap to 67 | * @param errorListener Error listener, or null to ignore errors 68 | */ 69 | public ImageRequest(String url, Response.Listener listener, int maxWidth, int maxHeight, 70 | Config decodeConfig, Response.ErrorListener errorListener) { 71 | super(Method.GET, url, errorListener); 72 | setRetryPolicy( 73 | new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT)); 74 | mListener = listener; 75 | mDecodeConfig = decodeConfig; 76 | mMaxWidth = maxWidth; 77 | mMaxHeight = maxHeight; 78 | } 79 | 80 | @Override 81 | public Priority getPriority() { 82 | return Priority.LOW; 83 | } 84 | 85 | /** 86 | * Scales one side of a rectangle to fit aspect ratio. 87 | * 88 | * @param maxPrimary Maximum size of the primary dimension (i.e. width for 89 | * max width), or zero to maintain aspect ratio with secondary 90 | * dimension 91 | * @param maxSecondary Maximum size of the secondary dimension, or zero to 92 | * maintain aspect ratio with primary dimension 93 | * @param actualPrimary Actual size of the primary dimension 94 | * @param actualSecondary Actual size of the secondary dimension 95 | */ 96 | private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, 97 | int actualSecondary) { 98 | // If no dominant value at all, just return the actual. 99 | if (maxPrimary == 0 && maxSecondary == 0) { 100 | return actualPrimary; 101 | } 102 | 103 | // If primary is unspecified, scale primary to match secondary's scaling ratio. 104 | if (maxPrimary == 0) { 105 | double ratio = (double) maxSecondary / (double) actualSecondary; 106 | return (int) (actualPrimary * ratio); 107 | } 108 | 109 | if (maxSecondary == 0) { 110 | return maxPrimary; 111 | } 112 | 113 | double ratio = (double) actualSecondary / (double) actualPrimary; 114 | int resized = maxPrimary; 115 | if (resized * ratio > maxSecondary) { 116 | resized = (int) (maxSecondary / ratio); 117 | } 118 | return resized; 119 | } 120 | 121 | @Override 122 | protected Response parseNetworkResponse(NetworkResponse response) { 123 | // Serialize all decode on a global lock to reduce concurrent heap usage. 124 | synchronized (sDecodeLock) { 125 | try { 126 | return doParse(response); 127 | } catch (OutOfMemoryError e) { 128 | VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl()); 129 | return Response.error(new ParseError(e)); 130 | } 131 | } 132 | } 133 | 134 | /** 135 | * The real guts of parseNetworkResponse. Broken out for readability. 136 | */ 137 | private Response doParse(NetworkResponse response) { 138 | byte[] data = response.data; 139 | BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); 140 | Bitmap bitmap = null; 141 | if (mMaxWidth == 0 && mMaxHeight == 0) { 142 | decodeOptions.inPreferredConfig = mDecodeConfig; 143 | bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 144 | } else { 145 | // If we have to resize this image, first get the natural bounds. 146 | decodeOptions.inJustDecodeBounds = true; 147 | BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 148 | int actualWidth = decodeOptions.outWidth; 149 | int actualHeight = decodeOptions.outHeight; 150 | 151 | // Then compute the dimensions we would ideally like to decode to. 152 | int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, 153 | actualWidth, actualHeight); 154 | int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, 155 | actualHeight, actualWidth); 156 | 157 | // Decode to the nearest power of two scaling factor. 158 | decodeOptions.inJustDecodeBounds = false; 159 | // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it? 160 | // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; 161 | decodeOptions.inSampleSize = 162 | findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); 163 | Bitmap tempBitmap = 164 | BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 165 | 166 | // If necessary, scale down to the maximal acceptable size. 167 | if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || 168 | tempBitmap.getHeight() > desiredHeight)) { 169 | bitmap = Bitmap.createScaledBitmap(tempBitmap, 170 | desiredWidth, desiredHeight, true); 171 | tempBitmap.recycle(); 172 | } else { 173 | bitmap = tempBitmap; 174 | } 175 | } 176 | 177 | if (bitmap == null) { 178 | return Response.error(new ParseError(response)); 179 | } else { 180 | return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response)); 181 | } 182 | } 183 | 184 | @Override 185 | protected void deliverResponse(Bitmap response) { 186 | mListener.onResponse(response); 187 | } 188 | 189 | /** 190 | * Returns the largest power-of-two divisor for use in downscaling a bitmap 191 | * that will not result in the scaling past the desired dimensions. 192 | * 193 | * @param actualWidth Actual width of the bitmap 194 | * @param actualHeight Actual height of the bitmap 195 | * @param desiredWidth Desired width of the bitmap 196 | * @param desiredHeight Desired height of the bitmap 197 | */ 198 | // Visible for testing. 199 | static int findBestSampleSize( 200 | int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { 201 | double wr = (double) actualWidth / desiredWidth; 202 | double hr = (double) actualHeight / desiredHeight; 203 | double ratio = Math.min(wr, hr); 204 | float n = 1.0f; 205 | while ((n * 2) <= ratio) { 206 | n *= 2; 207 | } 208 | 209 | return (int) n; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/HurlStack.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.jude.volley.toolbox; 18 | 19 | import com.jude.volley.AuthFailureError; 20 | import com.jude.volley.Request; 21 | import com.jude.volley.Request.Method; 22 | 23 | import org.apache.http.Header; 24 | import org.apache.http.HttpEntity; 25 | import org.apache.http.HttpResponse; 26 | import org.apache.http.ProtocolVersion; 27 | import org.apache.http.StatusLine; 28 | import org.apache.http.entity.BasicHttpEntity; 29 | import org.apache.http.message.BasicHeader; 30 | import org.apache.http.message.BasicHttpResponse; 31 | import org.apache.http.message.BasicStatusLine; 32 | 33 | import java.io.DataOutputStream; 34 | import java.io.IOException; 35 | import java.io.InputStream; 36 | import java.net.HttpURLConnection; 37 | import java.net.URL; 38 | import java.util.HashMap; 39 | import java.util.List; 40 | import java.util.Map; 41 | import java.util.Map.Entry; 42 | 43 | import javax.net.ssl.HttpsURLConnection; 44 | import javax.net.ssl.SSLSocketFactory; 45 | 46 | /** 47 | * An {@link HttpStack} based on {@link HttpURLConnection}. 48 | */ 49 | public class HurlStack implements HttpStack { 50 | 51 | private static final String HEADER_CONTENT_TYPE = "Content-Type"; 52 | 53 | /** 54 | * An interface for transforming URLs before use. 55 | */ 56 | public interface UrlRewriter { 57 | /** 58 | * Returns a URL to use instead of the provided one, or null to indicate 59 | * this URL should not be used at all. 60 | */ 61 | public String rewriteUrl(String originalUrl); 62 | } 63 | 64 | private final UrlRewriter mUrlRewriter; 65 | private final SSLSocketFactory mSslSocketFactory; 66 | 67 | public HurlStack() { 68 | this(null); 69 | } 70 | 71 | /** 72 | * @param urlRewriter Rewriter to use for request URLs 73 | */ 74 | public HurlStack(UrlRewriter urlRewriter) { 75 | this(urlRewriter, null); 76 | } 77 | 78 | /** 79 | * @param urlRewriter Rewriter to use for request URLs 80 | * @param sslSocketFactory SSL factory to use for HTTPS connections 81 | */ 82 | public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) { 83 | mUrlRewriter = urlRewriter; 84 | mSslSocketFactory = sslSocketFactory; 85 | } 86 | 87 | @Override 88 | public HttpResponse performRequest(Request request, Map additionalHeaders) 89 | throws IOException, AuthFailureError { 90 | String url = request.getUrl(); 91 | HashMap map = new HashMap(); 92 | map.putAll(request.getHeaders()); 93 | map.putAll(additionalHeaders); 94 | if (mUrlRewriter != null) { 95 | String rewritten = mUrlRewriter.rewriteUrl(url); 96 | if (rewritten == null) { 97 | throw new IOException("URL blocked by rewriter: " + url); 98 | } 99 | url = rewritten; 100 | } 101 | URL parsedUrl = new URL(url); 102 | HttpURLConnection connection = openConnection(parsedUrl, request); 103 | for (String headerName : map.keySet()) { 104 | connection.addRequestProperty(headerName, map.get(headerName)); 105 | } 106 | setConnectionParametersForRequest(connection, request); 107 | // Initialize HttpResponse with data from the HttpURLConnection. 108 | ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); 109 | int responseCode = connection.getResponseCode(); 110 | if (responseCode == -1) { 111 | // -1 is returned by getResponseCode() if the response code could not be retrieved. 112 | // Signal to the caller that something was wrong with the connection. 113 | throw new IOException("Could not retrieve response code from HttpUrlConnection."); 114 | } 115 | StatusLine responseStatus = new BasicStatusLine(protocolVersion, 116 | connection.getResponseCode(), connection.getResponseMessage()); 117 | BasicHttpResponse response = new BasicHttpResponse(responseStatus); 118 | response.setEntity(entityFromConnection(connection)); 119 | for (Entry> header : connection.getHeaderFields().entrySet()) { 120 | if (header.getKey() != null) { 121 | Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); 122 | response.addHeader(h); 123 | } 124 | } 125 | return response; 126 | } 127 | 128 | /** 129 | * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}. 130 | * @param connection 131 | * @return an HttpEntity populated with data from connection. 132 | */ 133 | private static HttpEntity entityFromConnection(HttpURLConnection connection) { 134 | BasicHttpEntity entity = new BasicHttpEntity(); 135 | InputStream inputStream; 136 | try { 137 | inputStream = connection.getInputStream(); 138 | } catch (IOException ioe) { 139 | inputStream = connection.getErrorStream(); 140 | } 141 | entity.setContent(inputStream); 142 | entity.setContentLength(connection.getContentLength()); 143 | entity.setContentEncoding(connection.getContentEncoding()); 144 | entity.setContentType(connection.getContentType()); 145 | return entity; 146 | } 147 | 148 | /** 149 | * Create an {@link HttpURLConnection} for the specified {@code url}. 150 | */ 151 | protected HttpURLConnection createConnection(URL url) throws IOException { 152 | return (HttpURLConnection) url.openConnection(); 153 | } 154 | 155 | /** 156 | * Opens an {@link HttpURLConnection} with parameters. 157 | * @param url 158 | * @return an open connection 159 | * @throws IOException 160 | */ 161 | private HttpURLConnection openConnection(URL url, Request request) throws IOException { 162 | HttpURLConnection connection = createConnection(url); 163 | 164 | int timeoutMs = request.getTimeoutMs(); 165 | connection.setConnectTimeout(timeoutMs); 166 | connection.setReadTimeout(timeoutMs); 167 | connection.setUseCaches(false); 168 | connection.setDoInput(true); 169 | 170 | // use caller-provided custom SslSocketFactory, if any, for HTTPS 171 | if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) { 172 | ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory); 173 | } 174 | 175 | return connection; 176 | } 177 | 178 | @SuppressWarnings("deprecation") 179 | /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection, 180 | Request request) throws IOException, AuthFailureError { 181 | switch (request.getMethod()) { 182 | case Method.DEPRECATED_GET_OR_POST: 183 | // This is the deprecated way that needs to be handled for backwards compatibility. 184 | // If the request's post body is null, then the assumption is that the request is 185 | // GET. Otherwise, it is assumed that the request is a POST. 186 | byte[] postBody = request.getPostBody(); 187 | if (postBody != null) { 188 | // Prepare output. There is no need to set Content-Length explicitly, 189 | // since this is handled by HttpURLConnection using the size of the prepared 190 | // output stream. 191 | connection.setDoOutput(true); 192 | connection.setRequestMethod("POST"); 193 | connection.addRequestProperty(HEADER_CONTENT_TYPE, 194 | request.getPostBodyContentType()); 195 | DataOutputStream out = new DataOutputStream(connection.getOutputStream()); 196 | out.write(postBody); 197 | out.close(); 198 | } 199 | break; 200 | case Method.GET: 201 | // Not necessary to set the request method because connection defaults to GET but 202 | // being explicit here. 203 | connection.setRequestMethod("GET"); 204 | break; 205 | case Method.DELETE: 206 | connection.setRequestMethod("DELETE"); 207 | break; 208 | case Method.POST: 209 | connection.setRequestMethod("POST"); 210 | addBodyIfExists(connection, request); 211 | break; 212 | case Method.PUT: 213 | connection.setRequestMethod("PUT"); 214 | addBodyIfExists(connection, request); 215 | break; 216 | case Method.HEAD: 217 | connection.setRequestMethod("HEAD"); 218 | break; 219 | case Method.OPTIONS: 220 | connection.setRequestMethod("OPTIONS"); 221 | break; 222 | case Method.TRACE: 223 | connection.setRequestMethod("TRACE"); 224 | break; 225 | case Method.PATCH: 226 | addBodyIfExists(connection, request); 227 | connection.setRequestMethod("PATCH"); 228 | break; 229 | default: 230 | throw new IllegalStateException("Unknown method type."); 231 | } 232 | } 233 | 234 | private static void addBodyIfExists(HttpURLConnection connection, Request request) 235 | throws IOException, AuthFailureError { 236 | byte[] body = request.getBody(); 237 | if (body != null) { 238 | connection.setDoOutput(true); 239 | connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); 240 | DataOutputStream out = new DataOutputStream(connection.getOutputStream()); 241 | out.write(body); 242 | out.close(); 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/RequestQueue.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.jude.volley; 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import android.util.Log; 22 | 23 | import java.util.HashMap; 24 | import java.util.HashSet; 25 | import java.util.LinkedList; 26 | import java.util.Map; 27 | import java.util.Queue; 28 | import java.util.Set; 29 | import java.util.concurrent.PriorityBlockingQueue; 30 | import java.util.concurrent.atomic.AtomicInteger; 31 | 32 | /** 33 | * A request dispatch queue with a thread pool of dispatchers. 34 | * 35 | * Calling {@link #add(Request)} will enqueue the given Request for dispatch, 36 | * resolving from either cache or network on a worker thread, and then delivering 37 | * a parsed response on the main thread. 38 | */ 39 | public class RequestQueue { 40 | 41 | /** Used for generating monotonically-increasing sequence numbers for requests. */ 42 | private AtomicInteger mSequenceGenerator = new AtomicInteger(); 43 | 44 | /** 45 | * Staging area for requests that already have a duplicate request in flight. 46 | * 47 | *
    48 | *
  • containsKey(cacheKey) indicates that there is a request in flight for the given cache 49 | * key.
  • 50 | *
  • get(cacheKey) returns waiting requests for the given cache key. The in flight request 51 | * is not contained in that list. Is null if no requests are staged.
  • 52 | *
53 | */ 54 | private final Map>> mWaitingRequests = 55 | new HashMap>>(); 56 | 57 | /** 58 | * The set of all requests currently being processed by this RequestQueue. A Request 59 | * will be in this set if it is waiting in any queue or currently being processed by 60 | * any dispatcher. 61 | */ 62 | private final Set> mCurrentRequests = new HashSet>(); 63 | 64 | /** The cache triage queue. */ 65 | private final PriorityBlockingQueue> mCacheQueue = 66 | new PriorityBlockingQueue>(); 67 | 68 | /** The queue of requests that are actually going out to the network. */ 69 | private final PriorityBlockingQueue> mNetworkQueue = 70 | new PriorityBlockingQueue>(); 71 | 72 | /** Number of network request dispatcher threads to start. */ 73 | private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; 74 | 75 | /** Cache interface for retrieving and storing responses. */ 76 | private final Cache mCache; 77 | 78 | /** Network interface for performing requests. */ 79 | private final Network mNetwork; 80 | 81 | /** Response delivery mechanism. */ 82 | private final ResponseDelivery mDelivery; 83 | 84 | /** The network dispatchers. */ 85 | private NetworkDispatcher[] mDispatchers; 86 | 87 | /** The cache dispatcher. */ 88 | private CacheDispatcher mCacheDispatcher; 89 | 90 | /** 91 | * Creates the worker pool. Processing will not begin until {@link #start()} is called. 92 | * 93 | * @param cache A Cache to use for persisting responses to disk 94 | * @param network A Network interface for performing HTTP requests 95 | * @param threadPoolSize Number of network dispatcher threads to create 96 | * @param delivery A ResponseDelivery interface for posting responses and errors 97 | */ 98 | public RequestQueue(Cache cache, Network network, int threadPoolSize, 99 | ResponseDelivery delivery) { 100 | mCache = cache; 101 | mNetwork = network; 102 | mDispatchers = new NetworkDispatcher[threadPoolSize]; 103 | mDelivery = delivery; 104 | } 105 | 106 | /** 107 | * Creates the worker pool. Processing will not begin until {@link #start()} is called. 108 | * 109 | * @param cache A Cache to use for persisting responses to disk 110 | * @param network A Network interface for performing HTTP requests 111 | * @param threadPoolSize Number of network dispatcher threads to create 112 | */ 113 | public RequestQueue(Cache cache, Network network, int threadPoolSize) { 114 | this(cache, network, threadPoolSize, 115 | new ExecutorDelivery(new Handler(Looper.getMainLooper()))); 116 | } 117 | 118 | /** 119 | * Creates the worker pool. Processing will not begin until {@link #start()} is called. 120 | * 121 | * @param cache A Cache to use for persisting responses to disk 122 | * @param network A Network interface for performing HTTP requests 123 | */ 124 | public RequestQueue(Cache cache, Network network) { 125 | this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); 126 | } 127 | 128 | /** 129 | * Starts the dispatchers in this queue. 130 | */ 131 | public void start() { 132 | stop(); // Make sure any currently running dispatchers are stopped. 133 | // Create the cache dispatcher and start it. 134 | mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); 135 | mCacheDispatcher.start(); 136 | 137 | // Create network dispatchers (and corresponding threads) up to the pool size. 138 | for (int i = 0; i < mDispatchers.length; i++) { 139 | NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, 140 | mCache, mDelivery); 141 | mDispatchers[i] = networkDispatcher; 142 | networkDispatcher.start(); 143 | } 144 | } 145 | 146 | /** 147 | * Stops the cache and network dispatchers. 148 | */ 149 | public void stop() { 150 | if (mCacheDispatcher != null) { 151 | mCacheDispatcher.quit(); 152 | } 153 | for (int i = 0; i < mDispatchers.length; i++) { 154 | if (mDispatchers[i] != null) { 155 | mDispatchers[i].quit(); 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * Gets a sequence number. 162 | */ 163 | public int getSequenceNumber() { 164 | return mSequenceGenerator.incrementAndGet(); 165 | } 166 | 167 | /** 168 | * Gets the {@link Cache} instance being used. 169 | */ 170 | public Cache getCache() { 171 | return mCache; 172 | } 173 | 174 | /** 175 | * A simple predicate or filter interface for Requests, for use by 176 | * {@link RequestQueue#cancelAll(RequestFilter)}. 177 | */ 178 | public interface RequestFilter { 179 | public boolean apply(Request request); 180 | } 181 | 182 | /** 183 | * Cancels all requests in this queue for which the given filter applies. 184 | * @param filter The filtering function to use 185 | */ 186 | public void cancelAll(RequestFilter filter) { 187 | synchronized (mCurrentRequests) { 188 | for (Request request : mCurrentRequests) { 189 | if (filter.apply(request)) { 190 | request.cancel(); 191 | } 192 | } 193 | } 194 | } 195 | 196 | /** 197 | * Cancels all requests in this queue with the given tag. Tag must be non-null 198 | * and equality is by identity. 199 | */ 200 | public void cancelAll(final Object tag) { 201 | if (tag == null) { 202 | throw new IllegalArgumentException("Cannot cancelAll with a null tag"); 203 | } 204 | cancelAll(new RequestFilter() { 205 | @Override 206 | public boolean apply(Request request) { 207 | return request.getTag() == tag; 208 | } 209 | }); 210 | } 211 | 212 | /** 213 | * Adds a Request to the dispatch queue. 214 | * @param request The request to service 215 | * @return The passed-in request 216 | */ 217 | public Request add(Request request) { 218 | // Tag the request as belonging to this queue and add it to the set of current requests. 219 | request.setRequestQueue(this); 220 | synchronized (mCurrentRequests) { 221 | mCurrentRequests.add(request); 222 | } 223 | 224 | // Process requests in the order they are added. 225 | request.setSequence(getSequenceNumber()); 226 | request.addMarker("add-to-queue"); 227 | 228 | // If the request is uncacheable, skip the cache queue and go straight to the network. 229 | if (!request.shouldCache()) { 230 | Log.i("JobNet","Shouldn't cache"); 231 | mNetworkQueue.add(request); 232 | return request; 233 | } 234 | 235 | // Insert request into stage if there's already a request with the same cache key in flight. 236 | synchronized (mWaitingRequests) { 237 | String cacheKey = request.getCacheKey(); 238 | if (mWaitingRequests.containsKey(cacheKey)) { 239 | // There is already a request in flight. Queue up. 240 | Queue> stagedRequests = mWaitingRequests.get(cacheKey); 241 | if (stagedRequests == null) { 242 | stagedRequests = new LinkedList>(); 243 | } 244 | stagedRequests.add(request); 245 | mWaitingRequests.put(cacheKey, stagedRequests); 246 | if (VolleyLog.DEBUG) { 247 | VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); 248 | } 249 | } else { 250 | // Insert 'null' queue for this cacheKey, indicating there is now a request in 251 | // flight. 252 | mWaitingRequests.put(cacheKey, null); 253 | mCacheQueue.add(request); 254 | } 255 | return request; 256 | } 257 | } 258 | 259 | /** 260 | * Called from {@link Request#finish(String)}, indicating that processing of the given request 261 | * has finished. 262 | * 263 | *

Releases waiting requests for request.getCacheKey() if 264 | * request.shouldCache().

265 | */ 266 | void finish(Request request) { 267 | // Remove from the set of requests currently being processed. 268 | synchronized (mCurrentRequests) { 269 | mCurrentRequests.remove(request); 270 | } 271 | 272 | if (request.shouldCache()) { 273 | synchronized (mWaitingRequests) { 274 | String cacheKey = request.getCacheKey(); 275 | Queue> waitingRequests = mWaitingRequests.remove(cacheKey); 276 | if (waitingRequests != null) { 277 | if (VolleyLog.DEBUG) { 278 | VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", 279 | waitingRequests.size(), cacheKey); 280 | } 281 | // Process all queued up requests. They won't be considered as in flight, but 282 | // that's not a problem as the cache has been primed by 'request'. 283 | mCacheQueue.addAll(waitingRequests); 284 | } 285 | } 286 | } 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/BasicNetwork.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.jude.volley.toolbox; 18 | 19 | import android.os.SystemClock; 20 | 21 | import com.jude.volley.AuthFailureError; 22 | import com.jude.volley.Cache; 23 | import com.jude.volley.Network; 24 | import com.jude.volley.NetworkError; 25 | import com.jude.volley.NetworkResponse; 26 | import com.jude.volley.NoConnectionError; 27 | import com.jude.volley.Request; 28 | import com.jude.volley.RetryPolicy; 29 | import com.jude.volley.ServerError; 30 | import com.jude.volley.TimeoutError; 31 | import com.jude.volley.VolleyError; 32 | import com.jude.volley.VolleyLog; 33 | 34 | import org.apache.http.Header; 35 | import org.apache.http.HttpEntity; 36 | import org.apache.http.HttpResponse; 37 | import org.apache.http.HttpStatus; 38 | import org.apache.http.StatusLine; 39 | import org.apache.http.conn.ConnectTimeoutException; 40 | import org.apache.http.impl.cookie.DateUtils; 41 | 42 | import java.io.IOException; 43 | import java.io.InputStream; 44 | import java.net.MalformedURLException; 45 | import java.net.SocketTimeoutException; 46 | import java.util.Date; 47 | import java.util.HashMap; 48 | import java.util.Map; 49 | 50 | /** 51 | * A network performing Volley requests over an {@link HttpStack}. 52 | */ 53 | public class BasicNetwork implements Network { 54 | protected static final boolean DEBUG = VolleyLog.DEBUG; 55 | 56 | private static int SLOW_REQUEST_THRESHOLD_MS = 3000; 57 | 58 | private static int DEFAULT_POOL_SIZE = 4096; 59 | 60 | protected final HttpStack mHttpStack; 61 | 62 | protected final ByteArrayPool mPool; 63 | 64 | /** 65 | * @param httpStack HTTP stack to be used 66 | */ 67 | public BasicNetwork(HttpStack httpStack) { 68 | // If a pool isn't passed in, then build a small default pool that will give us a lot of 69 | // benefit and not use too much memory. 70 | this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE)); 71 | } 72 | 73 | /** 74 | * @param httpStack HTTP stack to be used 75 | * @param pool a buffer pool that improves GC performance in copy operations 76 | */ 77 | public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) { 78 | mHttpStack = httpStack; 79 | mPool = pool; 80 | } 81 | 82 | @Override 83 | public NetworkResponse performRequest(Request request) throws VolleyError { 84 | long requestStart = SystemClock.elapsedRealtime(); 85 | while (true) { 86 | HttpResponse httpResponse = null; 87 | byte[] responseContents = null; 88 | Map responseHeaders = new HashMap(); 89 | try { 90 | // Gather headers. 91 | Map headers = new HashMap(); 92 | addCacheHeaders(headers, request.getCacheEntry()); 93 | httpResponse = mHttpStack.performRequest(request, headers); 94 | StatusLine statusLine = httpResponse.getStatusLine(); 95 | int statusCode = statusLine.getStatusCode(); 96 | 97 | responseHeaders = convertHeaders(httpResponse.getAllHeaders()); 98 | // Handle cache validation. 99 | if (statusCode == HttpStatus.SC_NOT_MODIFIED) { 100 | return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, 101 | request.getCacheEntry() == null ? null : request.getCacheEntry().data, 102 | responseHeaders, true); 103 | } 104 | 105 | // Handle moved resources 106 | if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { 107 | String newUrl = responseHeaders.get("Location"); 108 | request.setRedirectUrl(newUrl); 109 | } 110 | 111 | // Some responses such as 204s do not have content. We must check. 112 | if (httpResponse.getEntity() != null) { 113 | responseContents = entityToBytes(httpResponse.getEntity()); 114 | } else { 115 | // Add 0 byte response as a way of honestly representing a 116 | // no-content request. 117 | responseContents = new byte[0]; 118 | } 119 | 120 | // if the request is slow, log it. 121 | long requestLifetime = SystemClock.elapsedRealtime() - requestStart; 122 | logSlowRequests(requestLifetime, request, responseContents, statusLine); 123 | 124 | if (statusCode < 200 || statusCode > 299) { 125 | throw new IOException(); 126 | } 127 | return new NetworkResponse(statusCode, responseContents, responseHeaders, false); 128 | } catch (SocketTimeoutException e) { 129 | attemptRetryOnException("socket", request, new TimeoutError()); 130 | } catch (ConnectTimeoutException e) { 131 | attemptRetryOnException("connection", request, new TimeoutError()); 132 | } catch (MalformedURLException e) { 133 | throw new RuntimeException("Bad URL " + request.getUrl(), e); 134 | } catch (IOException e) { 135 | int statusCode = 0; 136 | NetworkResponse networkResponse = null; 137 | if (httpResponse != null) { 138 | statusCode = httpResponse.getStatusLine().getStatusCode(); 139 | } else { 140 | throw new NoConnectionError(e); 141 | } 142 | if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 143 | statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { 144 | VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl()); 145 | } else { 146 | VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); 147 | } 148 | if (responseContents != null) { 149 | networkResponse = new NetworkResponse(statusCode, responseContents, 150 | responseHeaders, false); 151 | if (statusCode == HttpStatus.SC_UNAUTHORIZED || 152 | statusCode == HttpStatus.SC_FORBIDDEN) { 153 | attemptRetryOnException("auth", 154 | request, new AuthFailureError(networkResponse)); 155 | } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 156 | statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { 157 | attemptRetryOnException("redirect", 158 | request, new AuthFailureError(networkResponse)); 159 | } else { 160 | // TODO: Only throw ServerError for 5xx status codes. 161 | throw new ServerError(networkResponse); 162 | } 163 | } else { 164 | throw new NetworkError(networkResponse); 165 | } 166 | } 167 | } 168 | } 169 | 170 | /** 171 | * Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete. 172 | */ 173 | private void logSlowRequests(long requestLifetime, Request request, 174 | byte[] responseContents, StatusLine statusLine) { 175 | if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) { 176 | VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " + 177 | "[rc=%d], [retryCount=%s]", request, requestLifetime, 178 | responseContents != null ? responseContents.length : "null", 179 | statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount()); 180 | } 181 | } 182 | 183 | /** 184 | * Attempts to prepare the request for a retry. If there are no more attempts remaining in the 185 | * request's retry policy, a timeout exception is thrown. 186 | * @param request The request to use. 187 | */ 188 | private static void attemptRetryOnException(String logPrefix, Request request, 189 | VolleyError exception) throws VolleyError { 190 | RetryPolicy retryPolicy = request.getRetryPolicy(); 191 | int oldTimeout = request.getTimeoutMs(); 192 | 193 | try { 194 | retryPolicy.retry(exception); 195 | } catch (VolleyError e) { 196 | request.addMarker( 197 | String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); 198 | throw e; 199 | } 200 | request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); 201 | } 202 | 203 | private void addCacheHeaders(Map headers, Cache.Entry entry) { 204 | // If there's no cache entry, we're done. 205 | if (entry == null) { 206 | return; 207 | } 208 | 209 | if (entry.etag != null) { 210 | headers.put("If-None-Match", entry.etag); 211 | } 212 | 213 | if (entry.serverDate > 0) { 214 | Date refTime = new Date(entry.serverDate); 215 | headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); 216 | } 217 | } 218 | 219 | protected void logError(String what, String url, long start) { 220 | long now = SystemClock.elapsedRealtime(); 221 | VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url); 222 | } 223 | 224 | /** Reads the contents of HttpEntity into a byte[]. */ 225 | private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError { 226 | PoolingByteArrayOutputStream bytes = 227 | new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength()); 228 | byte[] buffer = null; 229 | try { 230 | InputStream in = entity.getContent(); 231 | if (in == null) { 232 | throw new ServerError(); 233 | } 234 | buffer = mPool.getBuf(1024); 235 | int count; 236 | while ((count = in.read(buffer)) != -1) { 237 | bytes.write(buffer, 0, count); 238 | } 239 | return bytes.toByteArray(); 240 | } finally { 241 | try { 242 | // Close the InputStream and release the resources by "consuming the content". 243 | entity.consumeContent(); 244 | } catch (IOException e) { 245 | // This can happen if there was an exception above that left the entity in 246 | // an invalid state. 247 | VolleyLog.v("Error occured when calling consumingContent"); 248 | } 249 | mPool.returnBuf(buffer); 250 | bytes.close(); 251 | } 252 | } 253 | 254 | /** 255 | * Converts Headers[] to Map. 256 | */ 257 | private static Map convertHeaders(Header[] headers) { 258 | Map result = new HashMap(); 259 | for (int i = 0; i < headers.length; i++) { 260 | result.put(headers[i].getName(), headers[i].getValue()); 261 | } 262 | return result; 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /requestvolley/src/main/java/com/jude/volley/toolbox/DiskBasedCache.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.jude.volley.toolbox; 18 | 19 | import android.os.SystemClock; 20 | 21 | import com.jude.volley.Cache; 22 | import com.jude.volley.VolleyLog; 23 | 24 | import java.io.EOFException; 25 | import java.io.File; 26 | import java.io.FileInputStream; 27 | import java.io.FileOutputStream; 28 | import java.io.FilterInputStream; 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | import java.io.OutputStream; 32 | import java.util.Collections; 33 | import java.util.HashMap; 34 | import java.util.Iterator; 35 | import java.util.LinkedHashMap; 36 | import java.util.Map; 37 | 38 | /** 39 | * Cache implementation that caches files directly onto the hard disk in the specified 40 | * directory. The default disk usage size is 5MB, but is configurable. 41 | */ 42 | public class DiskBasedCache implements Cache { 43 | 44 | /** Map of the Key, CacheHeader pairs */ 45 | private final Map mEntries = 46 | new LinkedHashMap(16, .75f, true); 47 | 48 | /** Total amount of space currently used by the cache in bytes. */ 49 | private long mTotalSize = 0; 50 | 51 | /** The root directory to use for the cache. */ 52 | private final File mRootDirectory; 53 | 54 | /** The maximum size of the cache in bytes. */ 55 | private final int mMaxCacheSizeInBytes; 56 | 57 | /** Default maximum disk usage in bytes. */ 58 | private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024; 59 | 60 | /** High water mark percentage for the cache */ 61 | private static final float HYSTERESIS_FACTOR = 0.9f; 62 | 63 | /** Magic number for current version of cache file format. */ 64 | private static final int CACHE_MAGIC = 0x20120504; 65 | 66 | /** 67 | * Constructs an instance of the DiskBasedCache at the specified directory. 68 | * @param rootDirectory The root directory of the cache. 69 | * @param maxCacheSizeInBytes The maximum size of the cache in bytes. 70 | */ 71 | public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { 72 | mRootDirectory = rootDirectory; 73 | mMaxCacheSizeInBytes = maxCacheSizeInBytes; 74 | } 75 | 76 | /** 77 | * Constructs an instance of the DiskBasedCache at the specified directory using 78 | * the default maximum cache size of 5MB. 79 | * @param rootDirectory The root directory of the cache. 80 | */ 81 | public DiskBasedCache(File rootDirectory) { 82 | this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); 83 | } 84 | 85 | /** 86 | * Clears the cache. Deletes all cached files from disk. 87 | */ 88 | @Override 89 | public synchronized void clear() { 90 | File[] files = mRootDirectory.listFiles(); 91 | if (files != null) { 92 | for (File file : files) { 93 | file.delete(); 94 | } 95 | } 96 | mEntries.clear(); 97 | mTotalSize = 0; 98 | VolleyLog.d("Cache cleared."); 99 | } 100 | 101 | /** 102 | * Returns the cache entry with the specified key if it exists, null otherwise. 103 | */ 104 | @Override 105 | public synchronized Entry get(String key) { 106 | CacheHeader entry = mEntries.get(key); 107 | // if the entry does not exist, return. 108 | if (entry == null) { 109 | return null; 110 | } 111 | 112 | File file = getFileForKey(key); 113 | CountingInputStream cis = null; 114 | try { 115 | cis = new CountingInputStream(new FileInputStream(file)); 116 | CacheHeader.readHeader(cis); // eat header 117 | byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); 118 | return entry.toCacheEntry(data); 119 | } catch (IOException e) { 120 | VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString()); 121 | remove(key); 122 | return null; 123 | } finally { 124 | if (cis != null) { 125 | try { 126 | cis.close(); 127 | } catch (IOException ioe) { 128 | return null; 129 | } 130 | } 131 | } 132 | } 133 | 134 | /** 135 | * Initializes the DiskBasedCache by scanning for all files currently in the 136 | * specified root directory. Creates the root directory if necessary. 137 | */ 138 | @Override 139 | public synchronized void initialize() { 140 | if (!mRootDirectory.exists()) { 141 | if (!mRootDirectory.mkdirs()) { 142 | VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath()); 143 | } 144 | return; 145 | } 146 | 147 | File[] files = mRootDirectory.listFiles(); 148 | if (files == null) { 149 | return; 150 | } 151 | for (File file : files) { 152 | FileInputStream fis = null; 153 | try { 154 | fis = new FileInputStream(file); 155 | CacheHeader entry = CacheHeader.readHeader(fis); 156 | entry.size = file.length(); 157 | putEntry(entry.key, entry); 158 | } catch (IOException e) { 159 | if (file != null) { 160 | file.delete(); 161 | } 162 | } finally { 163 | try { 164 | if (fis != null) { 165 | fis.close(); 166 | } 167 | } catch (IOException ignored) { } 168 | } 169 | } 170 | } 171 | 172 | /** 173 | * Invalidates an entry in the cache. 174 | * @param key Cache key 175 | * @param fullExpire True to fully expire the entry, false to soft expire 176 | */ 177 | @Override 178 | public synchronized void invalidate(String key, boolean fullExpire) { 179 | Entry entry = get(key); 180 | if (entry != null) { 181 | entry.softTtl = 0; 182 | if (fullExpire) { 183 | entry.ttl = 0; 184 | } 185 | put(key, entry); 186 | } 187 | 188 | } 189 | 190 | /** 191 | * Puts the entry with the specified key into the cache. 192 | */ 193 | @Override 194 | public synchronized void put(String key, Entry entry) { 195 | pruneIfNeeded(entry.data.length); 196 | File file = getFileForKey(key); 197 | try { 198 | FileOutputStream fos = new FileOutputStream(file); 199 | CacheHeader e = new CacheHeader(key, entry); 200 | e.writeHeader(fos); 201 | fos.write(entry.data); 202 | fos.close(); 203 | putEntry(key, e); 204 | return; 205 | } catch (IOException e) { 206 | } 207 | boolean deleted = file.delete(); 208 | if (!deleted) { 209 | VolleyLog.d("Could not clean up file %s", file.getAbsolutePath()); 210 | } 211 | } 212 | 213 | /** 214 | * Removes the specified key from the cache if it exists. 215 | */ 216 | @Override 217 | public synchronized void remove(String key) { 218 | boolean deleted = getFileForKey(key).delete(); 219 | removeEntry(key); 220 | if (!deleted) { 221 | VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", 222 | key, getFilenameForKey(key)); 223 | } 224 | } 225 | 226 | /** 227 | * Creates a pseudo-unique filename for the specified cache key. 228 | * @param key The key to generate a file name for. 229 | * @return A pseudo-unique filename. 230 | */ 231 | private String getFilenameForKey(String key) { 232 | int firstHalfLength = key.length() / 2; 233 | String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode()); 234 | localFilename += String.valueOf(key.substring(firstHalfLength).hashCode()); 235 | return localFilename; 236 | } 237 | 238 | /** 239 | * Returns a file object for the given cache key. 240 | */ 241 | public File getFileForKey(String key) { 242 | return new File(mRootDirectory, getFilenameForKey(key)); 243 | } 244 | 245 | /** 246 | * Prunes the cache to fit the amount of bytes specified. 247 | * @param neededSpace The amount of bytes we are trying to fit into the cache. 248 | */ 249 | private void pruneIfNeeded(int neededSpace) { 250 | if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { 251 | return; 252 | } 253 | if (VolleyLog.DEBUG) { 254 | VolleyLog.v("Pruning old cache entries."); 255 | } 256 | 257 | long before = mTotalSize; 258 | int prunedFiles = 0; 259 | long startTime = SystemClock.elapsedRealtime(); 260 | 261 | Iterator> iterator = mEntries.entrySet().iterator(); 262 | while (iterator.hasNext()) { 263 | Map.Entry entry = iterator.next(); 264 | CacheHeader e = entry.getValue(); 265 | boolean deleted = getFileForKey(e.key).delete(); 266 | if (deleted) { 267 | mTotalSize -= e.size; 268 | } else { 269 | VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", 270 | e.key, getFilenameForKey(e.key)); 271 | } 272 | iterator.remove(); 273 | prunedFiles++; 274 | 275 | if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) { 276 | break; 277 | } 278 | } 279 | 280 | if (VolleyLog.DEBUG) { 281 | VolleyLog.v("pruned %d files, %d bytes, %d ms", 282 | prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime); 283 | } 284 | } 285 | 286 | /** 287 | * Puts the entry with the specified key into the cache. 288 | * @param key The key to identify the entry by. 289 | * @param entry The entry to cache. 290 | */ 291 | private void putEntry(String key, CacheHeader entry) { 292 | if (!mEntries.containsKey(key)) { 293 | mTotalSize += entry.size; 294 | } else { 295 | CacheHeader oldEntry = mEntries.get(key); 296 | mTotalSize += (entry.size - oldEntry.size); 297 | } 298 | mEntries.put(key, entry); 299 | } 300 | 301 | /** 302 | * Removes the entry identified by 'key' from the cache. 303 | */ 304 | private void removeEntry(String key) { 305 | CacheHeader entry = mEntries.get(key); 306 | if (entry != null) { 307 | mTotalSize -= entry.size; 308 | mEntries.remove(key); 309 | } 310 | } 311 | 312 | /** 313 | * Reads the contents of an InputStream into a byte[]. 314 | * */ 315 | private static byte[] streamToBytes(InputStream in, int length) throws IOException { 316 | byte[] bytes = new byte[length]; 317 | int count; 318 | int pos = 0; 319 | while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) { 320 | pos += count; 321 | } 322 | if (pos != length) { 323 | throw new IOException("Expected " + length + " bytes, read " + pos + " bytes"); 324 | } 325 | return bytes; 326 | } 327 | 328 | /** 329 | * Handles holding onto the cache headers for an entry. 330 | */ 331 | // Visible for testing. 332 | static class CacheHeader { 333 | /** The size of the data identified by this CacheHeader. (This is not 334 | * serialized to disk. */ 335 | public long size; 336 | 337 | /** The key that identifies the cache entry. */ 338 | public String key; 339 | 340 | /** ETag for cache coherence. */ 341 | public String etag; 342 | 343 | /** Date of this response as reported by the server. */ 344 | public long serverDate; 345 | 346 | /** TTL for this record. */ 347 | public long ttl; 348 | 349 | /** Soft TTL for this record. */ 350 | public long softTtl; 351 | 352 | /** Headers from the response resulting in this cache entry. */ 353 | public Map responseHeaders; 354 | 355 | private CacheHeader() { } 356 | 357 | /** 358 | * Instantiates a new CacheHeader object 359 | * @param key The key that identifies the cache entry 360 | * @param entry The cache entry. 361 | */ 362 | public CacheHeader(String key, Entry entry) { 363 | this.key = key; 364 | this.size = entry.data.length; 365 | this.etag = entry.etag; 366 | this.serverDate = entry.serverDate; 367 | this.ttl = entry.ttl; 368 | this.softTtl = entry.softTtl; 369 | this.responseHeaders = entry.responseHeaders; 370 | } 371 | 372 | /** 373 | * Reads the header off of an InputStream and returns a CacheHeader object. 374 | * @param is The InputStream to read from. 375 | * @throws IOException 376 | */ 377 | public static CacheHeader readHeader(InputStream is) throws IOException { 378 | CacheHeader entry = new CacheHeader(); 379 | int magic = readInt(is); 380 | if (magic != CACHE_MAGIC) { 381 | // don't bother deleting, it'll get pruned eventually 382 | throw new IOException(); 383 | } 384 | entry.key = readString(is); 385 | entry.etag = readString(is); 386 | if (entry.etag.equals("")) { 387 | entry.etag = null; 388 | } 389 | entry.serverDate = readLong(is); 390 | entry.ttl = readLong(is); 391 | entry.softTtl = readLong(is); 392 | entry.responseHeaders = readStringStringMap(is); 393 | return entry; 394 | } 395 | 396 | /** 397 | * Creates a cache entry for the specified data. 398 | */ 399 | public Entry toCacheEntry(byte[] data) { 400 | Entry e = new Entry(); 401 | e.data = data; 402 | e.etag = etag; 403 | e.serverDate = serverDate; 404 | e.ttl = ttl; 405 | e.softTtl = softTtl; 406 | e.responseHeaders = responseHeaders; 407 | return e; 408 | } 409 | 410 | 411 | /** 412 | * Writes the contents of this CacheHeader to the specified OutputStream. 413 | */ 414 | public boolean writeHeader(OutputStream os) { 415 | try { 416 | writeInt(os, CACHE_MAGIC); 417 | writeString(os, key); 418 | writeString(os, etag == null ? "" : etag); 419 | writeLong(os, serverDate); 420 | writeLong(os, ttl); 421 | writeLong(os, softTtl); 422 | writeStringStringMap(responseHeaders, os); 423 | os.flush(); 424 | return true; 425 | } catch (IOException e) { 426 | VolleyLog.d("%s", e.toString()); 427 | return false; 428 | } 429 | } 430 | 431 | } 432 | 433 | private static class CountingInputStream extends FilterInputStream { 434 | private int bytesRead = 0; 435 | 436 | private CountingInputStream(InputStream in) { 437 | super(in); 438 | } 439 | 440 | @Override 441 | public int read() throws IOException { 442 | int result = super.read(); 443 | if (result != -1) { 444 | bytesRead++; 445 | } 446 | return result; 447 | } 448 | 449 | @Override 450 | public int read(byte[] buffer, int offset, int count) throws IOException { 451 | int result = super.read(buffer, offset, count); 452 | if (result != -1) { 453 | bytesRead += result; 454 | } 455 | return result; 456 | } 457 | } 458 | 459 | /* 460 | * Homebrewed simple serialization system used for reading and writing cache 461 | * headers on disk. Once upon a time, this used the standard Java 462 | * Object{Input,Output}Stream, but the default implementation relies heavily 463 | * on reflection (even for standard types) and generates a ton of garbage. 464 | */ 465 | 466 | /** 467 | * Simple wrapper around {@link InputStream#read()} that throws EOFException 468 | * instead of returning -1. 469 | */ 470 | private static int read(InputStream is) throws IOException { 471 | int b = is.read(); 472 | if (b == -1) { 473 | throw new EOFException(); 474 | } 475 | return b; 476 | } 477 | 478 | static void writeInt(OutputStream os, int n) throws IOException { 479 | os.write((n >> 0) & 0xff); 480 | os.write((n >> 8) & 0xff); 481 | os.write((n >> 16) & 0xff); 482 | os.write((n >> 24) & 0xff); 483 | } 484 | 485 | static int readInt(InputStream is) throws IOException { 486 | int n = 0; 487 | n |= (read(is) << 0); 488 | n |= (read(is) << 8); 489 | n |= (read(is) << 16); 490 | n |= (read(is) << 24); 491 | return n; 492 | } 493 | 494 | static void writeLong(OutputStream os, long n) throws IOException { 495 | os.write((byte)(n >>> 0)); 496 | os.write((byte)(n >>> 8)); 497 | os.write((byte)(n >>> 16)); 498 | os.write((byte)(n >>> 24)); 499 | os.write((byte)(n >>> 32)); 500 | os.write((byte)(n >>> 40)); 501 | os.write((byte)(n >>> 48)); 502 | os.write((byte)(n >>> 56)); 503 | } 504 | 505 | static long readLong(InputStream is) throws IOException { 506 | long n = 0; 507 | n |= ((read(is) & 0xFFL) << 0); 508 | n |= ((read(is) & 0xFFL) << 8); 509 | n |= ((read(is) & 0xFFL) << 16); 510 | n |= ((read(is) & 0xFFL) << 24); 511 | n |= ((read(is) & 0xFFL) << 32); 512 | n |= ((read(is) & 0xFFL) << 40); 513 | n |= ((read(is) & 0xFFL) << 48); 514 | n |= ((read(is) & 0xFFL) << 56); 515 | return n; 516 | } 517 | 518 | static void writeString(OutputStream os, String s) throws IOException { 519 | byte[] b = s.getBytes("UTF-8"); 520 | writeLong(os, b.length); 521 | os.write(b, 0, b.length); 522 | } 523 | 524 | static String readString(InputStream is) throws IOException { 525 | int n = (int) readLong(is); 526 | byte[] b = streamToBytes(is, n); 527 | return new String(b, "UTF-8"); 528 | } 529 | 530 | static void writeStringStringMap(Map map, OutputStream os) throws IOException { 531 | if (map != null) { 532 | writeInt(os, map.size()); 533 | for (Map.Entry entry : map.entrySet()) { 534 | writeString(os, entry.getKey()); 535 | writeString(os, entry.getValue()); 536 | } 537 | } else { 538 | writeInt(os, 0); 539 | } 540 | } 541 | 542 | static Map readStringStringMap(InputStream is) throws IOException { 543 | int size = readInt(is); 544 | Map result = (size == 0) 545 | ? Collections.emptyMap() 546 | : new HashMap(size); 547 | for (int i = 0; i < size; i++) { 548 | String key = readString(is).intern(); 549 | String value = readString(is).intern(); 550 | result.put(key, value); 551 | } 552 | return result; 553 | } 554 | 555 | 556 | } 557 | --------------------------------------------------------------------------------