10 |
11 | [Demo Download](http://cdn.flowergo.xyz/davinci_demo.apk)
12 |
13 | 从上面我们可以明显看出,Glide加载一张Gif图比DaVinci明显花更久的时间。并且再看加载后的动画效果,DaVinci加载后的Gif图动画非常流畅,而Glide加载过后的Gif的动画有些显示问题。并且用DaVinci加载图片,你可以定制loading过程的图片,而Glide无法做到。
14 |
15 | 我们再来看下实现上述功能,两者需要的代码对比。
16 |
17 | #### DaVinci
18 | ```
19 | DaVinci
20 | .with(this)
21 | .getImageLoader()
22 | .load("http://7xlkhg.com2.z0.glb.qiniucdn.com/qbi_cry.gif")
23 | .into(image1);
24 | ```
25 |
26 | #### Glide
27 | ```
28 | Glide
29 | .with(this)
30 | .load("http://7xlkhg.com2.z0.glb.qiniucdn.com/qbi_cry.gif")
31 | .into(image2);
32 | ```
33 |
34 | 实现方式基本差不多,但你要知道本库可不单单只有图片加载功能哦。
35 |
36 |
37 | ### 1,特色
38 |
39 | 1,支持Gif图片,并且做到Gif库可插拔;
40 |
41 | 2,实现客户端Http请求的Cookie机制,只要调用一个enable方法就搞定;
42 |
43 | 3,支持内存和本地的二级缓存,让图片加载更加流畅;
44 |
45 | 4, 支持使用POST方法获得图片;
46 |
47 | 5, 支持创建多线程池
48 |
49 | 6, 支持上传、下载功能
50 |
51 | ### 2, 使用方法
52 |
53 | 用Gradle的方式导入DaVinci库,因为DaVinci的日志打印采用[VinciLog](https://github.com/CPPAlien/VinciLog),所以需要同时引入VinciLog库
54 |
55 | ```
56 | repositories{
57 | maven { url "https://jitpack.io" }
58 | }
59 | dependencies {
60 | compile 'com.github.CPPAlien:VinciLog:2.0.1'
61 | compile 'com.github.CPPAlien:DaVinci:1.3.2'
62 | }
63 | ```
64 |
65 | ### 3, Get和Post请求
66 | ```
67 | DaVinci.with(Context).getHttpRequest()
68 | doGet(String requestUrl, Map params, OnDaVinciRequestListener requestListener)
69 | doPost(String requestUrl, JSONObject postJsonData, OnDaVinciRequestListener requestListener)
70 | doPost(String requestUrl, String postBodyString, OnDaVinciRequestListener requestListener)
71 |
72 | public interface OnDaVinciRequestListener {
73 | void onDaVinciRequestSuccess(String response);
74 | void onDaVinciRequestFailed(String reason);
75 | }
76 | ```
77 |
78 | ### 4, 从网络上加载图片
79 | ```
80 | DaVinci.with(Context).getImageLoader().load("image url put here").into(imageView);
81 | ```
82 |
83 | 你也可以在into是使用`into(ImageView imageView, int loadingImage, int errorImage)`来设置loading图片,和加载错误时的图片
84 |
85 | 本库Gif图片加载采用koral--实现的[android-gif-drawable](https://github.com/koral--/android-gif-drawable),因为此库底层使用C库进行Gif的编解码,所以效率和显示效果方面都比Glide优秀。
86 |
87 | 开启本库Gif功能,你需要导入`compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.15'`,导入后,加载的图片如果为Gif,则会自动以动图的方式在ImageView里面显示。如果你没有导入该android-gif-drawable库,则Gif图会被当做普通图片处理。
88 |
89 | ### 5,其他用法
90 |
91 | * 如果你不想每次在使用`DaVinci.with(Context)`时都传入`Context`,则你可以在所有调用前先`init`一下,以后只要使用`DaVinci.with()`即可。
92 | ```
93 | /**
94 | * @param isEnableDebug if open log print
95 | * @param debugTag log tag
96 | * @param context context
97 | */
98 | DaVinci.init(boolean isEnableDebug, String debugTag, Context context)
99 | ```
100 |
101 | * 开启Cookie机制,Cookie机制开启后,每次的请求头中都会带有`Cookie`头信息。
102 | ```
103 | DaVinci.with(Context).enableCookie();
104 | ```
105 | * 设置默认的Content-Type (默认是 `application/json`) 和 charset(默认是 `utf-8`,此项可选)
106 | ```
107 | contentType(String contentType, String charset)
108 | ```
109 | * 加入请求头
110 | ```
111 | getHttpRequest().headers(Map headersMap)
112 | ```
113 | * 设置请求超时时间
114 | ```
115 | getHttpRequest().timeOut(int timesOutMs)
116 | ```
117 |
118 | * 设置请求的错误尝试次数
119 | ```
120 | getHttpRequest().maxRetries(int maxRetries)
121 | ```
122 | * 设置加载图片大小,下载后图片长宽按比例缩放为设定的maxpix大小,并且使用url + maxPix的方式作为key, 缓存到本地
123 | ```
124 | getImageLoader().resize(int maxPix).load(...)
125 | ```
126 | **注:1, 对Gif无效;2, 如果不想使用maxPix作为key的一部分,可以使用resize(int maxPix, 0);**
127 |
128 | * 使用POST方法加载图片,body中为post方法体
129 | ```
130 | getImageLoader().body(XXXX).load(...)
131 | ```
132 |
133 | Thanks for DiskLruCache which powered by Jake Wharton.
134 |
135 | https://github.com/JakeWharton/DiskLruCache
136 |
137 | Thanks for Fran Montiel who wrote the PersistentCookieStore(https://gist.github.com/franmontiel/ed12a2295566b7076161)
138 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | applicationId "cn.hadcn.davinci_example"
9 | minSdkVersion 10
10 | targetSdkVersion 23
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 | repositories {
23 | flatDir {
24 | dirs 'libs'
25 | }
26 | }
27 |
28 | dependencies {
29 | compile project(':davinci')
30 | compile 'com.android.support:appcompat-v7:23.3.0'
31 | compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.15'
32 | compile 'com.github.bumptech.glide:glide:3.7.0'
33 | compile 'com.android.support:recyclerview-v7:23.3.0'
34 | //compile 'com.github.CPPAlien:DaVinci:1.1.9'
35 | }
36 |
--------------------------------------------------------------------------------
/app/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 D:\Coding\android-sdk/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/cn/hadcn/davinci_example/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci_example;
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 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/hadcn/davinci_example/ListActivity.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci_example;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ImageView;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import cn.hadcn.davinci.DaVinci;
16 |
17 | public class ListActivity extends AppCompatActivity {
18 | private ImageAdapter mAdapter;
19 | private List urls = new ArrayList<>();
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_list);
25 |
26 | RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
27 | recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
28 | mAdapter = new ImageAdapter();
29 | recyclerView.setAdapter(mAdapter);
30 |
31 | urls.add("http://e.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494eef42db15f28f5e0fe99257e6c.jpg");
32 | urls.add("http://e.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494eef42db15f28f5e0fe99257e6c.jpg");
33 |
34 | mAdapter.notifyDataSetChanged();
35 |
36 | findViewById(R.id.refresh_button).setOnClickListener(new View.OnClickListener() {
37 | @Override
38 | public void onClick(View v) {
39 | urls.add(0, "http://g.hiphotos.baidu.com/image/pic/item/54fbb2fb43166d2219dec065442309f79152d292.jpg");
40 | mAdapter.notifyDataSetChanged();
41 | }
42 | });
43 | }
44 |
45 | private class ImageAdapter extends RecyclerView.Adapter {
46 |
47 | @Override
48 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
49 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false);
50 | return new ViewHolder(view);
51 | }
52 |
53 | @Override
54 | public void onBindViewHolder(ViewHolder holder, int position) {
55 | DaVinci.with().getImageLoader().load(urls.get(position)).into(holder.ivImage);
56 | }
57 |
58 | @Override
59 | public int getItemCount() {
60 | return urls.size();
61 | }
62 |
63 | class ViewHolder extends RecyclerView.ViewHolder {
64 | ImageView ivImage;
65 |
66 | public ViewHolder(View itemView) {
67 | super(itemView);
68 | ivImage = (ImageView)itemView.findViewById(R.id.item_image);
69 | }
70 | }
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/hadcn/davinci_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci_example;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Bitmap;
5 | import android.graphics.drawable.BitmapDrawable;
6 | import android.os.Environment;
7 | import android.support.v4.content.ContextCompat;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.ImageView;
13 | import android.widget.TextView;
14 |
15 | import com.bumptech.glide.Glide;
16 |
17 | import org.json.JSONException;
18 | import org.json.JSONObject;
19 |
20 | import java.io.ByteArrayOutputStream;
21 | import java.io.FileNotFoundException;
22 | import java.io.FileOutputStream;
23 | import java.io.OutputStream;
24 | import java.util.HashMap;
25 | import java.util.Map;
26 |
27 | import cn.hadcn.davinci.DaVinci;
28 | import cn.hadcn.davinci.log.LogLevel;
29 | import cn.hadcn.davinci.http.OnDaVinciRequestListener;
30 | import cn.hadcn.davinci.log.VinciLog;
31 | import cn.hadcn.davinci.other.OnVinciDownloadListener;
32 | import cn.hadcn.davinci.other.OnVinciUploadListener;
33 |
34 | public class MainActivity extends AppCompatActivity implements OnDaVinciRequestListener {
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 |
41 |
42 | DaVinci.init(5, LogLevel.DEBUG, "DaVinciTest", this);
43 | DaVinci.with(this).enableCookie();
44 |
45 | VinciLog.e(null, "a");
46 | VinciLog.e("test %d, test %s", 1, "a");
47 | VinciLog.e("test %d, test %s", "a", "a");
48 |
49 | ImageView image1 = (ImageView)findViewById(R.id.image1);
50 | ImageView image2 = (ImageView)findViewById(R.id.image2);
51 | ImageView image3 = (ImageView)findViewById(R.id.image3);
52 | DaVinci.with(this).getImageLoader().load("http://7xlkhg.com2.z0.glb.qiniucdn.com/qbi_cry.gif").into(image1);
53 |
54 | Glide.with(this).load("http://7xlkhg.com2.z0.glb.qiniucdn.com/qbi_cry.gif").into(image2);
55 | DaVinci.with(this).getImageLoader().load("http://photo.enterdesk.com/2011-11-26/enterdesk.com-1CB20FDF5918603F9264E5BFDC4DF691.jpg").resize(400).into(image3);
56 | image2.setOnClickListener(new View.OnClickListener() {
57 | @Override
58 | public void onClick(View view) {
59 | DaVinci.with(MainActivity.this).getHttpRequest().doGet("http://www.baidu.com/", null, null);
60 | }
61 | });
62 |
63 | Map map = new HashMap<>();
64 | map.put("q", "Beijing,cn");
65 | map.put("appid", "2de143494c0b295cca9337e1e96b00e0");
66 | DaVinci.with(this).getHttpRequest().doGet("http://api.openweathermap.org/data/2.5/weather", map, this);
67 |
68 | DaVinci.with(this).getHttpRequest().doGet("http://api.openweathermap.org/data/2.5/weather", map, this);
69 |
70 | DaVinci.with(this).getHttpRequest().doGet("http://api.openweathermap.org/data/2.5/weather", map, this);
71 |
72 | image3.setOnClickListener(new View.OnClickListener() {
73 | @Override
74 | public void onClick(View v) {
75 | startActivity(new Intent(MainActivity.this, ListActivity.class));
76 | }
77 | });
78 |
79 | String path = "/sdcard/Download/cc_logo.png";
80 | JSONObject jsonObject = new JSONObject();
81 | try {
82 | JSONObject header = new JSONObject();
83 | header.put("tokenId", "0e5495fb-da46-4b28-95ea-e9f6aec1d69a");
84 | jsonObject.put("_header_", header);
85 | } catch (JSONException e) {
86 | e.printStackTrace();
87 | }
88 |
89 | DaVinci.with(this).getUploader().extra("args", jsonObject).upload("http://192.168.3.117:12821/ecp/openapi/qs/file/upload", path, new OnVinciUploadListener() {
90 | @Override
91 | public void onVinciUploadSuccess(JSONObject response) {
92 |
93 | }
94 |
95 | @Override
96 | public void onVinciUploadFailed(String reason) {
97 |
98 | }
99 | });
100 |
101 | DaVinci.with().addThreadPool("one", 1);
102 | DaVinci.with().tag("one").getImageLoader().load("http://y3.ifengimg.com/fashion_spider/dci_2012/02/20a78c36cc31225b1a7efa89f566f591.jpg").resize(600).into(image3);
103 |
104 | OutputStream out;
105 | try {
106 | out = new FileOutputStream(Environment.getExternalStorageDirectory() + "/download/" + "a.txt");
107 | } catch (FileNotFoundException e) {
108 | e.printStackTrace();
109 | return;
110 | }
111 | final TextView tv = (TextView)findViewById(R.id.test_text);
112 | DaVinci.with().getDownloader().body(jsonObject.toString()).download("http://ec2-52-192-96-229.ap-northeast-1.compute.amazonaws.com:12821/ecp/openapi/qs/file/download/p/2016/07/06/03/f5d28e3065244ab9952858f991838246.txt"
113 | , out, new OnVinciDownloadListener() {
114 | @Override
115 | public void onVinciDownloadSuccess() {
116 |
117 | }
118 |
119 | @Override
120 | public void onVinciDownloadFailed(String reason) {
121 |
122 | }
123 |
124 | @Override
125 | public void onVinciDownloadProgress(int progress) {
126 | VinciLog.e("progress = " + progress);
127 |
128 | tv.setText(String.valueOf(progress));
129 | }
130 | });
131 | }
132 |
133 | @Override
134 | public void onDaVinciRequestSuccess(String jsonObject) {
135 | Log.i("DaVinciTest", toString());
136 | }
137 |
138 | @Override
139 | public void onDaVinciRequestFailed(int code, String reason) {
140 |
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
15 |
16 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
18 |
22 |
23 |
24 |
29 |
34 |
38 |
39 |
40 |
46 |
51 |
55 |
56 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPPAlien/DaVinci/8d8e82b4ec21cd57e494c8df67c37f6bec3c9c9f/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPPAlien/DaVinci/8d8e82b4ec21cd57e494c8df67c37f6bec3c9c9f/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPPAlien/DaVinci/8d8e82b4ec21cd57e494c8df67c37f6bec3c9c9f/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPPAlien/DaVinci/8d8e82b4ec21cd57e494c8df67c37f6bec3c9c9f/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPPAlien/DaVinci/8d8e82b4ec21cd57e494c8df67c37f6bec3c9c9f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DaVinci-example
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/cn/hadcn/davinci_example/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci_example;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | maven { url "https://jitpack.io" }
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
--------------------------------------------------------------------------------
/davinci/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/davinci/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 10
9 | versionCode 2016090801
10 | versionName "1.3.5"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | compile files('libs/httpmime-4.1.3.jar')
22 | compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.17'
23 | compile 'com.github.CPPAlien:VinciLog:2.0.1'
24 | }
25 |
--------------------------------------------------------------------------------
/davinci/libs/httpmime-4.1.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPPAlien/DaVinci/8d8e82b4ec21cd57e494c8df67c37f6bec3c9c9f/davinci/libs/httpmime-4.1.3.jar
--------------------------------------------------------------------------------
/davinci/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 D:\Coding\android-sdk/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 |
--------------------------------------------------------------------------------
/davinci/src/androidTest/java/cn/hadcn/davinci/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci;
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 | }
--------------------------------------------------------------------------------
/davinci/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/DaVinci.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci;
2 |
3 | import android.content.Context;
4 |
5 |
6 | import java.net.CookieHandler;
7 | import java.net.CookieManager;
8 | import java.net.CookiePolicy;
9 | import java.net.HttpCookie;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | import cn.hadcn.davinci.base.VolleyManager;
14 | import cn.hadcn.davinci.log.LogLevel;
15 | import cn.hadcn.davinci.log.VinciLog;
16 | import cn.hadcn.davinci.image.VinciImageLoader;
17 | import cn.hadcn.davinci.http.impl.HttpRequest;
18 | import cn.hadcn.davinci.http.impl.PersistentCookieStore;
19 | import cn.hadcn.davinci.other.impl.VinciDownload;
20 | import cn.hadcn.davinci.other.impl.VinciUpload;
21 | import cn.hadcn.davinci.volley.RequestQueue;
22 |
23 |
24 | /**
25 | * DaVinci
26 | * Created by 90Chris on 2015/9/10.
27 | */
28 | public class DaVinci {
29 | /** Number of network request dispatcher threads to start. */
30 | private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
31 |
32 | private static RequestQueue mRequestQueue;
33 | private static VinciImageLoader mDaImageLoader;
34 |
35 | private static RequestQueue mDefaultRequestQueue;
36 | private static VinciImageLoader mDefaultDaImageLoader;
37 |
38 | private static DaVinci sDaVinci = null;
39 | private boolean isEnableCookie = false;
40 | private CookieManager mCookieManager = null;
41 | private static Context mContext = null;
42 | private Map queues = new HashMap<>();
43 | private Map loaders = new HashMap<>();
44 |
45 | public static DaVinci with(Context context) {
46 | mContext = context.getApplicationContext();
47 | if ( sDaVinci == null ) {
48 | sDaVinci = new DaVinci(0);
49 | }
50 |
51 | mRequestQueue = mDefaultRequestQueue;
52 | mDaImageLoader = mDefaultDaImageLoader;
53 |
54 | return sDaVinci;
55 | }
56 |
57 | /**
58 | * if you want to use this way, you must call DaVinci.init(Context) before using it
59 | * @return DaVinci instance
60 | */
61 | public static DaVinci with() {
62 | if ( sDaVinci == null ) {
63 | throw new RuntimeException("DaVinci instance has not been initialized yet, please use DaVinci.init() first");
64 | }
65 | mRequestQueue = mDefaultRequestQueue;
66 | mDaImageLoader = mDefaultDaImageLoader;
67 |
68 | return sDaVinci;
69 | }
70 |
71 | /**
72 | * use other thread pool
73 | * @param tag tag of thread pool
74 | * @return this
75 | */
76 | public DaVinci tag(String tag) {
77 | if ( !queues.containsKey(tag) ) {
78 | throw new RuntimeException("The pool has not been initialized");
79 | }
80 | mRequestQueue = queues.get(tag);
81 | mDaImageLoader = loaders.get(tag);
82 | return this;
83 | }
84 |
85 | /**
86 | * add other thread pool
87 | * @param tag tag
88 | * @param size size
89 | */
90 | public void addThreadPool(String tag, int size) {
91 | if ( size <= 0 ) {
92 | throw new RuntimeException("pool size at least one");
93 | }
94 | RequestQueue requestQueue = VolleyManager.newRequestQueue(mContext, size);
95 | VinciImageLoader imageLoader = new VinciImageLoader(mContext, requestQueue);
96 | queues.put(tag, requestQueue);
97 | loaders.put(tag, imageLoader);
98 | }
99 |
100 | /**
101 | * init DaVinci instance, it's better to put it into application class
102 | * advantages
103 | * 1, you do not use pass context in request any more;
104 | * 2, application context is special in whole application life, so DaVinci do not need hook activity, optimize the memory management;
105 | * @param logLevel log level
106 | * @param debugTag log tag
107 | * @param context context
108 | */
109 | public static void init(LogLevel logLevel, String debugTag, Context context){
110 | init(0, logLevel, debugTag, context);
111 | }
112 |
113 |
114 | /**
115 | * init DaVinci instance, it's better to put it into application class
116 | * advantages
117 | * 1, you do not use pass context in request any more;
118 | * 2, application context is special in whole application life, so DaVinci do not need hook activity, optimize the memory management;
119 | * @param poolSize thread pool size
120 | * @param logLevel log level
121 | * @param debugTag log tag
122 | * @param context context
123 | */
124 | public static void init(int poolSize, LogLevel logLevel, String debugTag, Context context) {
125 | mContext = context.getApplicationContext();
126 | sDaVinci = new DaVinci(poolSize);
127 | VinciLog.init(logLevel, debugTag, context);
128 | }
129 |
130 | /**
131 | * each http request are different instance
132 | * but for image loader, there is only one instance for whole application
133 | */
134 | private DaVinci(int poolSize) {
135 | if ( poolSize <= 0 ) {
136 | poolSize = DEFAULT_NETWORK_THREAD_POOL_SIZE;
137 | }
138 | mDefaultRequestQueue = VolleyManager.newRequestQueue(mContext, poolSize);
139 | mDefaultDaImageLoader = new VinciImageLoader(mContext, mDefaultRequestQueue);
140 | }
141 |
142 | /**
143 | * enable cookie, save cookie when response header contains Set-Cookie
144 | * add Cookie header when sending request
145 | */
146 | public void enableCookie() {
147 | isEnableCookie = true;
148 | if ( mCookieManager == null ) {
149 | mCookieManager = new CookieManager(new PersistentCookieStore(mContext), CookiePolicy.ACCEPT_ALL);
150 | CookieHandler.setDefault(mCookieManager);
151 | }
152 | }
153 |
154 | private int mMaxRetries = 0;
155 | private int mTimeOut = 0;
156 | /**
157 | * set http configures globally
158 | * @param maxRetires max retries, default is 1
159 | * @param timeout timeout, default is 2500ms
160 | */
161 | public void setHttpGlobal(int maxRetires, int timeout) {
162 | mMaxRetries = maxRetires;
163 | mTimeOut = timeout;
164 | }
165 |
166 | /**
167 | * different instances in different calls, and put them into a request queue
168 | * @return HttpRequest
169 | */
170 | public HttpRequest getHttpRequest(){
171 | String cookieString = null;
172 | if ( isEnableCookie ) {
173 | // cookie may be changed at any request, so get the cookie from memory first
174 | // if cookie is empty, get it from disk, else save it.
175 | // because the default CookieStoreImpl doesn't implement local save
176 | StringBuilder cookieBuilder = new StringBuilder();
177 | String divider = "";
178 | for (HttpCookie cookie : mCookieManager.getCookieStore().getCookies()) {
179 | cookieBuilder.append(divider);
180 | divider = ";";
181 | cookieBuilder.append(cookie.getName());
182 | cookieBuilder.append("=");
183 | cookieBuilder.append(cookie.getValue());
184 | }
185 |
186 | cookieString = cookieBuilder.toString();
187 | }
188 |
189 | HttpRequest request = new HttpRequest( mRequestQueue, isEnableCookie, cookieString );
190 | if ( mMaxRetries != 0 ) {
191 | request.maxRetries(mMaxRetries);
192 | }
193 | if ( mTimeOut != 0) {
194 | request.timeOut(mTimeOut);
195 | }
196 | return request;
197 | }
198 |
199 | public VinciImageLoader getImageLoader() {
200 | return mDaImageLoader;
201 | }
202 |
203 | public VinciUpload getUploader() {
204 | return new VinciUpload(mRequestQueue);
205 | }
206 |
207 | public Context getContext() {
208 | return mContext;
209 | }
210 |
211 | private String gBody;
212 | public void setDownloadBody(String body) {
213 | gBody = body;
214 | }
215 |
216 | public VinciDownload getDownloader() {
217 | VinciDownload download = new VinciDownload(mRequestQueue);
218 | download.body(gBody);
219 | return download;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/base/VolleyManager.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 cn.hadcn.davinci.base;
18 |
19 | import android.content.Context;
20 |
21 | import java.io.File;
22 |
23 | import cn.hadcn.davinci.volley.Network;
24 | import cn.hadcn.davinci.volley.RequestQueue;
25 | import cn.hadcn.davinci.volley.toolbox.BasicNetwork;
26 | import cn.hadcn.davinci.volley.toolbox.DiskBasedCache;
27 | import cn.hadcn.davinci.volley.toolbox.HttpStack;
28 | import cn.hadcn.davinci.volley.toolbox.HurlStack;
29 |
30 | public class VolleyManager {
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 poolSize thread pool size, 4 for default.
40 | * @return A started {@link RequestQueue} instance.
41 | */
42 | public static RequestQueue newRequestQueue(Context context, int poolSize) {
43 | File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
44 |
45 | HttpStack stack = new HurlStack();
46 |
47 | Network network = new BasicNetwork(stack);
48 |
49 | RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network, poolSize);
50 | queue.start();
51 |
52 | return queue;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/http/OnDaVinciRequestListener.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.http;
2 |
3 |
4 | /**
5 | * OnDaVinciRequestListener
6 | * Created by 90Chris on 2015/9/10.
7 | */
8 | public interface OnDaVinciRequestListener {
9 | void onDaVinciRequestSuccess(String response);
10 | void onDaVinciRequestFailed(int code, String reason);
11 | }
12 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/http/base/StringRequest.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.http.base;
2 |
3 |
4 | import java.io.UnsupportedEncodingException;
5 | import java.util.Map;
6 |
7 | import cn.hadcn.davinci.log.VinciLog;
8 | import cn.hadcn.davinci.volley.AuthFailureError;
9 | import cn.hadcn.davinci.volley.NetworkResponse;
10 | import cn.hadcn.davinci.volley.ParseError;
11 | import cn.hadcn.davinci.volley.Request;
12 | import cn.hadcn.davinci.volley.Response;
13 | import cn.hadcn.davinci.volley.Response.*;
14 | import cn.hadcn.davinci.volley.toolbox.HttpHeaderParser;
15 |
16 | /**
17 | * A request for retrieving a T type response body at a given URL that also
18 | * optionally sends along a JSON body in the request specified.
19 | */
20 | public abstract class StringRequest extends Request {
21 | protected static final String PROTOCOL_CHARSET = "utf-8";
22 |
23 | private final Listener mListener;
24 | private final String mRequestBody;
25 |
26 |
27 | public StringRequest(int method, String url, String requestBody, Listener listener,
28 | ErrorListener errorListener) {
29 | super(method, url, errorListener);
30 | mListener = listener;
31 | mRequestBody = requestBody;
32 | }
33 |
34 | @Override
35 | protected void deliverResponse(String response) {
36 | mListener.onResponse(response);
37 | }
38 |
39 | /**
40 | * @deprecated Use {@link #getBodyContentType()}.
41 | */
42 | @Override
43 | public String getPostBodyContentType() {
44 | return getBodyContentType();
45 | }
46 |
47 | /**
48 | * @deprecated Use {@link #getBody()}.
49 | */
50 | @Override
51 | public byte[] getPostBody() {
52 | return getBody();
53 | }
54 |
55 | @Override
56 | public abstract String getBodyContentType();
57 |
58 | @Override
59 | public byte[] getBody() {
60 | try {
61 | return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
62 | } catch (UnsupportedEncodingException uee) {
63 | VinciLog.e("Unsupported Encoding while trying to get the bytes of %s using %s",
64 | mRequestBody, PROTOCOL_CHARSET);
65 | return null;
66 | }
67 | }
68 |
69 | @Override
70 | protected Response parseNetworkResponse(NetworkResponse response) {
71 | try {
72 | String jsonString = new String(response.data,
73 | HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
74 |
75 | return Response.success(jsonString,
76 | HttpHeaderParser.parseCacheHeaders(response));
77 | } catch (UnsupportedEncodingException e) {
78 | return Response.error(new ParseError(e));
79 | }
80 | }
81 |
82 | @Override
83 | abstract public Map getHeaders() throws AuthFailureError;
84 | }
85 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/http/impl/HttpRequest.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.http.impl;
2 |
3 |
4 | import org.json.JSONObject;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | import cn.hadcn.davinci.log.VinciLog;
10 | import cn.hadcn.davinci.http.base.StringRequest;
11 | import cn.hadcn.davinci.http.OnDaVinciRequestListener;
12 | import cn.hadcn.davinci.volley.AuthFailureError;
13 | import cn.hadcn.davinci.volley.DefaultRetryPolicy;
14 | import cn.hadcn.davinci.volley.Request;
15 | import cn.hadcn.davinci.volley.RequestQueue;
16 | import cn.hadcn.davinci.volley.Response;
17 | import cn.hadcn.davinci.volley.VolleyError;
18 |
19 | /**
20 | * BaseRequest
21 | * Created by 90Chris on 2015/1/26.
22 | * */
23 | public class HttpRequest {
24 | private RequestQueue mRequestQueue;
25 | private Map mHeadersMap = new HashMap<>();
26 | private String mContentType = null;
27 | private String mCharset = "utf-8";
28 | private int mTimeOutMs = DefaultRetryPolicy.DEFAULT_TIMEOUT_MS;
29 | private int mMaxRetries = DefaultRetryPolicy.DEFAULT_MAX_RETRIES;
30 | private boolean isEnableCookie = false;
31 | private String mCookie = null;
32 | private OnDaVinciRequestListener mRequestListener = null;
33 |
34 | public HttpRequest( RequestQueue requestQueue, boolean enableCookie, String cookie) {
35 | mRequestQueue = requestQueue;
36 | isEnableCookie = enableCookie;
37 | mCookie = cookie;
38 | }
39 |
40 | /**
41 | * timeout millisecond, default is 2500 ms
42 | * @param timeOutMs timeout
43 | * @return this
44 | */
45 | public HttpRequest timeOut(int timeOutMs) {
46 | mTimeOutMs = timeOutMs;
47 | return this;
48 | }
49 |
50 | /**
51 | * retry times, default is once
52 | * @param maxRetries time of retrying
53 | * @return this
54 | */
55 | public HttpRequest maxRetries(int maxRetries) {
56 | mMaxRetries = maxRetries;
57 | return this;
58 | }
59 |
60 | /**
61 | * add header in request
62 | * @param headersMap header
63 | * @return this
64 | */
65 | public HttpRequest headers(Map headersMap) {
66 | mHeadersMap = headersMap;
67 | return this;
68 | }
69 |
70 | /**
71 | * set Content-Type field, default is application/json
72 | * @param contentType content-type
73 | * @return this
74 | */
75 | public HttpRequest contentType(String contentType) {
76 | mContentType = contentType;
77 | return this;
78 | }
79 |
80 | /**
81 | * set Content-Type field, default is application/json
82 | * @param contentType content-type
83 | * @param charset charset of request body, default is utf-8
84 | * @return this
85 | */
86 | public HttpRequest contentType(String contentType, String charset) {
87 | mContentType = contentType;
88 | mCharset = charset;
89 | return this;
90 | }
91 |
92 | /**
93 | * get method
94 | * @param requestUrl request url of get, must include http:// as head
95 | * @param params get parameters, will combine the params as a get request, like http://ninty.cc?a=1&b=2
96 | * @param requestListener listener
97 | */
98 | public void doGet(String requestUrl, Map params, OnDaVinciRequestListener requestListener) {
99 | doRequest(Request.Method.GET,
100 | requestUrl, params, null, requestListener);
101 | }
102 |
103 | /**
104 | * post method
105 | * @param requestUrl request url of post, must include http:// as head
106 | * @param postJsonData post contents, json format data
107 | * @param requestListener listener
108 | */
109 | public void doPost(String requestUrl, JSONObject postJsonData, OnDaVinciRequestListener requestListener) {
110 | doRequest(Request.Method.POST,
111 | requestUrl, null, postJsonData, requestListener);
112 | }
113 |
114 | /**
115 | * post method
116 | * @param requestUrl request url of post, must include http:// as head
117 | * @param postBodyString post body part, String type
118 | * @param requestListener listener
119 | */
120 | public void doPost(String requestUrl, String postBodyString, OnDaVinciRequestListener requestListener) {
121 | doRequest(Request.Method.POST,
122 | requestUrl, null, postBodyString, requestListener);
123 | }
124 |
125 | /**
126 | * post method
127 | * @param requestUrl request url of post, must include http:// as head
128 | * @param requestListener listener
129 | */
130 | public void doPost(String requestUrl, OnDaVinciRequestListener requestListener) {
131 | doRequest(Request.Method.POST,
132 | requestUrl, null, null, requestListener);
133 | }
134 |
135 | /**
136 | * do http request
137 | * @param method GET or POST
138 | * @param url request url
139 | * @param urlMap get method parameters map
140 | * @param postBody post method parameters
141 | * @param requestListener listener
142 | */
143 | private void doRequest(int method, String url, Map urlMap, Object postBody, final OnDaVinciRequestListener requestListener) {
144 | mRequestListener = requestListener;
145 | String requestUrl = url;
146 |
147 | //construct url
148 | if ( null != urlMap ){
149 | requestUrl += "?";
150 | for ( String key : urlMap.keySet() ) {
151 | requestUrl = requestUrl + key + "=" + urlMap.get(key) + "&";
152 | }
153 | }
154 |
155 | VinciLog.d("Do request, url = " + requestUrl);
156 | DaVinciHttp jsonObjectRequest = getRequest(method, requestUrl, postBody);
157 |
158 | if ( jsonObjectRequest == null ){
159 | VinciLog.e("post body type is error, it should be json or string");
160 | return;
161 | }
162 |
163 | if ( isEnableCookie ) {
164 | jsonObjectRequest.setCookie( mCookie );
165 | }
166 | jsonObjectRequest.setRetryPolicy(new DefaultRetryPolicy(mTimeOutMs, mMaxRetries, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
167 | mRequestQueue.add(jsonObjectRequest);
168 | }
169 |
170 | private DaVinciHttp getRequest(int method, String requestUrl, Object postBody) {
171 | if ( null != postBody ){
172 | VinciLog.d("body data = " + postBody.toString());
173 | }
174 |
175 | //inflate body part depends on type we get
176 | DaVinciHttp jsonObjectRequest = null;
177 | if ( postBody == null ) {
178 | jsonObjectRequest = new DaVinciHttp(method, requestUrl,
179 | new ResponseListener(),
180 | new ErrorListener());
181 | } else if ( postBody instanceof JSONObject ) {
182 | jsonObjectRequest = new DaVinciHttp(method, requestUrl, (JSONObject)postBody,
183 | new ResponseListener(),
184 | new ErrorListener());
185 | } else if ( postBody instanceof String ) {
186 | jsonObjectRequest = new DaVinciHttp(method, requestUrl, (String)postBody,
187 | new ResponseListener(),
188 | new ErrorListener());
189 | }
190 |
191 | return jsonObjectRequest;
192 | }
193 |
194 |
195 | private class ResponseListener implements Response.Listener {
196 |
197 | @Override
198 | public void onResponse(String response) {
199 | VinciLog.d("response:" + response);
200 | if ( mRequestListener != null ) {
201 | mRequestListener.onDaVinciRequestSuccess(response);
202 | }
203 | }
204 | }
205 |
206 | private class ErrorListener implements Response.ErrorListener {
207 |
208 | @Override
209 | public void onErrorResponse(VolleyError error) {
210 | if ( error.networkResponse != null ) {
211 | int code = error.networkResponse.statusCode;
212 | byte[] data = error.networkResponse.data;
213 | String reason = ( data == null ? null : new String(data) );
214 | VinciLog.e("http failed: " + code + ", " + reason);
215 | if ( mRequestListener != null ) {
216 | mRequestListener.onDaVinciRequestFailed(code, reason);
217 | }
218 | } else {
219 | VinciLog.e("http failed: There is no Internet connection");
220 | if ( mRequestListener != null ) {
221 | mRequestListener.onDaVinciRequestFailed(-1, "There is no Internet connection");
222 | }
223 | }
224 |
225 | }
226 | }
227 |
228 | private class DaVinciHttp extends StringRequest {
229 |
230 | /** Content type for request. */
231 | private final String PROTOCOL_CONTENT_TYPE = "application/json";
232 |
233 | public DaVinciHttp(int method, String url, String requestBody, Response.Listener listener, Response.ErrorListener errorListener) {
234 | super(method, url, requestBody, listener, errorListener);
235 | }
236 |
237 | @Override
238 | public String getBodyContentType() {
239 | String contentType = PROTOCOL_CONTENT_TYPE;
240 | if ( mContentType != null ) {
241 | contentType = mContentType;
242 | }
243 | return String.format("%s; charset=%s", contentType, mCharset);
244 | }
245 |
246 | public DaVinciHttp(int method, String url, Response.Listener listener, Response.ErrorListener errorListener) {
247 | super(method, url, null, listener, errorListener);
248 | }
249 |
250 | public DaVinciHttp(int method, String url, JSONObject jsonRequest, Response.Listener listener, Response.ErrorListener errorListener) {
251 | super(method, url, jsonRequest.toString(), listener, errorListener);
252 | }
253 |
254 | @Override
255 | public Map getHeaders() throws AuthFailureError {
256 | VinciLog.d("Headers:" + mHeadersMap.toString());
257 | return mHeadersMap;
258 | }
259 |
260 | /**
261 | * set Cookie content
262 | * @param cookie cookie content
263 | */
264 | public void setCookie( String cookie ) {
265 | VinciLog.d("Put Cookie:" + cookie);
266 | mHeadersMap.put("Cookie", cookie);
267 | }
268 | }
269 | }
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/http/impl/PersistentCookieStore.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.http.impl;
2 | /*
3 | * Copyright (c) 2015 Fran Montiel
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | //https://gist.github.com/franmontiel/ed12a2295566b7076161
18 | import android.content.Context;
19 | import android.content.SharedPreferences;
20 | import android.util.Log;
21 |
22 | import java.net.CookieStore;
23 | import java.net.HttpCookie;
24 | import java.net.URI;
25 | import java.net.URISyntaxException;
26 | import java.util.ArrayList;
27 | import java.util.HashMap;
28 | import java.util.HashSet;
29 | import java.util.Iterator;
30 | import java.util.List;
31 | import java.util.Map;
32 | import java.util.Set;
33 |
34 | public class PersistentCookieStore implements CookieStore {
35 | private static final String TAG = PersistentCookieStore.class
36 | .getSimpleName();
37 |
38 | // Persistence
39 | private static final String SP_COOKIE_STORE = "cookieStore";
40 | private static final String SP_KEY_DELIMITER = "|"; // Unusual char in URL
41 | private static final String SP_KEY_DELIMITER_REGEX = "\\"
42 | + SP_KEY_DELIMITER;
43 | private SharedPreferences sharedPreferences;
44 |
45 | // In memory
46 | private Map> allCookies;
47 |
48 | public PersistentCookieStore(Context context) {
49 | sharedPreferences = context.getSharedPreferences(SP_COOKIE_STORE,
50 | Context.MODE_PRIVATE);
51 | loadAllFromPersistence();
52 | }
53 |
54 | private void loadAllFromPersistence() {
55 | allCookies = new HashMap>();
56 |
57 | Map allPairs = sharedPreferences.getAll();
58 | for (Map.Entry entry : allPairs.entrySet()) {
59 | String[] uriAndName = entry.getKey().split(SP_KEY_DELIMITER_REGEX,
60 | 2);
61 | try {
62 | URI uri = new URI(uriAndName[0]);
63 | String encodedCookie = (String) entry.getValue();
64 | HttpCookie cookie = new SerializableHttpCookie()
65 | .decode(encodedCookie);
66 |
67 | Set targetCookies = allCookies.get(uri);
68 | if (targetCookies == null) {
69 | targetCookies = new HashSet();
70 | allCookies.put(uri, targetCookies);
71 | }
72 | // Repeated cookies cannot exist in persistence
73 | // targetCookies.remove(cookie)
74 | targetCookies.add(cookie);
75 | } catch (URISyntaxException e) {
76 | Log.w(TAG, e);
77 | }
78 | }
79 | }
80 |
81 | @Override
82 | public synchronized void add(URI uri, HttpCookie cookie) {
83 | uri = cookieUri(uri, cookie);
84 |
85 | Set targetCookies = allCookies.get(uri);
86 | if (targetCookies == null) {
87 | targetCookies = new HashSet();
88 | allCookies.put(uri, targetCookies);
89 | }
90 | targetCookies.remove(cookie);
91 | targetCookies.add(cookie);
92 |
93 | saveToPersistence(uri, cookie);
94 | }
95 |
96 | /**
97 | * Get the real URI from the cookie "domain" and "path" attributes, if they
98 | * are not set then uses the URI provided (coming from the response)
99 | *
100 | * @param uri
101 | * @param cookie
102 | * @return
103 | */
104 | private static URI cookieUri(URI uri, HttpCookie cookie) {
105 | URI cookieUri = uri;
106 | if (cookie.getDomain() != null) {
107 | // Remove the starting dot character of the domain, if exists (e.g: .domain.com -> domain.com)
108 | String domain = cookie.getDomain();
109 | if (domain.charAt(0) == '.') {
110 | domain = domain.substring(1);
111 | }
112 | try {
113 | cookieUri = new URI(uri.getScheme() == null ? "http"
114 | : uri.getScheme(), domain,
115 | cookie.getPath() == null ? "/" : cookie.getPath(), null);
116 | } catch (URISyntaxException e) {
117 | Log.w(TAG, e);
118 | }
119 | }
120 | return cookieUri;
121 | }
122 |
123 | private void saveToPersistence(URI uri, HttpCookie cookie) {
124 | SharedPreferences.Editor editor = sharedPreferences.edit();
125 |
126 | editor.putString(uri.toString() + SP_KEY_DELIMITER + cookie.getName(),
127 | new SerializableHttpCookie().encode(cookie));
128 |
129 | editor.apply();
130 | }
131 |
132 | @Override
133 | public synchronized List get(URI uri) {
134 | return getValidCookies(uri);
135 | }
136 |
137 | @Override
138 | public synchronized List getCookies() {
139 | List allValidCookies = new ArrayList();
140 | for (URI storedUri : allCookies.keySet()) {
141 | allValidCookies.addAll(getValidCookies(storedUri));
142 | }
143 |
144 | return allValidCookies;
145 | }
146 |
147 | private List getValidCookies(URI uri) {
148 | List targetCookies = new ArrayList();
149 | // If the stored URI does not have a path then it must match any URI in
150 | // the same domain
151 | for (URI storedUri : allCookies.keySet()) {
152 | // Check ith the domains match according to RFC 6265
153 | if (checkDomainsMatch(storedUri.getHost(), uri.getHost())) {
154 | // Check if the paths match according to RFC 6265
155 | if (checkPathsMatch(storedUri.getPath(), uri.getPath())) {
156 | targetCookies.addAll(allCookies.get(storedUri));
157 | }
158 | }
159 | }
160 |
161 | // Check it there are expired cookies and remove them
162 | if (!targetCookies.isEmpty()) {
163 | List cookiesToRemoveFromPersistence = new ArrayList();
164 | for (Iterator it = targetCookies.iterator(); it
165 | .hasNext(); ) {
166 | HttpCookie currentCookie = it.next();
167 | if (currentCookie.hasExpired()) {
168 | cookiesToRemoveFromPersistence.add(currentCookie);
169 | it.remove();
170 | }
171 | }
172 |
173 | if (!cookiesToRemoveFromPersistence.isEmpty()) {
174 | removeFromPersistence(uri, cookiesToRemoveFromPersistence);
175 | }
176 | }
177 | return targetCookies;
178 | }
179 |
180 | /* http://tools.ietf.org/html/rfc6265#section-5.1.3
181 |
182 | A string domain-matches a given domain string if at least one of the
183 | following conditions hold:
184 |
185 | o The domain string and the string are identical. (Note that both
186 | the domain string and the string will have been canonicalized to
187 | lower case at this point.)
188 |
189 | o All of the following conditions hold:
190 |
191 | * The domain string is a suffix of the string.
192 |
193 | * The last character of the string that is not included in the
194 | domain string is a %x2E (".") character.
195 |
196 | * The string is a host name (i.e., not an IP address). */
197 |
198 | private boolean checkDomainsMatch(String cookieHost, String requestHost) {
199 | return requestHost.equals(cookieHost) || requestHost.endsWith("." + cookieHost);
200 | }
201 |
202 | /* http://tools.ietf.org/html/rfc6265#section-5.1.4
203 |
204 | A request-path path-matches a given cookie-path if at least one of
205 | the following conditions holds:
206 |
207 | o The cookie-path and the request-path are identical.
208 |
209 | o The cookie-path is a prefix of the request-path, and the last
210 | character of the cookie-path is %x2F ("/").
211 |
212 | o The cookie-path is a prefix of the request-path, and the first
213 | character of the request-path that is not included in the cookie-
214 | path is a %x2F ("/") character. */
215 |
216 | private boolean checkPathsMatch(String cookiePath, String requestPath) {
217 | return requestPath.equals(cookiePath) ||
218 | (requestPath.startsWith(cookiePath) && cookiePath.charAt(cookiePath.length() - 1) == '/') ||
219 | (requestPath.startsWith(cookiePath) && requestPath.substring(cookiePath.length()).charAt(0) == '/');
220 | }
221 |
222 | private void removeFromPersistence(URI uri, List cookiesToRemove) {
223 | SharedPreferences.Editor editor = sharedPreferences.edit();
224 | for (HttpCookie cookieToRemove : cookiesToRemove) {
225 | editor.remove(uri.toString() + SP_KEY_DELIMITER
226 | + cookieToRemove.getName());
227 | }
228 | editor.apply();
229 | }
230 |
231 | @Override
232 | public synchronized List getURIs() {
233 | return new ArrayList(allCookies.keySet());
234 | }
235 |
236 | @Override
237 | public synchronized boolean remove(URI uri, HttpCookie cookie) {
238 | Set targetCookies = allCookies.get(uri);
239 | boolean cookieRemoved = targetCookies != null && targetCookies
240 | .remove(cookie);
241 | if (cookieRemoved) {
242 | removeFromPersistence(uri, cookie);
243 | }
244 | return cookieRemoved;
245 |
246 | }
247 |
248 | private void removeFromPersistence(URI uri, HttpCookie cookieToRemove) {
249 | SharedPreferences.Editor editor = sharedPreferences.edit();
250 | editor.remove(uri.toString() + SP_KEY_DELIMITER
251 | + cookieToRemove.getName());
252 | editor.apply();
253 | }
254 |
255 | @Override
256 | public synchronized boolean removeAll() {
257 | allCookies.clear();
258 | removeAllFromPersistence();
259 | return true;
260 | }
261 |
262 | private void removeAllFromPersistence() {
263 | sharedPreferences.edit().clear().apply();
264 | }
265 |
266 | }
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/http/impl/SerializableHttpCookie.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.http.impl;/*
2 | * Copyright (c) 2011 James Smith
3 | * Copyright (c) 2015 Fran Montiel
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | import android.util.Log;
19 |
20 | import java.io.ByteArrayInputStream;
21 | import java.io.ByteArrayOutputStream;
22 | import java.io.IOException;
23 | import java.io.ObjectInputStream;
24 | import java.io.ObjectOutputStream;
25 | import java.io.Serializable;
26 | import java.lang.reflect.Field;
27 | import java.net.HttpCookie;
28 |
29 | /**
30 | * Based on the code from this stackoverflow answer http://stackoverflow.com/a/25462286/980387 by janoliver
31 | * Modifications in the structure of the class and addition of serialization of httpOnly attribute
32 | */
33 |
34 | public class SerializableHttpCookie implements Serializable {
35 | private static final String TAG = SerializableHttpCookie.class
36 | .getSimpleName();
37 |
38 | private static final long serialVersionUID = 6374381323722046732L;
39 |
40 | private transient HttpCookie cookie;
41 |
42 | // Workaround httpOnly: The httpOnly attribute is not accessible so when we
43 | // serialize and deserialize the cookie it not preserve the same value. We
44 | // need to access it using reflection
45 | private Field fieldHttpOnly;
46 |
47 | public SerializableHttpCookie() {
48 | }
49 |
50 | public String encode(HttpCookie cookie) {
51 | this.cookie = cookie;
52 |
53 | ByteArrayOutputStream os = new ByteArrayOutputStream();
54 | try {
55 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
56 | outputStream.writeObject(this);
57 | } catch (IOException e) {
58 | Log.d(TAG, "IOException in encodeCookie", e);
59 | return null;
60 | }
61 |
62 | return byteArrayToHexString(os.toByteArray());
63 | }
64 |
65 | public HttpCookie decode(String encodedCookie) {
66 | byte[] bytes = hexStringToByteArray(encodedCookie);
67 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
68 | bytes);
69 | HttpCookie cookie = null;
70 | try {
71 | ObjectInputStream objectInputStream = new ObjectInputStream(
72 | byteArrayInputStream);
73 | cookie = ((SerializableHttpCookie) objectInputStream.readObject()).cookie;
74 | } catch (IOException e) {
75 | Log.d(TAG, "IOException in decodeCookie", e);
76 | } catch (ClassNotFoundException e) {
77 | Log.d(TAG, "ClassNotFoundException in decodeCookie", e);
78 | }
79 |
80 | return cookie;
81 | }
82 |
83 | // Workaround httpOnly (getter)
84 | private boolean getHttpOnly() {
85 | try {
86 | initFieldHttpOnly();
87 | return (boolean) fieldHttpOnly.get(cookie);
88 | } catch (Exception e) {
89 | // NoSuchFieldException || IllegalAccessException ||
90 | // IllegalArgumentException
91 | Log.w(TAG, e);
92 | }
93 | return false;
94 | }
95 |
96 | // Workaround httpOnly (setter)
97 | private void setHttpOnly(boolean httpOnly) {
98 | try {
99 | initFieldHttpOnly();
100 | fieldHttpOnly.set(cookie, httpOnly);
101 | } catch (Exception e) {
102 | // NoSuchFieldException || IllegalAccessException ||
103 | // IllegalArgumentException
104 | Log.w(TAG, e);
105 | }
106 | }
107 |
108 | private void initFieldHttpOnly() throws NoSuchFieldException {
109 | fieldHttpOnly = cookie.getClass().getDeclaredField("httpOnly");
110 | fieldHttpOnly.setAccessible(true);
111 | }
112 |
113 | private void writeObject(ObjectOutputStream out) throws IOException {
114 | out.writeObject(cookie.getName());
115 | out.writeObject(cookie.getValue());
116 | out.writeObject(cookie.getComment());
117 | out.writeObject(cookie.getCommentURL());
118 | out.writeObject(cookie.getDomain());
119 | out.writeLong(cookie.getMaxAge());
120 | out.writeObject(cookie.getPath());
121 | out.writeObject(cookie.getPortlist());
122 | out.writeInt(cookie.getVersion());
123 | out.writeBoolean(cookie.getSecure());
124 | out.writeBoolean(cookie.getDiscard());
125 | out.writeBoolean(getHttpOnly());
126 | }
127 |
128 | private void readObject(ObjectInputStream in) throws IOException,
129 | ClassNotFoundException {
130 | String name = (String) in.readObject();
131 | String value = (String) in.readObject();
132 | cookie = new HttpCookie(name, value);
133 | cookie.setComment((String) in.readObject());
134 | cookie.setCommentURL((String) in.readObject());
135 | cookie.setDomain((String) in.readObject());
136 | cookie.setMaxAge(in.readLong());
137 | cookie.setPath((String) in.readObject());
138 | cookie.setPortlist((String) in.readObject());
139 | cookie.setVersion(in.readInt());
140 | cookie.setSecure(in.readBoolean());
141 | cookie.setDiscard(in.readBoolean());
142 | setHttpOnly(in.readBoolean());
143 | }
144 |
145 | /**
146 | * Using some super basic byte array <-> hex conversions so we don't
147 | * have to rely on any large Base64 libraries. Can be overridden if you
148 | * like!
149 | *
150 | * @param bytes byte array to be converted
151 | * @return string containing hex values
152 | */
153 | private String byteArrayToHexString(byte[] bytes) {
154 | StringBuilder sb = new StringBuilder(bytes.length * 2);
155 | for (byte element : bytes) {
156 | int v = element & 0xff;
157 | if (v < 16) {
158 | sb.append('0');
159 | }
160 | sb.append(Integer.toHexString(v));
161 | }
162 | return sb.toString();
163 | }
164 |
165 | /**
166 | * Converts hex values from strings to byte array
167 | *
168 | * @param hexString string of hex-encoded values
169 | * @return decoded byte array
170 | */
171 | private byte[] hexStringToByteArray(String hexString) {
172 | int len = hexString.length();
173 | byte[] data = new byte[len / 2];
174 | for (int i = 0; i < len; i += 2) {
175 | data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
176 | .digit(hexString.charAt(i + 1), 16));
177 | }
178 | return data;
179 | }
180 | }
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/ReadImageTask.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.image;
2 |
3 | import android.content.Context;
4 | import android.widget.ImageView;
5 |
6 | import cn.hadcn.davinci.R;
7 | import cn.hadcn.davinci.image.base.ImageEntity;
8 | import cn.hadcn.davinci.image.base.ImageLoader;
9 | import cn.hadcn.davinci.image.base.Util;
10 | import cn.hadcn.davinci.log.VinciLog;
11 | import pl.droidsonroids.gif.GifDrawable;
12 |
13 | /**
14 | * read image from any where
15 | * Created by 90Chris on 2016/5/5.
16 | */
17 | class ReadImageTask {
18 | private final int DEFAULT_IMAGE_LOADING = R.drawable.image_loading;
19 | private final int DEFAULT_IMAGE_ERROR = R.drawable.image_load_error;
20 |
21 | private ImageView mImageView;
22 | private String mImageUrl;
23 | private int mLoadingImage = DEFAULT_IMAGE_LOADING;
24 | private int mErrorImage = DEFAULT_IMAGE_ERROR;
25 | private VinciImageLoader.ImageCache mImageCache;
26 | private ImageLoader mImageLoader;
27 | private Context mContext;
28 | private int mMaxSize;
29 | private int mKeyMode;
30 |
31 | ReadImageTask(Context context, VinciImageLoader.ImageCache imageCache, ImageLoader imageLoader, String imageUrl) {
32 | mImageUrl = imageUrl;
33 | mImageCache = imageCache;
34 | mImageLoader = imageLoader;
35 | mContext = context;
36 | }
37 |
38 | final void execute(String requestBody) {
39 | if ( mImageUrl == null || mImageUrl.isEmpty() || Util.generateKey(mImageUrl).isEmpty() ) {
40 | mImageView.setImageDrawable(mContext.getResources().getDrawable(mErrorImage));
41 | return;
42 | }
43 | String rawKey = mImageUrl;
44 | if ( mKeyMode != 0 && mMaxSize != 0 ) rawKey += mMaxSize;
45 | ImageEntity entity = mImageCache.getBitmap(Util.generateKey(rawKey));
46 |
47 | if ( entity != null ) {
48 | VinciLog.d("Load image from cache, key = " + Util.generateKey(rawKey));
49 |
50 | // if it's gif, show as gif
51 | if ( entity.isGif() ) {
52 | try {
53 | GifDrawable gifDrawable = new GifDrawable(entity.getBytes());
54 | mImageView.setImageDrawable(gifDrawable);
55 | } catch (Throwable e) {
56 | VinciLog.w("pl.droidsonroids.gif.GifDrawable not found");
57 | }
58 | } else {
59 | mImageView.setImageBitmap(entity.getBitmap());
60 | }
61 | } else if ( mImageUrl.startsWith("http") ) {
62 | VolleyImageListener listener = new VolleyImageListener(mContext, mImageView, mImageCache);
63 | listener.setDefaultImage(mLoadingImage, mErrorImage);
64 | listener.setMaxSize(mMaxSize, mKeyMode);
65 | VinciLog.d("Load image from web, url = " + mImageUrl );
66 | mImageLoader.get(mImageUrl, requestBody, listener);
67 | } else {
68 | mImageView.setImageDrawable(mContext.getResources().getDrawable(mErrorImage));
69 | }
70 | }
71 |
72 | void setView(ImageView imageView, int image_loading, int image_error) {
73 | mImageView = imageView;
74 | if ( image_loading != 0 ) mLoadingImage = image_loading;
75 | if ( image_error != 0 ) mErrorImage = image_error;
76 | }
77 |
78 | protected void setView(ImageView imageView) {
79 | mImageView = imageView;
80 | }
81 |
82 | void setSize(int size, int mode) {
83 | mMaxSize = size;
84 | mKeyMode = mode;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/VinciImageLoader.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.image;
2 |
3 | import android.content.Context;
4 | import android.widget.ImageView;
5 |
6 | import java.io.File;
7 |
8 | import cn.hadcn.davinci.image.base.ImageEntity;
9 | import cn.hadcn.davinci.image.base.ImageLoader;
10 | import cn.hadcn.davinci.image.base.Util;
11 | import cn.hadcn.davinci.image.cache.DiskLruImageCache;
12 | import cn.hadcn.davinci.log.VinciLog;
13 | import cn.hadcn.davinci.volley.RequestQueue;
14 |
15 |
16 | /**
17 | * DaImageLoader
18 | * Created by 90Chris on 2015/9/11.
19 | */
20 | public class VinciImageLoader {
21 | private String mCacheDir = null;
22 | private ImageCache mImageCache;
23 | private ImageLoader mImageLoader;
24 | private Context mContext;
25 | private int mMaxSize = 0;
26 | private int mKeyMode = 0;
27 | private final static int CACHE_SIZE = 1024 * 1024 * 50;
28 | private ReadImageTask mReadImageTask;
29 |
30 | /**
31 | * Simple cache adapter interface. If provided to the ImageLoader, it
32 | * will be used as an L1 cache before dispatch to Volley. Implementations
33 | * must not block. Implementation with an LruCache is recommended.
34 | */
35 | public interface ImageCache {
36 | ImageEntity getBitmap(String url);
37 | void putBitmap(String url, byte[] data);
38 | }
39 |
40 | public VinciImageLoader(Context context, RequestQueue requestQueue) {
41 | mCacheDir = getDiskCacheDir(context);
42 | mContext = context;
43 | mImageCache = new DiskLruImageCache(mCacheDir, CACHE_SIZE);
44 | mImageLoader = new ImageLoader(requestQueue);
45 | }
46 |
47 | private String getDiskCacheDir(Context context) {
48 | final String CACHE_DIR_NAME = "imgCache";
49 | final String cachePath = context.getCacheDir().getPath();
50 | return cachePath + File.separator + CACHE_DIR_NAME;
51 | }
52 |
53 | public String getAbsolutePath( String fileName ) {
54 | return mCacheDir + File.separator + Util.generateKey(fileName) + ".0";
55 | }
56 |
57 | public ImageEntity getImage(String name) {
58 | String key = Util.generateKey(name);
59 | if ( key.isEmpty() ) throw new RuntimeException("key is invalid");
60 |
61 | try {
62 | return mImageCache.getBitmap(key);
63 | } catch (NullPointerException e) {
64 | VinciLog.w("Get Image failed, name = " + key);
65 | return null;
66 | }
67 | }
68 |
69 | public void putImage(String name, byte[] bitmap) {
70 | String key = Util.generateKey(name);
71 | if ( key.isEmpty() ) throw new RuntimeException("key is invalid");
72 | try {
73 | mImageCache.putBitmap(key, bitmap);
74 | } catch (NullPointerException e) {
75 | VinciLog.e("Put Image failed, name cannot be null", e);
76 | }
77 | }
78 |
79 | public VinciImageLoader load(String url) {
80 | mReadImageTask = new ReadImageTask(mContext, mImageCache, mImageLoader, url);
81 | return this;
82 | }
83 |
84 | private String gBody = null;
85 | /**
86 | * set image load global body, post way
87 | * @param body post body, if null, change to get way
88 | */
89 | public void gBody(String body) {
90 | gBody = body;
91 | }
92 |
93 | private String mBody = null;
94 | /**
95 | * load image using post way, pass body part
96 | * @param body post body
97 | * @return this
98 | */
99 | public VinciImageLoader body(String body) {
100 | mBody = body;
101 | return this;
102 | }
103 |
104 | public void into(ImageView imageView) {
105 | into(imageView, 0, 0);
106 | }
107 |
108 | public void into(ImageView imageView, int loadingImage, int errorImage) {
109 | mReadImageTask.setView(imageView, loadingImage, errorImage);
110 | mReadImageTask.setSize(mMaxSize, mKeyMode);
111 | if ( mBody != null ) mReadImageTask.execute(mBody);
112 | else mReadImageTask.execute(gBody);
113 | mBody = null;
114 | }
115 |
116 | /**
117 | * limit the max size of an image will be displayed, height and width are both shorter than maxPix
118 | * @param maxPix max pixels of height and width
119 | * @return DaImageLoader instance
120 | */
121 | public VinciImageLoader resize(int maxPix) {
122 | mMaxSize = maxPix;
123 | mKeyMode = 1;
124 | return this;
125 | }
126 |
127 | /**
128 | * limit the max size of an image will be displayed, height and width are both shorter than maxPix
129 | * @param maxPix max pixels of height and width
130 | * @param mode save mode, mode = 0, do not use maxPix as image save key
131 | * @return DaImageLoader instance
132 | */
133 | public VinciImageLoader resize(int maxPix, int mode) {
134 | mMaxSize = maxPix;
135 | mKeyMode = mode;
136 | return this;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/VolleyImageListener.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.image;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.widget.ImageView;
7 |
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.nio.ByteBuffer;
11 |
12 | import cn.hadcn.davinci.image.base.ImageLoader;
13 | import cn.hadcn.davinci.image.base.Util;
14 | import cn.hadcn.davinci.log.VinciLog;
15 | import cn.hadcn.davinci.volley.VolleyError;
16 |
17 | /**
18 | *
19 | * Created by 90Chris on 2016/5/5.
20 | */
21 | class VolleyImageListener implements ImageLoader.ImageListener {
22 | private ImageView mImageView;
23 | private int mLoadingImage;
24 | private int mErrorImage;
25 | private int mMaxSize = 0;
26 | private int mKeyMode = 0;
27 | private Context mContext;
28 | private VinciImageLoader.ImageCache mImageCache;
29 |
30 | VolleyImageListener(Context context, ImageView imageView, VinciImageLoader.ImageCache imageCache) {
31 | this.mImageView = imageView;
32 | mContext = context;
33 | mImageCache = imageCache;
34 | }
35 |
36 | void setMaxSize(int size, int mode) {
37 | mMaxSize = size;
38 | mKeyMode = mode;
39 | }
40 |
41 | void setDefaultImage(int loadingImage, int errorImage) {
42 | mLoadingImage = loadingImage;
43 | mErrorImage = errorImage;
44 | }
45 |
46 | @Override
47 | public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
48 | ByteBuffer byteBuffer = response.getBitmap();
49 | if ( null != byteBuffer ) {
50 | byte[] bytes = byteBuffer.array();
51 | VinciLog.d("Image loaded success, and saved in cache, url = " + response.getRequestUrl());
52 |
53 | // if it's gif, show as gif, and save in cache
54 | if ( Util.doGif(mImageView, bytes) ) {
55 | cacheImage(response.getRequestUrl(), bytes);
56 | return;
57 | }
58 |
59 | // deal with bitmap
60 | Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
61 |
62 | int bHeight = bitmap.getHeight();
63 | int bWidth = bitmap.getWidth();
64 | if ( mMaxSize > 0 && mMaxSize != bHeight && mMaxSize != bWidth ) {
65 | //createScaledBitmap will create a new bitmap, we need release the old one
66 | Bitmap oldBitmap = bitmap;
67 | if ( bWidth > bHeight ) {
68 | int otherSize = (mMaxSize * bHeight) / bWidth;
69 | bitmap = Bitmap.createScaledBitmap(oldBitmap, mMaxSize, otherSize, false);
70 | } else {
71 | int otherSize = (mMaxSize * bWidth) / bHeight;
72 | bitmap = Bitmap.createScaledBitmap(oldBitmap, otherSize, mMaxSize, false);
73 | }
74 | oldBitmap.recycle();
75 | }
76 | mImageView.setImageBitmap(bitmap);
77 |
78 | // cache the image that was fetched.
79 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
80 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
81 | byte[] byteArray = stream.toByteArray();
82 |
83 | cacheImage(response.getRequestUrl(), byteArray);
84 | } else {
85 | mImageView.setImageDrawable(mContext.getResources().getDrawable(mLoadingImage));
86 | }
87 | }
88 |
89 | @Override
90 | public void onErrorResponse(VolleyError error) {
91 | mImageView.setImageDrawable(mContext.getResources().getDrawable(mErrorImage));
92 | }
93 |
94 | private void cacheImage(String url, byte[] data) {
95 | String rawKey = url;
96 | if ( mKeyMode != 0 && mMaxSize != 0 ) rawKey += mMaxSize;
97 |
98 | String key = Util.generateKey(rawKey);
99 | if ( key.isEmpty() ) return;
100 | mImageCache.putBitmap(key, data);
101 | }
102 | }
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/base/ByteRequest.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 cn.hadcn.davinci.image.base;
18 |
19 | import android.os.Handler;
20 | import android.os.Looper;
21 |
22 | import java.io.UnsupportedEncodingException;
23 | import java.nio.ByteBuffer;
24 |
25 | import cn.hadcn.davinci.log.VinciLog;
26 | import cn.hadcn.davinci.volley.NetworkResponse;
27 | import cn.hadcn.davinci.volley.ParseError;
28 | import cn.hadcn.davinci.volley.Request;
29 | import cn.hadcn.davinci.volley.Response;
30 | import cn.hadcn.davinci.volley.toolbox.HttpHeaderParser;
31 |
32 | /**
33 | * A canned request for getting an image at a given URL and calling
34 | * back with a decoded Bitmap.
35 | */
36 | public class ByteRequest extends Request {
37 | protected static final String PROTOCOL_CHARSET = "utf-8";
38 |
39 | private final Response.Listener mListener;
40 | private Response.ProgressListener mProgressListener;
41 |
42 | private Handler mHandler = new Handler(Looper.getMainLooper());
43 |
44 | /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
45 | private static final Object sDecodeLock = new Object();
46 |
47 | private final String mRequestBody;
48 |
49 |
50 | public ByteRequest(int method, String url, String requestBody, Response.Listener listener, Response.ErrorListener errorListener) {
51 | super(method, url, errorListener);
52 |
53 | mListener = listener;
54 | mRequestBody = requestBody;
55 | }
56 |
57 | public ByteRequest(int method, String url, String requestBody, Response.Listener listener, Response.ErrorListener errorListener, Response.ProgressListener progressListener) {
58 | this(method, url, requestBody, listener, errorListener);
59 | mProgressListener = progressListener;
60 | }
61 |
62 | @Override
63 | public Priority getPriority() {
64 | return Priority.LOW;
65 | }
66 |
67 | @Override
68 | public byte[] getBody() {
69 | try {
70 | return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
71 | } catch (UnsupportedEncodingException uee) {
72 | VinciLog.e("Unsupported Encoding while trying to get the bytes of %s using %s",
73 | mRequestBody, PROTOCOL_CHARSET);
74 | return null;
75 | }
76 | }
77 |
78 | @Override
79 | protected Response parseNetworkResponse(NetworkResponse response) {
80 | // Serialize all decode on a global lock to reduce concurrent heap usage.
81 | synchronized (sDecodeLock) {
82 | try {
83 | byte[] data = response.data;
84 | ByteBuffer byteBuffer = ByteBuffer.wrap(data);
85 | if (byteBuffer == null) {
86 | return Response.error(new ParseError(response));
87 | } else {
88 | return Response.success(byteBuffer, HttpHeaderParser.parseCacheHeaders(response));
89 | }
90 | } catch (OutOfMemoryError e) {
91 | VinciLog.e("Caught OOM for %d byte image, url=%s", e, response.data.length, getUrl());
92 | return Response.error(new ParseError(e));
93 | }
94 | }
95 | }
96 |
97 | @Override
98 | protected void deliverResponse(ByteBuffer response) {
99 | mListener.onResponse(response);
100 | }
101 |
102 | @Override
103 | public void progressUpdate(final int progress) {
104 | if ( mProgressListener == null ) return;
105 | mHandler.post(new Runnable() {
106 | @Override
107 | public void run() {
108 | mProgressListener.onProgressUpdate(progress);
109 | }
110 | });
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/base/ImageEntity.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.image.base;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | /**
6 | *
7 | * Created by 90Chris on 2016/7/20.
8 | */
9 | public class ImageEntity {
10 | private Bitmap bitmap;
11 | private byte[] bytes;
12 | private boolean isGif;
13 | private int size;
14 |
15 | public ImageEntity(byte[] bytes) {
16 | this.bytes = bytes;
17 | this.isGif = true;
18 | this.size = bytes.length;
19 | }
20 |
21 | public ImageEntity(Bitmap bitmap) {
22 | this.bitmap = bitmap;
23 | this.isGif = false;
24 | this.size = bitmap.getRowBytes();
25 | }
26 |
27 | public Bitmap getBitmap() {
28 | return bitmap;
29 | }
30 |
31 | public byte[] getBytes() {
32 | return bytes;
33 | }
34 |
35 | public boolean isGif() {
36 | return isGif;
37 | }
38 |
39 | public int getSize() {
40 | return size;
41 | }
42 |
43 | @Override
44 | protected void finalize() throws Throwable {
45 | super.finalize();
46 | bitmap.recycle();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/base/Util.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 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 cn.hadcn.davinci.image.base;
18 |
19 | import android.widget.ImageView;
20 |
21 | import java.io.Closeable;
22 | import java.io.File;
23 | import java.io.IOException;
24 | import java.io.Reader;
25 | import java.io.StringWriter;
26 | import java.nio.charset.Charset;
27 | import java.util.regex.Matcher;
28 | import java.util.regex.Pattern;
29 |
30 | import cn.hadcn.davinci.log.VinciLog;
31 | import pl.droidsonroids.gif.GifDrawable;
32 |
33 | public final class Util {
34 | public static final Charset US_ASCII = Charset.forName("US-ASCII");
35 | public static final Charset UTF_8 = Charset.forName("UTF-8");
36 |
37 | private Util() {
38 | }
39 |
40 | public static String readFully(Reader reader) throws IOException {
41 | try {
42 | StringWriter writer = new StringWriter();
43 | char[] buffer = new char[1024];
44 | int count;
45 | while ((count = reader.read(buffer)) != -1) {
46 | writer.write(buffer, 0, count);
47 | }
48 | return writer.toString();
49 | } finally {
50 | reader.close();
51 | }
52 | }
53 |
54 | /**
55 | * Deletes the contents of {@code dir}. Throws an IOException if any file
56 | * could not be deleted, or if {@code dir} is not a readable directory.
57 | */
58 | public static void deleteContents(File dir) throws IOException {
59 | File[] files = dir.listFiles();
60 | if (files == null) {
61 | throw new IOException("not a readable directory: " + dir);
62 | }
63 | for (File file : files) {
64 | if (file.isDirectory()) {
65 | deleteContents(file);
66 | }
67 | if (!file.delete()) {
68 | throw new IOException("failed to delete file: " + file);
69 | }
70 | }
71 | }
72 |
73 | public static void closeQuietly(/*Auto*/Closeable closeable) {
74 | if (closeable != null) {
75 | try {
76 | closeable.close();
77 | } catch (RuntimeException rethrown) {
78 | throw rethrown;
79 | } catch (Exception ignored) {
80 | }
81 | }
82 | }
83 |
84 | /**
85 | * Creates a unique cache key based on a url value
86 | * because file name in linux is limited
87 | * @param uri
88 | * uri to be used in key creation
89 | * @return
90 | * cache key value
91 | */
92 | public static String generateKey(String uri) {
93 | String regEx = "[^a-zA-Z0-9_-]";
94 | Pattern p = Pattern.compile(regEx);
95 | Matcher m = p.matcher(uri);
96 | String key = m.replaceAll("").trim();
97 | int length = key.length();
98 | if ( length <= 120 ) { //limited by DisLruCache
99 | return key;
100 | } else {
101 | return key.substring(0, 120);
102 | }
103 | }
104 |
105 | public static boolean doGif(ImageView imageView, byte[] data) {
106 | if ( isGif( data ) ) {
107 | try {
108 | GifDrawable gifDrawable = new GifDrawable(data);
109 | imageView.setImageDrawable(gifDrawable);
110 | return true;
111 | } catch (Throwable e) {
112 | VinciLog.w("pl.droidsonroids.gif.GifDrawable not found");
113 | }
114 | }
115 | return false;
116 | }
117 |
118 | public static boolean isGif(byte[] data) {
119 | return data[0] == 'G' && data[1] == 'I' && data[2] == 'F';
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/cache/DiskLruImageCache.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.image.cache;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 |
6 | import java.io.BufferedInputStream;
7 | import java.io.BufferedOutputStream;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.io.OutputStream;
12 |
13 | import cn.hadcn.davinci.image.VinciImageLoader;
14 | import cn.hadcn.davinci.image.base.ImageEntity;
15 | import cn.hadcn.davinci.image.base.Util;
16 | import cn.hadcn.davinci.log.VinciLog;
17 |
18 |
19 | /**
20 | * Implementation of DiskLruCache by Jake Wharton
21 | * modified by 90Chris
22 | */
23 | public class DiskLruImageCache implements VinciImageLoader.ImageCache {
24 | private DiskLruCache mDiskCache;
25 | private LruImageCache mMemoryCache;
26 | private static int IO_BUFFER_SIZE = 8 * 1024;
27 | private static final int APP_VERSION = 1;
28 | private static final int VALUE_COUNT = 1;
29 | private static final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 8);
30 |
31 | public DiskLruImageCache( String cachePath, int diskCacheSize ) {
32 | try {
33 | final File diskCacheDir = new File(cachePath);
34 | mMemoryCache = new LruImageCache(maxMemory);
35 | mDiskCache = DiskLruCache.open( diskCacheDir, APP_VERSION, VALUE_COUNT, diskCacheSize );
36 | } catch (IOException e) {
37 | e.printStackTrace();
38 | }
39 | }
40 |
41 | private void writeBitmapToFile(byte[] data, DiskLruCache.Editor editor ) throws IOException {
42 | OutputStream out = null;
43 | try {
44 | out = new BufferedOutputStream( editor.newOutputStream(0), IO_BUFFER_SIZE );
45 | out.write(data);
46 | } finally {
47 | if ( out != null ) {
48 | out.close();
49 | }
50 | }
51 | }
52 |
53 | @Override
54 | public void putBitmap( String key, byte[] data ) {
55 | // save to memory cache first
56 | saveToMemory(key, data);
57 |
58 | // save to disk cache
59 | DiskLruCache.Editor editor = null;
60 | try {
61 | editor = mDiskCache.edit( key );
62 | if ( editor == null ) {
63 | return;
64 | }
65 | writeBitmapToFile( data, editor);
66 | mDiskCache.flush();
67 | editor.commit();
68 | } catch (IOException e) {
69 | VinciLog.e("Image put on disk cache failed, key = " + key, e);
70 | try {
71 | if ( editor != null ) {
72 | editor.abort();
73 | }
74 | } catch (IOException ignored) {
75 | }
76 | }
77 | }
78 |
79 | @Override
80 | public ImageEntity getBitmap(String key) {
81 | ImageEntity imageEntity = mMemoryCache.getMemCache(key);
82 |
83 | if ( imageEntity != null ) {
84 | return imageEntity;
85 | }
86 |
87 | DiskLruCache.Snapshot snapshot = null;
88 | try {
89 | snapshot = mDiskCache.get( key );
90 | if ( snapshot == null ) {
91 | return null;
92 | }
93 | final InputStream in = snapshot.getInputStream(0);
94 | if ( in != null ) {
95 | final BufferedInputStream buffIn = new BufferedInputStream(in, IO_BUFFER_SIZE);
96 | int size = buffIn.available();
97 | byte[] bytes = new byte[size];
98 | if ( buffIn.read(bytes) == -1) return null;
99 |
100 | imageEntity = saveToMemory(key, bytes);
101 | }
102 | } catch ( IOException e ) {
103 | e.printStackTrace();
104 | } finally {
105 | if ( snapshot != null ) {
106 | snapshot.close();
107 | }
108 | }
109 |
110 | return imageEntity;
111 | }
112 |
113 | private ImageEntity saveToMemory(String key, byte[] data) {
114 | ImageEntity entity;
115 |
116 | if ( Util.isGif(data) ) {
117 | entity = new ImageEntity(data);
118 | } else {
119 | Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);
120 | entity = new ImageEntity(image);
121 | }
122 |
123 | mMemoryCache.putMemCache(key, entity);
124 | return entity;
125 | }
126 |
127 | public void clearCache() {
128 | try {
129 | mDiskCache.delete();
130 | } catch ( IOException e ) {
131 | e.printStackTrace();
132 | }
133 | }
134 |
135 | public File getCacheFolder() {
136 | return mDiskCache.getDirectory();
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/cache/LruCache.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 | package cn.hadcn.davinci.image.cache;
17 | import java.util.LinkedHashMap;
18 | import java.util.Map;
19 | // STOPSHIP replace "Honeycomb MR1" with numbered release 2x below
20 | /**
21 | * Static library version of {@link android.util.LruCache}. Used to write apps
22 | * that run on platforms prior to Android Honeycomb MR1. When running on
23 | * Honeycomb MR1 or above, this implementation is still used; it does not try to
24 | * switch to the framework's implementation. See the framework SDK
25 | * documentation for a class overview.
26 | */
27 | public class LruCache {
28 | private final LinkedHashMap map;
29 | /** Size of this cache in units. Not necessarily the number of elements. */
30 | private int size;
31 | private int maxSize;
32 | private int putCount;
33 | private int createCount;
34 | private int evictionCount;
35 | private int hitCount;
36 | private int missCount;
37 | /**
38 | * @param maxSize for caches that do not override {@link #sizeOf}, this is
39 | * the maximum number of entries in the cache. For all other caches,
40 | * this is the maximum sum of the sizes of the entries in this cache.
41 | */
42 | public LruCache(int maxSize) {
43 | if (maxSize <= 0) {
44 | throw new IllegalArgumentException("maxSize <= 0");
45 | }
46 | this.maxSize = maxSize;
47 | this.map = new LinkedHashMap(0, 0.75f, true);
48 | }
49 | /**
50 | * Returns the value for {@code key} if it exists in the cache or can be
51 | * created by {@code #create}. If a value was returned, it is moved to the
52 | * head of the queue. This returns null if a value is not cached and cannot
53 | * be created.
54 | */
55 | public synchronized final V get(K key) {
56 | if (key == null) {
57 | throw new NullPointerException("key == null");
58 | }
59 | V result = map.get(key);
60 | if (result != null) {
61 | hitCount++;
62 | return result;
63 | }
64 | missCount++;
65 | // TODO: release the lock while calling this potentially slow user code
66 | result = create(key);
67 | if (result != null) {
68 | createCount++;
69 | size += safeSizeOf(key, result);
70 | map.put(key, result);
71 | trimToSize(maxSize);
72 | }
73 | return result;
74 | }
75 | /**
76 | * Caches {@code value} for {@code key}. The value is moved to the head of
77 | * the queue.
78 | *
79 | * @return the previous value mapped by {@code key}. Although that entry is
80 | * no longer cached, it has not been passed to {@link #entryEvicted}.
81 | */
82 | public synchronized final V put(K key, V value) {
83 | if (key == null || value == null) {
84 | throw new NullPointerException("key == null || value == null");
85 | }
86 | putCount++;
87 | size += safeSizeOf(key, value);
88 | V previous = map.put(key, value);
89 | if (previous != null) {
90 | size -= safeSizeOf(key, previous);
91 | }
92 | trimToSize(maxSize);
93 | return previous;
94 | }
95 | private void trimToSize(int maxSize) {
96 | while (size > maxSize && !map.isEmpty()) {
97 | Map.Entry toEvict = map.entrySet().iterator().next();
98 | if (toEvict == null) {
99 | break; // map is empty; if size is not 0 then throw an error below
100 | }
101 | K key = toEvict.getKey();
102 | V value = toEvict.getValue();
103 | map.remove(key);
104 | size -= safeSizeOf(key, value);
105 | evictionCount++;
106 | // TODO: release the lock while calling this potentially slow user code
107 | entryEvicted(key, value);
108 | }
109 | if (size < 0 || (map.isEmpty() && size != 0)) {
110 | throw new IllegalStateException(getClass().getName()
111 | + ".sizeOf() is reporting inconsistent results!");
112 | }
113 | }
114 | /**
115 | * Removes the entry for {@code key} if it exists.
116 | *
117 | * @return the previous value mapped by {@code key}. Although that entry is
118 | * no longer cached, it has not been passed to {@link #entryEvicted}.
119 | */
120 | public synchronized final V remove(K key) {
121 | if (key == null) {
122 | throw new NullPointerException("key == null");
123 | }
124 | V previous = map.remove(key);
125 | if (previous != null) {
126 | size -= safeSizeOf(key, previous);
127 | }
128 | return previous;
129 | }
130 | /**
131 | * Called for entries that have reached the tail of the least recently used
132 | * queue and are be removed. The default implementation does nothing.
133 | */
134 | protected void entryEvicted(K key, V value) {}
135 | /**
136 | * Called after a cache miss to compute a value for the corresponding key.
137 | * Returns the computed value or null if no value can be computed. The
138 | * default implementation returns null.
139 | */
140 | protected V create(K key) {
141 | return null;
142 | }
143 | private int safeSizeOf(K key, V value) {
144 | int result = sizeOf(key, value);
145 | if (result < 0) {
146 | throw new IllegalStateException("Negative size: " + key + "=" + value);
147 | }
148 | return result;
149 | }
150 | /**
151 | * Returns the size of the entry for {@code key} and {@code value} in
152 | * user-defined units. The default implementation returns 1 so that size
153 | * is the number of entries and max size is the maximum number of entries.
154 | *
155 | *
An entry's size must not change while it is in the cache.
156 | */
157 | protected int sizeOf(K key, V value) {
158 | return 1;
159 | }
160 | /**
161 | * Clear the cache, calling {@link #entryEvicted} on each removed entry.
162 | */
163 | public synchronized final void evictAll() {
164 | trimToSize(-1); // -1 will evict 0-sized elements
165 | }
166 | /**
167 | * For caches that do not override {@link #sizeOf}, this returns the number
168 | * of entries in the cache. For all other caches, this returns the sum of
169 | * the sizes of the entries in this cache.
170 | */
171 | public synchronized final int size() {
172 | return size;
173 | }
174 | /**
175 | * For caches that do not override {@link #sizeOf}, this returns the maximum
176 | * number of entries in the cache. For all other caches, this returns the
177 | * maximum sum of the sizes of the entries in this cache.
178 | */
179 | public synchronized final int maxSize() {
180 | return maxSize;
181 | }
182 | /**
183 | * Returns the number of times {@link #get} returned a value.
184 | */
185 | public synchronized final int hitCount() {
186 | return hitCount;
187 | }
188 | /**
189 | * Returns the number of times {@link #get} returned null or required a new
190 | * value to be created.
191 | */
192 | public synchronized final int missCount() {
193 | return missCount;
194 | }
195 | /**
196 | * Returns the number of times {@link #create(Object)} returned a value.
197 | */
198 | public synchronized final int createCount() {
199 | return createCount;
200 | }
201 | /**
202 | * Returns the number of times {@link #put} was called.
203 | */
204 | public synchronized final int putCount() {
205 | return putCount;
206 | }
207 | /**
208 | * Returns the number of values that have been evicted.
209 | */
210 | public synchronized final int evictionCount() {
211 | return evictionCount;
212 | }
213 | /**
214 | * Returns a copy of the current contents of the cache, ordered from least
215 | * recently accessed to most recently accessed.
216 | */
217 | public synchronized final Map snapshot() {
218 | return new LinkedHashMap(map);
219 | }
220 | @Override public synchronized final String toString() {
221 | int accesses = hitCount + missCount;
222 | int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
223 | return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
224 | maxSize, hitCount, missCount, hitPercent);
225 | }
226 | }
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/cache/LruImageCache.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.image.cache;
2 |
3 | import cn.hadcn.davinci.image.base.ImageEntity;
4 |
5 |
6 | /**
7 | * memory cache
8 | * @author 90Chris
9 | */
10 | class LruImageCache extends LruCache {
11 |
12 | LruImageCache(int maxSize) {
13 | super(maxSize);
14 | }
15 |
16 | @Override
17 | protected int sizeOf(String key, ImageEntity value) {
18 | return value.getSize() + 1024; //多加1024 bytes,包入整个对象大小,宁多勿少
19 | }
20 |
21 | ImageEntity getMemCache(String url) {
22 | return get(url);
23 | }
24 |
25 | void putMemCache(String url, ImageEntity bitmap) {
26 | put(url, bitmap);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/image/cache/StrictLineReader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.hadcn.davinci.image.cache;
18 |
19 | import java.io.ByteArrayOutputStream;
20 | import java.io.Closeable;
21 | import java.io.EOFException;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 | import java.io.UnsupportedEncodingException;
25 | import java.nio.charset.Charset;
26 |
27 | import cn.hadcn.davinci.image.base.Util;
28 |
29 | /**
30 | * Buffers input from an {@link InputStream} for reading lines.
31 | *
32 | *
This class is used for buffered reading of lines. For purposes of this class, a line ends
33 | * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
34 | * line at end of input is invalid and will be ignored, the caller may use {@code
35 | * hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
36 | *
37 | *
This class is intended for reading input that strictly consists of lines, such as line-based
38 | * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
39 | * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
40 | * end-of-input reporting and a more restrictive definition of a line.
41 | *
42 | *
This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
43 | * and 10, respectively, and the representation of no other character contains these values.
44 | * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
45 | * The default charset is US_ASCII.
46 | */
47 | class StrictLineReader implements Closeable {
48 | private static final byte CR = (byte) '\r';
49 | private static final byte LF = (byte) '\n';
50 |
51 | private final InputStream in;
52 | private final Charset charset;
53 |
54 | /*
55 | * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
56 | * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
57 | * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
58 | * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
59 | */
60 | private byte[] buf;
61 | private int pos;
62 | private int end;
63 |
64 | /**
65 | * Constructs a new {@code LineReader} with the specified charset and the default capacity.
66 | *
67 | * @param in the {@code InputStream} to read data from.
68 | * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
69 | * supported.
70 | * @throws NullPointerException if {@code in} or {@code charset} is null.
71 | * @throws IllegalArgumentException if the specified charset is not supported.
72 | */
73 | public StrictLineReader(InputStream in, Charset charset) {
74 | this(in, 8192, charset);
75 | }
76 |
77 | /**
78 | * Constructs a new {@code LineReader} with the specified capacity and charset.
79 | *
80 | * @param in the {@code InputStream} to read data from.
81 | * @param capacity the capacity of the buffer.
82 | * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
83 | * supported.
84 | * @throws NullPointerException if {@code in} or {@code charset} is null.
85 | * @throws IllegalArgumentException if {@code capacity} is negative or zero
86 | * or the specified charset is not supported.
87 | */
88 | public StrictLineReader(InputStream in, int capacity, Charset charset) {
89 | if (in == null || charset == null) {
90 | throw new NullPointerException();
91 | }
92 | if (capacity < 0) {
93 | throw new IllegalArgumentException("capacity <= 0");
94 | }
95 | if (!(charset.equals(Util.US_ASCII))) {
96 | throw new IllegalArgumentException("Unsupported encoding");
97 | }
98 |
99 | this.in = in;
100 | this.charset = charset;
101 | buf = new byte[capacity];
102 | }
103 |
104 | /**
105 | * Closes the reader by closing the underlying {@code InputStream} and
106 | * marking this reader as closed.
107 | *
108 | * @throws IOException for errors when closing the underlying {@code InputStream}.
109 | */
110 | public void close() throws IOException {
111 | synchronized (in) {
112 | if (buf != null) {
113 | buf = null;
114 | in.close();
115 | }
116 | }
117 | }
118 |
119 | /**
120 | * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
121 | * this end of line marker is not included in the result.
122 | *
123 | * @return the next line from the input.
124 | * @throws IOException for underlying {@code InputStream} errors.
125 | * @throws EOFException for the end of source stream.
126 | */
127 | public String readLine() throws IOException {
128 | synchronized (in) {
129 | if (buf == null) {
130 | throw new IOException("LineReader is closed");
131 | }
132 |
133 | // Read more data if we are at the end of the buffered data.
134 | // Though it's an error to read after an exception, we will let {@code fillBuf()}
135 | // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
136 | if (pos >= end) {
137 | fillBuf();
138 | }
139 | // Try to find LF in the buffered data and return the line if successful.
140 | for (int i = pos; i != end; ++i) {
141 | if (buf[i] == LF) {
142 | int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
143 | String res = new String(buf, pos, lineEnd - pos, charset.name());
144 | pos = i + 1;
145 | return res;
146 | }
147 | }
148 |
149 | // Let's anticipate up to 80 characters on top of those already read.
150 | ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
151 | @Override
152 | public String toString() {
153 | int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
154 | try {
155 | return new String(buf, 0, length, charset.name());
156 | } catch (UnsupportedEncodingException e) {
157 | throw new AssertionError(e); // Since we control the charset this will never happen.
158 | }
159 | }
160 | };
161 |
162 | while (true) {
163 | out.write(buf, pos, end - pos);
164 | // Mark unterminated line in case fillBuf throws EOFException or IOException.
165 | end = -1;
166 | fillBuf();
167 | // Try to find LF in the buffered data and return the line if successful.
168 | for (int i = pos; i != end; ++i) {
169 | if (buf[i] == LF) {
170 | if (i != pos) {
171 | out.write(buf, pos, i - pos);
172 | }
173 | pos = i + 1;
174 | return out.toString();
175 | }
176 | }
177 | }
178 | }
179 | }
180 |
181 | public boolean hasUnterminatedLine() {
182 | return end == -1;
183 | }
184 |
185 | /**
186 | * Reads new input data into the buffer. Call only with pos == end or end == -1,
187 | * depending on the desired outcome if the function throws.
188 | */
189 | private void fillBuf() throws IOException {
190 | int result = in.read(buf, 0, buf.length);
191 | if (result == -1) {
192 | throw new EOFException();
193 | }
194 | pos = 0;
195 | end = result;
196 | }
197 | }
198 |
199 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/other/OnVinciDownloadListener.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.other;
2 |
3 | /**
4 | *
5 | * Created by 90Chris on 2016/7/6.
6 | */
7 | public interface OnVinciDownloadListener {
8 | void onVinciDownloadSuccess();
9 | void onVinciDownloadFailed(String reason);
10 | void onVinciDownloadProgress(int progress);
11 | }
12 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/other/OnVinciUploadListener.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.other;
2 |
3 | import org.json.JSONObject;
4 |
5 | /**
6 | *
7 | * Created by 90Chris on 2016/7/6.
8 | */
9 | public interface OnVinciUploadListener {
10 | void onVinciUploadSuccess(JSONObject response);
11 | void onVinciUploadFailed(String reason);
12 | }
13 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/other/impl/VinciDownload.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.other.impl;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 | import java.nio.ByteBuffer;
6 |
7 | import cn.hadcn.davinci.image.base.ByteRequest;
8 | import cn.hadcn.davinci.log.VinciLog;
9 | import cn.hadcn.davinci.other.OnVinciDownloadListener;
10 | import cn.hadcn.davinci.volley.DefaultRetryPolicy;
11 | import cn.hadcn.davinci.volley.Request;
12 | import cn.hadcn.davinci.volley.RequestQueue;
13 | import cn.hadcn.davinci.volley.Response;
14 | import cn.hadcn.davinci.volley.VolleyError;
15 |
16 | /**
17 | * VinciDownload
18 | * Created by 90Chris on 2016/7/5.
19 | */
20 | public class VinciDownload {
21 | private RequestQueue mRequestQueue;
22 | private String mBody;
23 |
24 | public VinciDownload(RequestQueue mRequestQueue) {
25 | this.mRequestQueue = mRequestQueue;
26 | }
27 |
28 | public VinciDownload body(String body) {
29 | mBody = body;
30 | return this;
31 | }
32 |
33 | public void download(String url, final OutputStream out, final OnVinciDownloadListener listener) {
34 | ByteRequest request = new ByteRequest(Request.Method.POST, url, mBody, new Response.Listener() {
35 | @Override
36 | public void onResponse(ByteBuffer response) {
37 | try {
38 | out.write(response.array());
39 | listener.onVinciDownloadSuccess();
40 | } catch (IOException e) {
41 | VinciLog.e("write out error", e);
42 | listener.onVinciDownloadFailed("write file failed");
43 | }
44 | }
45 | }, new Response.ErrorListener() {
46 | @Override
47 | public void onErrorResponse(VolleyError error) {
48 | listener.onVinciDownloadFailed("net failed");
49 | }
50 | }, new Response.ProgressListener() {
51 | @Override
52 | public void onProgressUpdate(int progress) {
53 | listener.onVinciDownloadProgress(progress);
54 | }
55 | });
56 | request.setRetryPolicy(new DefaultRetryPolicy(4 * DefaultRetryPolicy.DEFAULT_TIMEOUT_MS,
57 | DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
58 | mRequestQueue.add(request);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/other/impl/VinciUpload.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.other.impl;
2 |
3 |
4 | import org.json.JSONObject;
5 |
6 | import java.io.File;
7 |
8 | import cn.hadcn.davinci.log.VinciLog;
9 | import cn.hadcn.davinci.other.OnVinciUploadListener;
10 | import cn.hadcn.davinci.other.request.UploadRequest;
11 | import cn.hadcn.davinci.volley.DefaultRetryPolicy;
12 | import cn.hadcn.davinci.volley.RequestQueue;
13 | import cn.hadcn.davinci.volley.Response;
14 | import cn.hadcn.davinci.volley.VolleyError;
15 |
16 | /**
17 | * DaVinciUpload
18 | * Created by 90Chris on 2015/11/11.
19 | */
20 | public class VinciUpload {
21 | private RequestQueue mRequestQueue;
22 | private String mFilePartName = null;
23 | private String extraName;
24 | private JSONObject extraObject;
25 |
26 | public VinciUpload(RequestQueue mRequestQueue) {
27 | this.mRequestQueue = mRequestQueue;
28 | }
29 |
30 | /**
31 | * name of file in form data
32 | * @param name default is 'file'
33 | */
34 | public VinciUpload name(String name) {
35 | mFilePartName = name;
36 | return this;
37 | }
38 |
39 | public VinciUpload extra(String name, JSONObject object) {
40 | extraName = name;
41 | extraObject = object;
42 | return this;
43 | }
44 |
45 | /**
46 | * upload file to server
47 | * @param uploadUrl file server url
48 | * @param filePath local file path
49 | * @param listener listener of uploading
50 | */
51 | public void upload(String uploadUrl, String filePath, final OnVinciUploadListener listener) {
52 | File file = new File(filePath);
53 | if ( !file.exists() ) {
54 | VinciLog.w("Upload file is not exists");
55 | listener.onVinciUploadFailed("Upload file is not exists");
56 | return;
57 | }
58 | UploadRequest uploadRequest = new UploadRequest(uploadUrl,
59 |
60 | new Response.Listener() {
61 | @Override
62 | public void onResponse(JSONObject response) {
63 | VinciLog.d("upload response:" + (response == null ? null : response.toString()));
64 | listener.onVinciUploadSuccess(response);
65 | }
66 | },
67 | new Response.ErrorListener() {
68 | @Override
69 | public void onErrorResponse(VolleyError error) {
70 | String reason = null;
71 | if ( error.networkResponse != null ) {
72 | reason = "status code : " + String.valueOf(error.networkResponse.statusCode) + ";";
73 | byte[] data = error.networkResponse.data;
74 | reason += ( data == null ? null : new String(data) );
75 | }
76 | VinciLog.e("http failed: " + reason);
77 | if ( listener != null ) {
78 | listener.onVinciUploadFailed(reason);
79 | }
80 | }
81 | });
82 | uploadRequest.setRetryPolicy(new DefaultRetryPolicy(4 * DefaultRetryPolicy.DEFAULT_TIMEOUT_MS,
83 | DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
84 |
85 | if ( extraName != null && extraObject != null ) {
86 | uploadRequest.addExtra(extraName, extraObject);
87 | }
88 |
89 | uploadRequest.addFile(mFilePartName, file);
90 |
91 | mRequestQueue.add(uploadRequest);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/other/request/UploadRequest.java:
--------------------------------------------------------------------------------
1 | package cn.hadcn.davinci.other.request;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.io.OutputStream;
7 | import java.io.UnsupportedEncodingException;
8 | import java.nio.charset.Charset;
9 | import java.util.Collections;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 |
14 | import org.apache.http.entity.mime.HttpMultipartMode;
15 | import org.apache.http.entity.mime.MultipartEntity;
16 | import org.apache.http.entity.mime.content.AbstractContentBody;
17 | import org.apache.http.entity.mime.content.FileBody;
18 | import org.json.JSONException;
19 | import org.json.JSONObject;
20 |
21 | import cn.hadcn.davinci.log.VinciLog;
22 | import cn.hadcn.davinci.volley.AuthFailureError;
23 | import cn.hadcn.davinci.volley.NetworkResponse;
24 | import cn.hadcn.davinci.volley.ParseError;
25 | import cn.hadcn.davinci.volley.Request;
26 | import cn.hadcn.davinci.volley.Response;
27 | import cn.hadcn.davinci.volley.Response.*;
28 | import cn.hadcn.davinci.volley.toolbox.HttpHeaderParser;
29 |
30 |
31 | public class UploadRequest extends Request {
32 | private static final String FILE_PART_NAME = "file";
33 | private static final String BOUNDARY = "----WebKitFormBoundarysU2wZJMAVKl3MW6Q";
34 | private static final String CHARSET = "utf-8";
35 | private MultipartEntity mEntity;
36 | private final Response.Listener mListener;
37 |
38 | public UploadRequest(String url, Listener listener, ErrorListener errorListener) {
39 | super(Method.POST, url, errorListener);
40 |
41 | mListener = listener;
42 |
43 | mEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, BOUNDARY, Charset.forName(CHARSET));
44 | }
45 |
46 | public void addFile(String filePartName, File file) {
47 | if ( null == filePartName ) {
48 | filePartName = FILE_PART_NAME;
49 | }
50 | mEntity.addPart(filePartName, new FileBody(file, "application/octet-stream", CHARSET));
51 | }
52 |
53 | public void addExtra(String name, final JSONObject object) {
54 | AbstractContentBody body = new AbstractContentBody("application/json") {
55 | @Override
56 | public String getFilename() {
57 | return null;
58 | }
59 |
60 | @Override
61 | public void writeTo(OutputStream outputStream) throws IOException {
62 | outputStream.write(object.toString().getBytes());
63 | }
64 |
65 | @Override
66 | public String getCharset() {
67 | return CHARSET;
68 | }
69 |
70 | @Override
71 | public String getTransferEncoding() {
72 | return "binary";
73 | }
74 |
75 | @Override
76 | public long getContentLength() {
77 | return object.length();
78 | }
79 | };
80 | VinciLog.d(name + ":" + object.toString());
81 | mEntity.addPart(name, body);
82 | }
83 |
84 | @Override
85 | public Map getHeaders() throws AuthFailureError {
86 | Map headers = super.getHeaders();
87 |
88 | if (headers == null
89 | || headers.equals(Collections.emptyMap())) {
90 | headers = new HashMap<>();
91 | }
92 |
93 | headers.put("Accept", "application/json");
94 |
95 | return headers;
96 | }
97 |
98 | @Override
99 | public Priority getPriority() {
100 | return Priority.NORMAL;
101 | }
102 |
103 | @Override
104 | public String getBodyContentType()
105 | {
106 | return "multipart/form-data; boundary=" + BOUNDARY;
107 | }
108 |
109 | @Override
110 | public byte[] getBody() throws AuthFailureError
111 | {
112 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
113 | try
114 | {
115 | mEntity.writeTo(bos);
116 | }
117 | catch (IOException e)
118 | {
119 | VinciLog.e("IOException writing to ByteArrayOutputStream bos, building the multipart request.", e);
120 | }
121 | return bos.toByteArray();
122 | }
123 |
124 | @Override
125 | protected Response parseNetworkResponse(NetworkResponse response)
126 | {
127 | try {
128 | String jsonString = new String(response.data,
129 | HttpHeaderParser.parseCharset(response.headers, CHARSET));
130 | return Response.success(new JSONObject(jsonString),
131 | HttpHeaderParser.parseCacheHeaders(response));
132 | } catch (UnsupportedEncodingException e) {
133 | return Response.error(new ParseError(e));
134 | } catch (JSONException je) {
135 | return Response.error(new ParseError(je));
136 | }
137 | }
138 |
139 | @Override
140 | protected void deliverResponse(JSONObject response)
141 | {
142 | if (mListener != null) {
143 | mListener.onResponse(response);
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 | /** The last modified date for the requested object. */
78 | public long lastModified;
79 |
80 | /** TTL for this record. */
81 | public long ttl;
82 |
83 | /** Soft TTL for this record. */
84 | public long softTtl;
85 |
86 | /** Immutable response headers as received from server; must be non-null. */
87 | public Map responseHeaders = Collections.emptyMap();
88 |
89 | /** True if the entry is expired. */
90 | public boolean isExpired() {
91 | return this.ttl < System.currentTimeMillis();
92 | }
93 |
94 | /** True if a refresh is needed from the original data source. */
95 | public boolean refreshNeeded() {
96 | return this.softTtl < System.currentTimeMillis();
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley;
18 |
19 | import android.os.Process;
20 |
21 | import java.util.concurrent.BlockingQueue;
22 |
23 | import cn.hadcn.davinci.log.VinciLog;
24 |
25 | /**
26 | * Provides a thread for performing cache triage on a queue of requests.
27 | *
28 | * Requests added to the specified cache queue are resolved from cache.
29 | * Any deliverable response is posted back to the caller via a
30 | * {@link ResponseDelivery}. Cache misses and responses that require
31 | * refresh are enqueued on the specified network queue for processing
32 | * by a {@link NetworkDispatcher}.
33 | */
34 | public class CacheDispatcher extends Thread {
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 | VinciLog.d("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 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/volley/ClientError.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 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 cn.hadcn.davinci.volley;
18 |
19 | /**
20 | * Indicates that the server responded with an error response indicating that the client has erred.
21 | *
22 | * For backwards compatibility, extends ServerError which used to be thrown for all server errors,
23 | * including 4xx error codes indicating a client error.
24 | */
25 | @SuppressWarnings("serial")
26 | public class ClientError extends ServerError {
27 | public ClientError(NetworkResponse networkResponse) {
28 | super(networkResponse);
29 | }
30 |
31 | public ClientError() {
32 | super();
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 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 | * Returns the backoff multiplier for the policy.
81 | */
82 | public float getBackoffMultiplier() {
83 | return mBackoffMultiplier;
84 | }
85 |
86 | /**
87 | * Prepares for the next retry by applying a backoff to the timeout.
88 | * @param error The error code of the last attempt.
89 | */
90 | @Override
91 | public void retry(VolleyError error) throws VolleyError {
92 | mCurrentRetryCount++;
93 | mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
94 | if (!hasAttemptRemaining()) {
95 | throw error;
96 | }
97 | }
98 |
99 | /**
100 | * Returns true if this policy has attempts remaining, false otherwise.
101 | */
102 | protected boolean hasAttemptRemaining() {
103 | return mCurrentRetryCount <= mMaxNumRetries;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 | NetworkResponse performRequest(Request> request) throws VolleyError;
30 | }
31 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley;
18 |
19 | import android.annotation.TargetApi;
20 | import android.net.TrafficStats;
21 | import android.os.Build;
22 | import android.os.Process;
23 | import android.os.SystemClock;
24 |
25 | import java.util.concurrent.BlockingQueue;
26 |
27 | import cn.hadcn.davinci.log.VinciLog;
28 |
29 | /**
30 | * Provides a thread for performing network dispatch from a queue of requests.
31 | *
32 | * Requests added to the specified queue are processed from the network via a
33 | * specified {@link Network} interface. Responses are committed to cache, if
34 | * eligible, using a specified {@link Cache} interface. Valid responses and
35 | * errors are posted back to the caller via a {@link ResponseDelivery}.
36 | */
37 | public class NetworkDispatcher extends Thread {
38 | /** The queue of requests to service. */
39 | private final BlockingQueue> mQueue;
40 | /** The network interface for processing requests. */
41 | private final Network mNetwork;
42 | /** The cache to write to. */
43 | private final Cache mCache;
44 | /** For posting responses and errors. */
45 | private final ResponseDelivery mDelivery;
46 | /** Used for telling us to die. */
47 | private volatile boolean mQuit = false;
48 |
49 | /**
50 | * Creates a new network dispatcher thread. You must call {@link #start()}
51 | * in order to begin processing.
52 | *
53 | * @param queue Queue of incoming requests for triage
54 | * @param network Network interface to use for performing requests
55 | * @param cache Cache interface to use for writing responses to cache
56 | * @param delivery Delivery interface to use for posting responses
57 | */
58 | public NetworkDispatcher(BlockingQueue> queue,
59 | Network network, Cache cache,
60 | ResponseDelivery delivery) {
61 | mQueue = queue;
62 | mNetwork = network;
63 | mCache = cache;
64 | mDelivery = delivery;
65 | }
66 |
67 | /**
68 | * Forces this dispatcher to quit immediately. If any requests are still in
69 | * the queue, they are not guaranteed to be processed.
70 | */
71 | public void quit() {
72 | mQuit = true;
73 | interrupt();
74 | }
75 |
76 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
77 | private void addTrafficStatsTag(Request> request) {
78 | // Tag the request (if API >= 14)
79 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
80 | TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
81 | }
82 | }
83 |
84 | @Override
85 | public void run() {
86 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
87 | while (true) {
88 | long startTimeMs = SystemClock.elapsedRealtime();
89 | Request> request;
90 | try {
91 | // Take a request from the queue.
92 | request = mQueue.take();
93 | } catch (InterruptedException e) {
94 | // We may have been interrupted because it was time to quit.
95 | if (mQuit) {
96 | return;
97 | }
98 | continue;
99 | }
100 | try {
101 | request.addMarker("network-queue-take");
102 |
103 | // If the request was cancelled already, do not perform the
104 | // network request.
105 | if (request.isCanceled()) {
106 | request.finish("network-discard-cancelled");
107 | continue;
108 | }
109 |
110 | addTrafficStatsTag(request);
111 |
112 | // Perform the network request.
113 | NetworkResponse networkResponse = mNetwork.performRequest(request);
114 | request.addMarker("network-http-complete");
115 |
116 | // If the server returned 304 AND we delivered a response already,
117 | // we're done -- don't deliver a second identical response.
118 | if (networkResponse.notModified && request.hasHadResponseDelivered()) {
119 | request.finish("not-modified");
120 | continue;
121 | }
122 |
123 | // Parse the response here on the worker thread.
124 | Response> response = request.parseNetworkResponse(networkResponse);
125 | request.addMarker("network-parse-complete");
126 |
127 | // Write to cache if applicable.
128 | // TODO: Only update cache metadata instead of entire record for 304s.
129 | if (request.shouldCache() && response.cacheEntry != null) {
130 | mCache.put(request.getCacheKey(), response.cacheEntry);
131 | request.addMarker("network-cache-written");
132 | }
133 |
134 | // Post the response back.
135 | request.markDelivered();
136 | mDelivery.postResponse(request, response);
137 | } catch (VolleyError volleyError) {
138 | volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
139 | parseAndDeliverNetworkError(request, volleyError);
140 | } catch (Exception e) {
141 | VinciLog.e("Unhandled exception %s", e);
142 | VolleyError volleyError = new VolleyError(e);
143 | volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
144 | mDelivery.postError(request, volleyError);
145 | }
146 | }
147 | }
148 |
149 | private void parseAndDeliverNetworkError(Request> request, VolleyError error) {
150 | error = request.parseNetworkError(error);
151 | mDelivery.postError(request, error);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 | * @param networkTimeMs Round-trip network time to receive network response
35 | */
36 | public NetworkResponse(int statusCode, byte[] data, Map headers,
37 | boolean notModified, long networkTimeMs) {
38 | this.statusCode = statusCode;
39 | this.data = data;
40 | this.headers = headers;
41 | this.notModified = notModified;
42 | this.networkTimeMs = networkTimeMs;
43 | }
44 |
45 | public NetworkResponse(int statusCode, byte[] data, Map headers,
46 | boolean notModified) {
47 | this(statusCode, data, headers, notModified, 0);
48 | }
49 |
50 | public NetworkResponse(byte[] data) {
51 | this(HttpStatus.SC_OK, data, Collections.emptyMap(), false, 0);
52 | }
53 |
54 | public NetworkResponse(byte[] data, Map headers) {
55 | this(HttpStatus.SC_OK, data, headers, false, 0);
56 | }
57 |
58 | /** The HTTP status code. */
59 | public final int statusCode;
60 |
61 | /** Raw data from this response. */
62 | public final byte[] data;
63 |
64 | /** Response headers. */
65 | public final Map headers;
66 |
67 | /** True if the server returned a 304 (Not Modified). */
68 | public final boolean notModified;
69 |
70 | /** Network roundtrip time in milliseconds. */
71 | public final long networkTimeMs;
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 | 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 | void onErrorResponse(VolleyError error);
39 | }
40 |
41 | public interface ProgressListener{
42 | void onProgressUpdate(int progress);
43 | }
44 |
45 | /** Returns a successful response containing the parsed result. */
46 | public static Response success(T result, Cache.Entry cacheEntry) {
47 | return new Response(result, cacheEntry);
48 | }
49 |
50 | /**
51 | * Returns a failed response containing the given error code and an optional
52 | * localized message displayed to the user.
53 | */
54 | public static Response error(VolleyError error) {
55 | return new Response(error);
56 | }
57 |
58 | /** Parsed response, or null in the case of error. */
59 | public final T result;
60 |
61 | /** Cache metadata for this response, or null in the case of error. */
62 | public final Cache.Entry cacheEntry;
63 |
64 | /** Detailed error information if errorCode != OK. */
65 | public final VolleyError error;
66 |
67 | /** True if this response was a soft-expired one and a second one MAY be coming. */
68 | public boolean intermediate = false;
69 |
70 | /**
71 | * Returns whether this response is considered successful.
72 | */
73 | public boolean isSuccess() {
74 | return error == null;
75 | }
76 |
77 |
78 | private Response(T result, Cache.Entry cacheEntry) {
79 | this.result = result;
80 | this.cacheEntry = cacheEntry;
81 | this.error = null;
82 | }
83 |
84 | private Response(VolleyError error) {
85 | this.result = null;
86 | this.cacheEntry = null;
87 | this.error = error;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley;
18 |
19 | public interface ResponseDelivery {
20 | /**
21 | * Parses a response from the network or cache and delivers it.
22 | */
23 | 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 | void postResponse(Request> request, Response> response, Runnable runnable);
30 |
31 | /**
32 | * Posts an error for the given request.
33 | */
34 | void postError(Request> request, VolleyError error);
35 | }
36 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 | int getCurrentTimeout();
28 |
29 | /**
30 | * Returns the current retry count (used for logging).
31 | */
32 | 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 | void retry(VolleyError error) throws VolleyError;
41 | }
42 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley;
18 |
19 | /**
20 | * Indicates that the server 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 |
33 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley;
18 |
19 | /**
20 | * Indicates that the connection or the socket timed out.
21 | */
22 | @SuppressWarnings("serial")
23 | public class TimeoutError extends VolleyError { }
24 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 | private long networkTimeMs;
26 |
27 | public VolleyError() {
28 | networkResponse = null;
29 | }
30 |
31 | public VolleyError(NetworkResponse response) {
32 | networkResponse = response;
33 | }
34 |
35 | public VolleyError(String exceptionMessage) {
36 | super(exceptionMessage);
37 | networkResponse = null;
38 | }
39 |
40 | public VolleyError(String exceptionMessage, Throwable reason) {
41 | super(exceptionMessage, reason);
42 | networkResponse = null;
43 | }
44 |
45 | public VolleyError(Throwable cause) {
46 | super(cause);
47 | networkResponse = null;
48 | }
49 |
50 | /* package */ void setNetworkTimeMs(long networkTimeMs) {
51 | this.networkTimeMs = networkTimeMs;
52 | }
53 |
54 | public long getNetworkTimeMs() {
55 | return networkTimeMs;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley.toolbox;
18 |
19 | import android.accounts.Account;
20 | import android.accounts.AccountManager;
21 | import android.accounts.AccountManagerFuture;
22 | import android.content.Context;
23 | import android.content.Intent;
24 | import android.os.Bundle;
25 |
26 | import cn.hadcn.davinci.volley.AuthFailureError;
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 AccountManager mAccountManager;
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 | this(AccountManager.get(context), account, authTokenType, notifyAuthFailure);
58 | }
59 |
60 | // Visible for testing. Allows injection of a mock AccountManager.
61 | AndroidAuthenticator(AccountManager accountManager, Account account,
62 | String authTokenType, boolean notifyAuthFailure) {
63 | mAccountManager = accountManager;
64 | mAccount = account;
65 | mAuthTokenType = authTokenType;
66 | mNotifyAuthFailure = notifyAuthFailure;
67 | }
68 |
69 | /**
70 | * Returns the Account being used by this authenticator.
71 | */
72 | public Account getAccount() {
73 | return mAccount;
74 | }
75 |
76 | /**
77 | * Returns the Auth Token Type used by this authenticator.
78 | */
79 | public String getAuthTokenType() {
80 | return mAuthTokenType;
81 | }
82 |
83 | // TODO: Figure out what to do about notifyAuthFailure
84 | @SuppressWarnings("deprecation")
85 | @Override
86 | public String getAuthToken() throws AuthFailureError {
87 | AccountManagerFuture future = mAccountManager.getAuthToken(mAccount,
88 | mAuthTokenType, mNotifyAuthFailure, null, null);
89 | Bundle result;
90 | try {
91 | result = future.getResult();
92 | } catch (Exception e) {
93 | throw new AuthFailureError("Error while retrieving auth token", e);
94 | }
95 | String authToken = null;
96 | if (future.isDone() && !future.isCancelled()) {
97 | if (result.containsKey(AccountManager.KEY_INTENT)) {
98 | Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
99 | throw new AuthFailureError(intent);
100 | }
101 | authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
102 | }
103 | if (authToken == null) {
104 | throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType);
105 | }
106 |
107 | return authToken;
108 | }
109 |
110 | @Override
111 | public void invalidateAuthToken(String authToken) {
112 | mAccountManager.invalidateAuthToken(mAccount.type, authToken);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley.toolbox;
18 |
19 | import cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.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 |
--------------------------------------------------------------------------------
/davinci/src/main/java/cn/hadcn/davinci/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 cn.hadcn.davinci.volley.toolbox;
18 |
19 | import android.os.Handler;
20 | import android.os.Looper;
21 |
22 | import cn.hadcn.davinci.volley.Cache;
23 | import cn.hadcn.davinci.volley.NetworkResponse;
24 | import cn.hadcn.davinci.volley.Request;
25 | import cn.hadcn.davinci.volley.Response;
26 |
27 | /**
28 | * A synthetic request used for clearing the cache.
29 | */
30 | public class ClearCacheRequest extends Request