call, Throwable t) {
36 |
37 | }
38 | });
39 | }
40 |
41 | private void initView() {
42 | listView = (ListView) findViewById(R.id.list);
43 | adapter = new ImageListAdapter();
44 | listView.setAdapter(adapter);
45 | }
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import com.facebook.drawee.backends.pipeline.Fresco;
7 |
8 | /**
9 | * Created by xing on 2016/7/19.
10 | */
11 | public class MyApplication extends Application{
12 |
13 | private static MyApplication appcontext = null;
14 | public void onCreate() {
15 | super.onCreate();
16 | appcontext = this;
17 | Fresco.initialize(appcontext, ConfigConstants.getImagePipelineConfig(appcontext));
18 | }
19 |
20 | // 单例模式
21 | public static MyApplication getInstance() {
22 | return appcontext;
23 | }
24 | public static Context getContext() {
25 | return appcontext;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/MyOkHttpImagePipelineConfigFactory.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2;
2 |
3 | import android.content.Context;
4 |
5 | import com.facebook.imagepipeline.core.ImagePipelineConfig;
6 |
7 | import okhttp3.OkHttpClient;
8 |
9 | /**
10 | * Created by admin on 2016/1/8.
11 | *
12 | * This source code is licensed under the BSD-style license found in the
13 | * LICENSE file in the root directory of this source tree. An additional grant
14 | * of patent rights can be found in the PATENTS file in the same directory.
15 | */
16 | public class MyOkHttpImagePipelineConfigFactory {
17 |
18 | public static ImagePipelineConfig.Builder newBuilder(Context context, OkHttpClient okHttpClient) {
19 | return ImagePipelineConfig.newBuilder(context).setNetworkFetcher(new MyOkHttpNetworkFetcher(okHttpClient));
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/MyOkHttpNetworkFetcher.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2;
2 |
3 | import android.net.Uri;
4 | import android.os.Looper;
5 | import android.os.SystemClock;
6 | import android.util.Log;
7 |
8 | import com.facebook.imagepipeline.image.EncodedImage;
9 | import com.facebook.imagepipeline.producers.BaseNetworkFetcher;
10 | import com.facebook.imagepipeline.producers.BaseProducerContextCallbacks;
11 | import com.facebook.imagepipeline.producers.Consumer;
12 | import com.facebook.imagepipeline.producers.FetchState;
13 | import com.facebook.imagepipeline.producers.ProducerContext;
14 |
15 | import java.io.IOException;
16 | import java.util.HashMap;
17 | import java.util.Map;
18 | import java.util.concurrent.Executor;
19 |
20 | import okhttp3.CacheControl;
21 | import okhttp3.Call;
22 | import okhttp3.OkHttpClient;
23 | import okhttp3.Request;
24 | import okhttp3.Response;
25 | import okhttp3.ResponseBody;
26 |
27 | /**
28 | * This source code is licensed under the BSD-style license found in the
29 | * LICENSE file in the root directory of this source tree. An additional grant
30 | * of patent rights can be found in the PATENTS file in the same directory.
31 | *
32 | * Created by admin on 2016/1/8.
33 | *
34 | * Network fetcher that uses OkHttp as a backend.
35 | */
36 | public class MyOkHttpNetworkFetcher extends BaseNetworkFetcher {
37 |
38 | public static class MyOkHttpNetworkFetchState extends FetchState {
39 |
40 | public long submitTime;
41 | public long responseTime;
42 | public long fetchCompleteTime;
43 |
44 | public MyOkHttpNetworkFetchState(Consumer consumer, ProducerContext context) {
45 | super(consumer, context);
46 | }
47 |
48 | }
49 |
50 | private static final String TAG = "MyOkHttpNetworkFetcher";
51 |
52 | private static final String QUEUE_TIME = "queue_time";
53 | private static final String FETCH_TIME = "fetch_time";
54 | private static final String TOTAL_TIME = "total_time";
55 | private static final String IMAGE_SIZE = "image_size";
56 |
57 | private final OkHttpClient mOkHttpClient;
58 |
59 | private Executor mCancellationExecutor;
60 |
61 | public MyOkHttpNetworkFetcher(OkHttpClient okHttpClient) {
62 | mOkHttpClient = okHttpClient;
63 | mCancellationExecutor = okHttpClient.dispatcher().executorService();
64 | }
65 |
66 | @Override
67 | public MyOkHttpNetworkFetchState createFetchState(Consumer consumer, ProducerContext producerContext) {
68 | return new MyOkHttpNetworkFetchState(consumer, producerContext);
69 | }
70 |
71 | @Override
72 | public void fetch(final MyOkHttpNetworkFetchState fetchState, final Callback callback) {
73 | fetchState.submitTime = SystemClock.elapsedRealtime();
74 |
75 | final Uri uri = fetchState.getUri();
76 |
77 | final Request request = new Request.Builder()
78 | .cacheControl(new CacheControl.Builder().noStore().build())
79 | .url(uri.toString())
80 | .get()
81 | .build();
82 |
83 | final Call call = mOkHttpClient.newCall(request);
84 |
85 | fetchState.getContext().addCallbacks(new BaseProducerContextCallbacks() {
86 | @Override
87 | public void onCancellationRequested() {
88 | if (Looper.myLooper() != Looper.getMainLooper()) {
89 | call.cancel();
90 | } else {
91 | mCancellationExecutor.execute(new Runnable() {
92 | @Override
93 | public void run() {
94 | call.cancel();
95 | }
96 | });
97 | }
98 | }
99 | });
100 |
101 | call.enqueue(new okhttp3.Callback() {
102 | @Override
103 | public void onFailure(Call call, IOException e) {
104 | handleException(call, e, callback);
105 | }
106 |
107 | @Override
108 | public void onResponse(Call call, Response response) throws IOException {
109 | fetchState.responseTime = SystemClock.elapsedRealtime();
110 | final ResponseBody body = response.body();
111 | try {
112 | long contentLength = body.contentLength();
113 | if (contentLength < 0) {
114 | contentLength = 0;
115 | }
116 | callback.onResponse(body.byteStream(), (int) contentLength);
117 | } catch (Exception e) {
118 | handleException(call, e, callback);
119 | } finally {
120 | try {
121 | body.close();
122 | } catch (Exception e) {
123 | Log.w(TAG, "Exception when closing response body", e);
124 | }
125 | }
126 | }
127 | });
128 |
129 | }
130 |
131 | @Override
132 | public void onFetchCompletion(MyOkHttpNetworkFetchState fetchState, int byteSize) {
133 | fetchState.fetchCompleteTime = SystemClock.elapsedRealtime();
134 | }
135 |
136 | @Override
137 | public Map getExtraMap(MyOkHttpNetworkFetchState fetchState, int byteSize) {
138 | Map extraMap = new HashMap<>(4);
139 | extraMap.put(QUEUE_TIME, Long.toString(fetchState.responseTime - fetchState.submitTime));
140 | extraMap.put(FETCH_TIME, Long.toString(fetchState.fetchCompleteTime - fetchState.responseTime));
141 | extraMap.put(TOTAL_TIME, Long.toString(fetchState.fetchCompleteTime - fetchState.submitTime));
142 | extraMap.put(IMAGE_SIZE, Integer.toString(byteSize));
143 | return extraMap;
144 | }
145 |
146 | /**
147 | * Handles exceptions.
148 | *
149 | *
OkHttp notifies callers of cancellations via an IOException. If IOException is caught
150 | * after request cancellation, then the exception is interpreted as successful cancellation
151 | * and onCancellation is called. Otherwise onFailure is called.
152 | */
153 | private void handleException(final Call call, final Exception e, final Callback callback) {
154 | if (call.isCanceled()) {
155 | callback.onCancellation();
156 | } else {
157 | callback.onFailure(e);
158 | }
159 | }
160 |
161 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/base/MyBaseAdapter.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2.base;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 | import android.widget.BaseAdapter;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * 设置不同个状态显示数据
12 | * @author Win7
13 | *
14 | */
15 | public class MyBaseAdapter extends BaseAdapter {
16 | @SuppressWarnings("rawtypes")
17 | protected ArrayList _data = new ArrayList();
18 |
19 | @Override
20 | public int getCount() {
21 | return getDataSize();
22 | }
23 | /**
24 | * data数据的大小
25 | * @return
26 | */
27 | public int getDataSize() {
28 | return _data.size();
29 | }
30 |
31 | @Override
32 | public Object getItem(int arg0) {
33 | if (_data.size() > arg0) {
34 | return _data.get(arg0);
35 | }
36 | return null;
37 | }
38 |
39 | @Override
40 | public long getItemId(int arg0) {
41 | return arg0;
42 | }
43 |
44 | @SuppressWarnings("rawtypes")
45 | public void setData(ArrayList data) {
46 | _data = data;
47 | notifyDataSetChanged();
48 | }
49 |
50 | @SuppressWarnings("rawtypes")
51 | public ArrayList getData() {
52 | return _data == null ? (_data = new ArrayList()) : _data;
53 | }
54 |
55 | @SuppressWarnings({ "unchecked", "rawtypes" })
56 | public void addData(List data) {
57 | if (_data == null) {
58 | _data = new ArrayList();
59 | }
60 | _data.addAll(data);
61 | notifyDataSetChanged();
62 | }
63 |
64 | @SuppressWarnings({ "unchecked", "rawtypes" })
65 | public void addItem(Object obj) {
66 | if (_data == null) {
67 | _data = new ArrayList();
68 | }
69 | _data.add(obj);
70 | notifyDataSetChanged();
71 | }
72 |
73 | @SuppressWarnings({ "rawtypes", "unchecked" })
74 | public void addItem(int pos, Object obj) {
75 | if (_data == null) {
76 | _data = new ArrayList();
77 | }
78 | _data.add(pos, obj);
79 | notifyDataSetChanged();
80 | }
81 |
82 | public void removeItem(Object obj) {
83 | _data.remove(obj);
84 | notifyDataSetChanged();
85 | }
86 | public void removeByPosition(int i) {
87 | _data.remove(i);
88 | notifyDataSetChanged();
89 | }
90 |
91 |
92 | public void clear() {
93 | _data.clear();
94 | notifyDataSetChanged();
95 | }
96 |
97 | @Override
98 | public View getView(int position, View convertView, ViewGroup parent) {
99 |
100 | return getRealView(position, convertView, parent);
101 | }
102 |
103 | protected View getRealView(int position, View convertView, ViewGroup parent) {
104 | return null;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/retrofit/ImageListBean.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2.retrofit;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by xing on 2016/7/19.
8 | */
9 | public class ImageListBean {
10 |
11 |
12 | /**
13 | * status : true
14 | * total : 752
15 | * tngou : [{"count":292,"fcount":0,"galleryclass":4,"id":766,"img":"/ext/160717/0cac4d91e152e76d9e50f700a1025fca.jpg","rcount":0,"size":6,"time":1468728796000,"title":"妩媚的裸身性感美女一丝不挂销魂床上私房照"},{"count":230,"fcount":0,"galleryclass":1,"id":765,"img":"/ext/160717/29f188fadcd57812ed305df6e4ce3a65.jpg","rcount":0,"size":8,"time":1468728652000,"title":"90后酥胸正妹可爱自拍"},{"count":186,"fcount":0,"galleryclass":4,"id":764,"img":"/ext/160717/72ce009cd73a23e30dc9c8b0cc294d76.jpg","rcount":0,"size":21,"time":1468728613000,"title":"美女幼师Evelyn艾莉浴室勇斗私房照"},{"count":312,"fcount":0,"galleryclass":7,"id":763,"img":"/ext/160714/2507ee143d6285680b9ee1a2501c3308.jpg","rcount":0,"size":9,"time":1468497460000,"title":"性感车模与奔驰激情碰撞魅力无限"},{"count":176,"fcount":0,"galleryclass":7,"id":762,"img":"/ext/160714/b36610cc6d080e569a541102de5bb2f7.jpg","rcount":0,"size":9,"time":1468497405000,"title":"蓝色妖姬车展包裙尽显魅力高清大图"},{"count":234,"fcount":0,"galleryclass":5,"id":761,"img":"/ext/160714/41a62b88753dbb148a7d065474d89191.jpg","rcount":0,"size":10,"time":1468497350000,"title":"性感嫩模潘恩思性感包裙艺术"},{"count":139,"fcount":0,"galleryclass":5,"id":760,"img":"/ext/160714/43731ba40326757b80956306573cf0d0.jpg","rcount":0,"size":6,"time":1468497264000,"title":"性感女星刘凡菲笑容明媚宛若气质公主"},{"count":103,"fcount":0,"galleryclass":5,"id":759,"img":"/ext/160714/4eabc89d447fcc1f1f62393b9760978d.jpg","rcount":0,"size":6,"time":1468497149000,"title":"百变女王张虹紧身群性感时尚写"},{"count":80,"fcount":0,"galleryclass":5,"id":758,"img":"/ext/160714/c01d95059203c6b4186b78e5794d0a40.jpg","rcount":0,"size":5,"time":1468497087000,"title":"性感美女郝泽嘉酥胸隐现魅力"},{"count":351,"fcount":0,"galleryclass":3,"id":757,"img":"/ext/160713/a2cc1eea970661a2447a2ec6a4330501.jpg","rcount":0,"size":4,"time":1468415744000,"title":"Beautyleg美女超短旗袍私房照"},{"count":118,"fcount":0,"galleryclass":5,"id":756,"img":"/ext/160713/56061edae6c4556b5dace5e3c66c8a22.jpg","rcount":0,"size":3,"time":1468415682000,"title":"气质白衣制服诱惑美女"},{"count":555,"fcount":0,"galleryclass":6,"id":755,"img":"/ext/160713/57b0437520f390f2c36914e8290cae6b.jpg","rcount":0,"size":6,"time":1468415630000,"title":"极品日本巨乳美女 浑圆光滑的大奶少妇亚洲大尺度人体艺术图片"},{"count":487,"fcount":0,"galleryclass":1,"id":754,"img":"/ext/160713/daa8900536d4d82a0cd32f397b18e16c.jpg","rcount":0,"size":11,"time":1468415580000,"title":"带眼镜的麻辣白领美女办公室职业装黑丝美腿写真"},{"count":216,"fcount":0,"galleryclass":3,"id":753,"img":"/ext/160713/676453bc8b3c1a2b32cc6e04d376bbda.jpg","rcount":0,"size":17,"time":1468415535000,"title":"简晓育vicni蓝色紧身短裙制服黑丝美腿写真"},{"count":96,"fcount":0,"galleryclass":7,"id":752,"img":"/ext/160713/0d5e649ce564dbf35a9e78c5bd6f28fc.jpg","rcount":0,"size":4,"time":1468415465000,"title":"白皙巨乳车模美女车展电眼迷人气质美胸唯美大胆写真图片"},{"count":253,"fcount":0,"galleryclass":7,"id":751,"img":"/ext/160711/455754b5e4dd37fa918ed6dfe7801077.jpg","rcount":0,"size":4,"time":1468237275000,"title":"韩国美女白衣秀大长腿性感写真"},{"count":261,"fcount":0,"galleryclass":3,"id":750,"img":"/ext/160711/bc5ede5c4f2f3a3775ba05cb829d5a29.jpg","rcount":0,"size":7,"time":1468237211000,"title":"韩雪彩色丝袜完美身段性感时尚"},{"count":644,"fcount":0,"galleryclass":1,"id":749,"img":"/ext/160711/8d0093442185844e6250a32cd7b1691d.jpg","rcount":0,"size":9,"time":1468237172000,"title":"翘臀美女开叉礼服装大胆私房"},{"count":199,"fcount":0,"galleryclass":4,"id":748,"img":"/ext/160711/41b79f4d0848d2c0ede366e3cb6c8718.jpg","rcount":0,"size":9,"time":1468237113000,"title":"极品嫩模深V爆乳性感美腿写真"},{"count":109,"fcount":0,"galleryclass":5,"id":747,"img":"/ext/160711/b5308c4afc253ab8df4c04b3e7b38538.jpg","rcount":0,"size":8,"time":1468237071000,"title":"气质美女包臀短裙性感美腿写真图片"}]
16 | */
17 |
18 | private boolean status;
19 | private int total;
20 | /**
21 | * count : 292
22 | * fcount : 0
23 | * galleryclass : 4
24 | * id : 766
25 | * img : /ext/160717/0cac4d91e152e76d9e50f700a1025fca.jpg
26 | * rcount : 0
27 | * size : 6
28 | * time : 1468728796000
29 | * title : 妩媚的裸身性感美女一丝不挂销魂床上私房照
30 | */
31 |
32 | private ArrayList tngou;
33 |
34 | public boolean isStatus() {
35 | return status;
36 | }
37 |
38 | public void setStatus(boolean status) {
39 | this.status = status;
40 | }
41 |
42 | public int getTotal() {
43 | return total;
44 | }
45 |
46 | public void setTotal(int total) {
47 | this.total = total;
48 | }
49 |
50 | public ArrayList getTngou() {
51 | return tngou;
52 | }
53 |
54 | public void setTngou(ArrayList tngou) {
55 | this.tngou = tngou;
56 | }
57 |
58 | public static class TngouBean {
59 | private int count;
60 | private int fcount;
61 | private int galleryclass;
62 | private int id;
63 | private String img;
64 | private int rcount;
65 | private int size;
66 | private long time;
67 | private String title;
68 |
69 | public int getCount() {
70 | return count;
71 | }
72 |
73 | public void setCount(int count) {
74 | this.count = count;
75 | }
76 |
77 | public int getFcount() {
78 | return fcount;
79 | }
80 |
81 | public void setFcount(int fcount) {
82 | this.fcount = fcount;
83 | }
84 |
85 | public int getGalleryclass() {
86 | return galleryclass;
87 | }
88 |
89 | public void setGalleryclass(int galleryclass) {
90 | this.galleryclass = galleryclass;
91 | }
92 |
93 | public int getId() {
94 | return id;
95 | }
96 |
97 | public void setId(int id) {
98 | this.id = id;
99 | }
100 |
101 | public String getImg() {
102 | return img;
103 | }
104 |
105 | public void setImg(String img) {
106 | this.img = img;
107 | }
108 |
109 | public int getRcount() {
110 | return rcount;
111 | }
112 |
113 | public void setRcount(int rcount) {
114 | this.rcount = rcount;
115 | }
116 |
117 | public int getSize() {
118 | return size;
119 | }
120 |
121 | public void setSize(int size) {
122 | this.size = size;
123 | }
124 |
125 | public long getTime() {
126 | return time;
127 | }
128 |
129 | public void setTime(long time) {
130 | this.time = time;
131 | }
132 |
133 | public String getTitle() {
134 | return title;
135 | }
136 |
137 | public void setTitle(String title) {
138 | this.title = title;
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/retrofit/MainFactory.java:
--------------------------------------------------------------------------------
1 |
2 | package com.lizubing.smartcacheforretrofit2.retrofit;
3 |
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 | import com.lizubing.smartcache.BasicCaching;
7 | import com.lizubing.smartcache.SmartCallFactory;
8 | import com.lizubing.smartcacheforretrofit2.MyApplication;
9 |
10 | import java.io.IOException;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import okhttp3.Interceptor;
14 | import okhttp3.OkHttpClient;
15 | import okhttp3.Request;
16 | import okhttp3.Response;
17 | import retrofit2.Retrofit;
18 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
19 | import retrofit2.converter.gson.GsonConverterFactory;
20 |
21 | /**
22 | * 无缓存get post请求
23 | * 支持Rxjava Observable
24 | */
25 | public class MainFactory {
26 | public static final String HOST = "http://www.tngou.net/";
27 |
28 | private static MeoHttp mGuDong;
29 |
30 | protected static final Object monitor = new Object();
31 |
32 | public static MeoHttp getInstance(){
33 | synchronized (monitor){
34 | if(mGuDong==null){
35 | Gson gson = new GsonBuilder()
36 | .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
37 | .create();
38 | SmartCallFactory smartFactory = new SmartCallFactory(BasicCaching.fromCtx(MyApplication.getContext()));
39 | //实现拦截器,设置请求头
40 | Interceptor interceptorImpl = new Interceptor() {
41 | @Override
42 | public Response intercept(Chain chain) throws IOException {
43 | Request request = chain.request();
44 | Request compressedRequest = request.newBuilder()
45 | .header("X-Requested-With", "XMLHttpRequest")
46 | .build();
47 | return chain.proceed(compressedRequest);
48 | }
49 | };
50 | //设置OKHttpClient
51 | OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder()
52 | .connectTimeout(2, TimeUnit.SECONDS)
53 | .writeTimeout(60, TimeUnit.SECONDS)
54 | .readTimeout(60, TimeUnit.SECONDS)
55 | .addInterceptor(interceptorImpl);//创建OKHttpClient的Builder
56 | //build OKHttpClient
57 | OkHttpClient okHttpClient = httpClientBuilder.build();
58 | Retrofit client = new Retrofit.Builder()
59 | .baseUrl(HOST)
60 | .client(okHttpClient)
61 | .addConverterFactory(GsonConverterFactory.create(gson))
62 | .addCallAdapterFactory(smartFactory)
63 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
64 | .build();
65 | mGuDong = client.create(MeoHttp.class);
66 | }
67 | return mGuDong;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lizubing/smartcacheforretrofit2/retrofit/MeoHttp.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2.retrofit;
2 |
3 |
4 | import com.lizubing.smartcache.SmartCall;
5 |
6 | import retrofit2.http.GET;
7 |
8 | public interface MeoHttp {
9 |
10 | /**
11 | * 获得图片列表
12 | */
13 | @GET("tnfs/api/list")
14 | SmartCall getImageList();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_imagelist.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizubing1992/Smartcacheforretrofit2/fa6cde08c090ce367e89dc0748a8a17701aee969/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizubing1992/Smartcacheforretrofit2/fa6cde08c090ce367e89dc0748a8a17701aee969/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizubing1992/Smartcacheforretrofit2/fa6cde08c090ce367e89dc0748a8a17701aee969/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizubing1992/Smartcacheforretrofit2/fa6cde08c090ce367e89dc0748a8a17701aee969/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizubing1992/Smartcacheforretrofit2/fa6cde08c090ce367e89dc0748a8a17701aee969/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 | Smartcacheforretrofit2
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/lizubing/smartcacheforretrofit2/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcacheforretrofit2;
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:1.5.0'
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 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizubing1992/Smartcacheforretrofit2/fa6cde08c090ce367e89dc0748a8a17701aee969/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':smartcache'
2 |
--------------------------------------------------------------------------------
/smartcache/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/smartcache/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | testCompile 'junit:junit:4.12'
24 | compile 'com.jakewharton:disklrucache:2.0.2'
25 | compile 'com.squareup.retrofit2:retrofit:2.1.0'//修改成2.0版本
26 | compile 'com.squareup.retrofit2:converter-gson:2.1.0'
27 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
28 | compile 'com.android.support:appcompat-v7:23.4.0'
29 | compile 'com.google.guava:guava:18.0'
30 | }
31 |
--------------------------------------------------------------------------------
/smartcache/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:\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 |
--------------------------------------------------------------------------------
/smartcache/src/androidTest/java/com/lizubing/smartcache/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
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 | }
--------------------------------------------------------------------------------
/smartcache/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/smartcache/src/main/java/com/lizubing/smartcache/AndroidExecutor.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import java.util.concurrent.Executor;
7 |
8 | class AndroidExecutor implements Executor {
9 | private final Handler handler = new Handler(Looper.getMainLooper());
10 |
11 | @Override
12 | public void execute(Runnable r) {
13 | handler.post(r);
14 | }
15 | }
--------------------------------------------------------------------------------
/smartcache/src/main/java/com/lizubing/smartcache/BasicCaching.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.util.LruCache;
6 |
7 | import com.google.common.hash.Hashing;
8 | import com.jakewharton.disklrucache.DiskLruCache;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.net.URL;
13 | import java.nio.charset.Charset;
14 |
15 | import okhttp3.Request;
16 | import retrofit2.Response;
17 |
18 |
19 | /**
20 | * A basic caching system that stores responses in RAM & disk
21 | * It uses {@link DiskLruCache} and {@link LruCache} to do the former.
22 | */
23 | public class BasicCaching implements CachingSystem {
24 | private DiskLruCache diskCache;
25 | private LruCache memoryCache;
26 |
27 | public BasicCaching(File diskDirectory, long maxDiskSize, int memoryEntries){
28 | try{
29 | diskCache = DiskLruCache.open(diskDirectory, 1, 1, maxDiskSize);
30 | }catch(IOException exc){
31 | Log.e("SmartCall", "", exc);
32 | diskCache = null;
33 | }
34 |
35 | memoryCache = new LruCache<>(memoryEntries);
36 | }
37 |
38 | private static final long REASONABLE_DISK_SIZE = 1024 * 1024; // 1 MB
39 | private static final int REASONABLE_MEM_ENTRIES = 50; // 50 entries
40 |
41 | /***
42 | * Constructs a BasicCaching system using settings that should work for everyone
43 | * @param context
44 | * @return
45 | */
46 | public static BasicCaching fromCtx(Context context){
47 | return new BasicCaching(
48 | new File(context.getCacheDir(), "retrofit_smartcache"),
49 | REASONABLE_DISK_SIZE,
50 | REASONABLE_MEM_ENTRIES);
51 | }
52 |
53 | @Override
54 | public void addInCache(Response response, byte[] rawResponse) {
55 | String cacheKey = urlToKey(response.raw().request().url().url());
56 | memoryCache.put(cacheKey, rawResponse);
57 |
58 | try {
59 | DiskLruCache.Editor editor = diskCache.edit(urlToKey(response.raw().request().url().url()));
60 | editor.set(0, new String(rawResponse, Charset.defaultCharset()));
61 | editor.commit();
62 | }catch(IOException exc){
63 | Log.e("SmartCall", "", exc);
64 | }
65 | }
66 |
67 | @Override
68 | public byte[] getFromCache(Request request) {
69 | String cacheKey = urlToKey(request.url().url());
70 | byte[] memoryResponse = (byte[]) memoryCache.get(cacheKey);
71 | if(memoryResponse != null){
72 | Log.d("SmartCall", "Memory hit!");
73 | return memoryResponse;
74 | }
75 |
76 | try {
77 | DiskLruCache.Snapshot cacheSnapshot = diskCache.get(cacheKey);
78 | if(cacheSnapshot != null){
79 | Log.d("SmartCall", "Disk hit!");
80 | return cacheSnapshot.getString(0).getBytes();
81 | }else{
82 | return null;
83 | }
84 | }catch(IOException exc){
85 | return null;
86 | }
87 | }
88 |
89 | private String urlToKey(URL url){
90 | return Hashing.sha1().hashString(url.toString(), Charset.defaultCharset()).toString();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/smartcache/src/main/java/com/lizubing/smartcache/CachingSystem.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
2 |
3 |
4 | import okhttp3.Request;
5 | import retrofit2.Response;
6 |
7 | public interface CachingSystem {
8 | void addInCache(Response response, byte[] rawResponse);
9 | byte[] getFromCache(Request request);
10 | }
11 |
--------------------------------------------------------------------------------
/smartcache/src/main/java/com/lizubing/smartcache/SmartCall.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
2 |
3 |
4 | import java.io.IOException;
5 | import java.lang.reflect.Type;
6 |
7 | import okhttp3.Request;
8 | import retrofit2.Callback;
9 | import retrofit2.Response;
10 |
11 |
12 | public interface SmartCall{
13 | /**
14 | * Asynchronously send the request and notify {@code callback} of its response or if an error
15 | * occurred talking to the server, creating the request, or processing the response.
16 | */
17 | void enqueue(Callback callback);
18 |
19 | /**
20 | * Returns a runtime {@link Type} that corresponds to the response type specified in your
21 | * service.
22 | */
23 | Type responseType();
24 |
25 | /**
26 | * Builds a new {@link Request} that is identical to the one that will be dispatched
27 | * when the {@link SmartCall} is executed/enqueued.
28 | */
29 | Request buildRequest();
30 |
31 | /**
32 | * Create a new, identical call to this one which can be enqueued or executed even if this call
33 | * has already been.
34 | */
35 | SmartCall clone();
36 |
37 | /* ================================================================ */
38 | /* Now it's time for the blocking methods - which can't be smart :(
39 | /* ================================================================ */
40 |
41 | /**
42 | * Synchronously send the request and return its response. NOTE: No smart caching allowed!
43 | *
44 | * @throws IOException if a problem occurred talking to the server.
45 | * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
46 | * or decoding the response.
47 | */
48 | Response execute() throws IOException;
49 |
50 | /**
51 | * Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not
52 | * yet been executed it never will be.
53 | */
54 | void cancel();
55 | }
--------------------------------------------------------------------------------
/smartcache/src/main/java/com/lizubing/smartcache/SmartCallFactory.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
2 |
3 | import com.google.common.reflect.TypeToken;
4 |
5 | import java.io.IOException;
6 | import java.lang.annotation.Annotation;
7 | import java.lang.reflect.Field;
8 | import java.lang.reflect.Method;
9 | import java.lang.reflect.ParameterizedType;
10 | import java.lang.reflect.Type;
11 | import java.util.concurrent.Executor;
12 |
13 | import okhttp3.Request;
14 | import retrofit2.Call;
15 | import retrofit2.CallAdapter;
16 | import retrofit2.Callback;
17 | import retrofit2.Response;
18 | import retrofit2.Retrofit;
19 |
20 |
21 | public class SmartCallFactory extends CallAdapter.Factory {
22 | private final CachingSystem cachingSystem;
23 | private final Executor asyncExecutor;
24 |
25 | public SmartCallFactory(CachingSystem cachingSystem){
26 | this.cachingSystem = cachingSystem;
27 | this.asyncExecutor = new AndroidExecutor();
28 | }
29 |
30 | public SmartCallFactory(CachingSystem cachingSystem, Executor executor){
31 | this.cachingSystem = cachingSystem;
32 | this.asyncExecutor = executor;
33 | }
34 |
35 | @Override
36 | public CallAdapter> get(final Type returnType, final Annotation[] annotations,
37 | final Retrofit retrofit) {
38 |
39 | TypeToken> token = TypeToken.of(returnType);
40 | if (token.getRawType() != SmartCall.class) {
41 | return null;
42 | }
43 |
44 | if (!(returnType instanceof ParameterizedType)) {
45 | throw new IllegalStateException(
46 | "SmartCall must have generic type (e.g., SmartCall)");
47 | }
48 |
49 | final Type responseType = ((ParameterizedType) returnType).getActualTypeArguments()[0];
50 | final Executor callbackExecutor = asyncExecutor;
51 |
52 | return new CallAdapter>() {
53 | @Override
54 | public Type responseType() {
55 | return responseType;
56 | }
57 |
58 | @Override
59 | public SmartCall adapt(Call call) {
60 | return new SmartCallImpl<>(callbackExecutor, call, responseType(), annotations,
61 | retrofit, cachingSystem);
62 | }
63 | };
64 | }
65 |
66 | static class SmartCallImpl implements SmartCall{
67 | private final Executor callbackExecutor;
68 | private final Call baseCall;
69 | private final Type responseType;
70 | private final Annotation[] annotations;
71 | private final Retrofit retrofit;
72 | private final CachingSystem cachingSystem;
73 | private final Request request;
74 |
75 | public SmartCallImpl(Executor callbackExecutor, Call baseCall, Type responseType,
76 | Annotation[] annotations, Retrofit retrofit, CachingSystem cachingSystem){
77 | this.callbackExecutor = callbackExecutor;
78 | this.baseCall = baseCall;
79 | this.responseType = responseType;
80 | this.annotations = annotations;
81 | this.retrofit = retrofit;
82 | this.cachingSystem = cachingSystem;
83 |
84 | // This one is a hack but should create a valid Response (which can later be cloned)
85 | this.request = buildRequestFromCall();
86 | }
87 |
88 | /***
89 | * Inspects an OkHttp-powered Call and builds a Request
90 | * * @return A valid Request (that contains query parameters, right method and endpoint)
91 | */
92 | private Request buildRequestFromCall(){
93 | try {
94 | Field argsField = baseCall.getClass().getDeclaredField("args");
95 | argsField.setAccessible(true);
96 | Object[] args = (Object[]) argsField.get(baseCall);
97 | //retrofit2.0更改了字段(1.0+)requestFactory-->(2.0+)serviceMethod
98 | Field serviceMethodField = baseCall.getClass().getDeclaredField("serviceMethod");
99 | serviceMethodField.setAccessible(true);
100 | Object requestFactory = serviceMethodField.get(baseCall);
101 | //retrofit2.0更改了方法(1.0+)create-->(2.0+)toRequest
102 | Method createMethod = requestFactory.getClass().getDeclaredMethod("toRequest", Object[].class);
103 | createMethod.setAccessible(true);
104 | return (Request) createMethod.invoke(requestFactory, new Object[]{args});
105 | }catch(Exception exc){
106 | // Log.e("buildRequestFromCall"+exc.toString());
107 | return null;
108 | }
109 | }
110 |
111 | public void enqueueWithCache(final Callback callback) {
112 | Runnable enqueueRunnable = new Runnable() {
113 | @Override
114 | public void run() {
115 | /* Read cache */
116 | byte[] data = cachingSystem.getFromCache(buildRequest());
117 | if(data != null) {
118 | final T convertedData = SmartUtils.bytesToResponse(retrofit, responseType, annotations,
119 | data);
120 | Runnable cacheCallbackRunnable = new Runnable() {
121 | @Override
122 | public void run() {
123 | callback.onResponse(baseCall, Response.success(convertedData));
124 | }
125 | };
126 | callbackExecutor.execute(cacheCallbackRunnable);
127 | }
128 |
129 | /* Enqueue actual network call */
130 | baseCall.enqueue(new Callback() {
131 | @Override
132 | public void onResponse(final Call call,final Response response) {
133 | Runnable responseRunnable = new Runnable() {
134 | @Override
135 | public void run() {
136 | if (response.isSuccessful()) {
137 | byte[] rawData = SmartUtils.responseToBytes(retrofit, response.body(),
138 | responseType(), annotations);
139 | cachingSystem.addInCache(response, rawData);
140 | }
141 | callback.onResponse(call, response);
142 | }
143 | };
144 | // Run it on the proper thread
145 | callbackExecutor.execute(responseRunnable);
146 | }
147 |
148 | @Override
149 | public void onFailure(final Call call, final Throwable t) {
150 | Runnable failureRunnable = new Runnable() {
151 | @Override
152 | public void run() {
153 | callback.onFailure(call,t);
154 | }
155 | };
156 | callbackExecutor.execute(failureRunnable);
157 | }
158 |
159 | });
160 |
161 | }
162 | };
163 | Thread enqueueThread = new Thread(enqueueRunnable);
164 | enqueueThread.start();
165 | }
166 |
167 | @Override
168 | public void enqueue(final Callback callback) {
169 | if(buildRequest().method().equals("GET")){
170 | enqueueWithCache(callback);
171 | }else{
172 | baseCall.enqueue(new Callback() {
173 | @Override
174 | public void onResponse(final Call call, final Response response) {
175 | callbackExecutor.execute(new Runnable() {
176 | @Override
177 | public void run() {
178 | callback.onResponse(call,response);
179 | }
180 | });
181 | }
182 |
183 | @Override
184 | public void onFailure(final Call call, final Throwable t) {
185 | callbackExecutor.execute(new Runnable() {
186 | @Override
187 | public void run() {
188 | callback.onFailure(call,t);
189 | }
190 | });
191 | }
192 | });
193 | }
194 | }
195 |
196 | @Override
197 | public Type responseType() {
198 | return responseType;
199 | }
200 |
201 | @Override
202 | public Request buildRequest() {
203 | return request.newBuilder().build();
204 | }
205 |
206 | @Override
207 | public SmartCall clone() {
208 | return new SmartCallImpl<>(callbackExecutor, baseCall.clone(), responseType(),
209 | annotations, retrofit, cachingSystem);
210 | }
211 |
212 | @Override
213 | public Response execute() throws IOException {
214 | return baseCall.execute();
215 | }
216 |
217 | @Override
218 | public void cancel() {
219 | baseCall.cancel();
220 | }
221 | }
222 | }
--------------------------------------------------------------------------------
/smartcache/src/main/java/com/lizubing/smartcache/SmartUtils.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.IOException;
6 | import java.lang.annotation.Annotation;
7 | import java.lang.reflect.Type;
8 |
9 | import okhttp3.RequestBody;
10 | import okhttp3.ResponseBody;
11 | import okio.Buffer;
12 | import retrofit2.Converter;
13 | import retrofit2.Retrofit;
14 |
15 | public final class SmartUtils {
16 | /*
17 | * TODO: Do an inverse iteration instead so that the latest Factory that supports
18 | * does the job?
19 | */
20 | @SuppressWarnings("unchecked")
21 | public static byte[] responseToBytes(Retrofit retrofit, T data, Type dataType,
22 | Annotation[] annotations){
23 | for(Converter.Factory factory : retrofit.converterFactories()){
24 | if(factory == null) continue;
25 | Converter converter =
26 | (Converter) factory.requestBodyConverter(dataType, annotations,null,retrofit);
27 |
28 | if(converter != null){
29 | Buffer buff = new Buffer();
30 | try {
31 | converter.convert(data).writeTo(buff);
32 | }catch(IOException ioException){
33 | continue;
34 | }
35 |
36 | return buff.readByteArray();
37 | }
38 | }
39 | return null;
40 | }
41 |
42 | @SuppressWarnings("unchecked")
43 | public static T bytesToResponse(Retrofit retrofit, Type dataType, Annotation[] annotations,
44 | byte[] data){
45 | for(Converter.Factory factory : retrofit.converterFactories()){
46 | if(factory == null) continue;
47 | Converter converter =
48 | (Converter) factory.responseBodyConverter(dataType, annotations, retrofit);
49 |
50 | if(converter != null){
51 | try {
52 | return converter.convert(ResponseBody.create(null, data));
53 | }catch(IOException | NullPointerException exc){
54 | Log.e("SmartCall", "", exc);
55 | }
56 | }
57 | }
58 |
59 | return null;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/smartcache/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SmartCache
3 |
4 |
--------------------------------------------------------------------------------
/smartcache/src/test/java/com/lizubing/smartcache/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.lizubing.smartcache;
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 | }
--------------------------------------------------------------------------------