From 5788ef1cc992ce76aa73c0cae21d5665cc52d909 Mon Sep 17 00:00:00 2001 From: kymjs Date: Wed, 26 Aug 2015 15:18:10 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/src/org/kymjs/kjframe/KJHttp.java | 2 +- KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java | 6 +----- KJFrame/src/org/kymjs/kjframe/http/Request.java | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/KJFrame/src/org/kymjs/kjframe/KJHttp.java b/KJFrame/src/org/kymjs/kjframe/KJHttp.java index bd03e619..f5c796e7 100755 --- a/KJFrame/src/org/kymjs/kjframe/KJHttp.java +++ b/KJFrame/src/org/kymjs/kjframe/KJHttp.java @@ -53,7 +53,7 @@ * 2、另一边由TaskThread不停从NetworkQueue中取Request并交给Network执行器(逻辑请查看 * {@link NetworkDispatcher} ),
* 3、Network执行器将执行成功的NetworkResponse返回给TaskThead,并通过Request的定制方法 - * {@link Request#parseNetworkResponse()}封装成Response,最终交给分发器 {@link Delivery} + * Request.parseNetworkResponse()封装成Response,最终交给分发器 {@link Delivery} * 分发到主线程并调用HttpCallback相应的方法 * * @author kymjs (https://www.kymjs.com/) diff --git a/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java b/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java index 256b2455..30e1c771 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java +++ b/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java @@ -36,7 +36,7 @@ * 如果缓存器中没有有效缓存,则把请求添加到mNetworkQueue工作队列中去执行网络请求;
* * Note:
- * 关于中介相应查看{@link Response#intermediate} + * 关于中介相应查看Response.intermediate() */ public class CacheDispatcher extends Thread { @@ -55,10 +55,6 @@ public class CacheDispatcher extends Thread { * 缓存队列 * @param networkQueue * 正在执行的队列 - * @param cache - * 缓存器对象 - * @param delivery - * 分发器 */ public CacheDispatcher(BlockingQueue> cacheQueue, BlockingQueue> networkQueue, HttpConfig config) { diff --git a/KJFrame/src/org/kymjs/kjframe/http/Request.java b/KJFrame/src/org/kymjs/kjframe/http/Request.java index b19c6f64..817e1a17 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/Request.java +++ b/KJFrame/src/org/kymjs/kjframe/http/Request.java @@ -23,7 +23,6 @@ import org.kymjs.kjframe.KJHttp; import org.kymjs.kjframe.utils.KJLoger; -import android.net.TrafficStats; import android.net.Uri; import android.os.SystemClock; import android.text.TextUtils; @@ -111,7 +110,7 @@ public Object getTag() { } /** - * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)} + * @return A tag for use with TrafficStats.setThreadStatsTag(int) */ public int getTrafficStatsTag() { return mDefaultTrafficStatsTag; From b317e25473e79c90fb0a066936083839b2bf1bab Mon Sep 17 00:00:00 2001 From: kymjs Date: Fri, 28 Aug 2015 16:24:43 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E5=A4=84httph?= =?UTF-8?q?eader=E8=AF=BB=E5=8F=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/src/org/kymjs/kjframe/http/DiskCache.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java b/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java index 8239a267..2c6aa7e4 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java @@ -522,9 +522,10 @@ static Map readStringStringMap(InputStream is) Map result = (size == 0) ? Collections . emptyMap() : new HashMap(size); + String str = readString(is); for (int i = 0; i < size; i++) { - String key = readString(is).intern(); - String value = readString(is).intern(); + String key = str.intern(); + String value = str.intern(); result.put(key, value); } return result; From f2e619cd412fc6b72b4aa1fe61d627be0d1d1152 Mon Sep 17 00:00:00 2001 From: kymjs Date: Fri, 28 Aug 2015 16:26:52 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=87=B32.43?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/AndroidManifest.xml | 2 +- KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/KJFrame/AndroidManifest.xml b/KJFrame/AndroidManifest.xml index d74278f1..c77adb51 100644 --- a/KJFrame/AndroidManifest.xml +++ b/KJFrame/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionName="2.243" > Date: Fri, 28 Aug 2015 16:34:47 +0800 Subject: [PATCH 04/14] update to version 2.243 --- ...2.242.jar => KJFrameForAndroid_v2.243.jar} | Bin 233734 -> 233733 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename binrary/{KJFrameForAndroid_v2.242.jar => KJFrameForAndroid_v2.243.jar} (87%) diff --git a/binrary/KJFrameForAndroid_v2.242.jar b/binrary/KJFrameForAndroid_v2.243.jar similarity index 87% rename from binrary/KJFrameForAndroid_v2.242.jar rename to binrary/KJFrameForAndroid_v2.243.jar index 3fffab95aa7640f0e0b77679ffecaea1cf48b93b..edc12db4f03941cd6da5b7bf2a65d0387ce31f9c 100644 GIT binary patch delta 20620 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 GOXB}8D)#~a From 597b9e1200a04e5f77be8a3e498fa126c6d70738 Mon Sep 17 00:00:00 2001 From: kymjs Date: Fri, 28 Aug 2015 17:48:46 +0800 Subject: [PATCH 05/14] =?UTF-8?q?KJBitmap=E6=98=BE=E7=A4=BA=E6=97=B6?= =?UTF-8?q?=E5=A6=82=E6=9E=9C=E8=B7=AF=E5=BE=84=E4=B8=BA=E7=A9=BA=E5=88=99?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E6=98=BE=E7=A4=BA=E9=94=99=E8=AF=AF=E5=9B=BE?= =?UTF-8?q?=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/src/org/kymjs/kjframe/KJBitmap.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/KJFrame/src/org/kymjs/kjframe/KJBitmap.java b/KJFrame/src/org/kymjs/kjframe/KJBitmap.java index 346ee0c5..c2beef82 100644 --- a/KJFrame/src/org/kymjs/kjframe/KJBitmap.java +++ b/KJFrame/src/org/kymjs/kjframe/KJBitmap.java @@ -269,20 +269,18 @@ public void display(View imageView, String imageUrl, int width, int height, showLogIfOpen("imageview is null"); return; } + if (errorBitmap == null) { + errorBitmap = new ColorDrawable(0xFFCFCFCF); + } if (StringUtils.isEmpty(imageUrl)) { showLogIfOpen("image url is empty"); + setViewImage(imageView, errorBitmap); return; } if (loadBitmap == null) { loadBitmap = new ColorDrawable(0xFFCFCFCF); } - if (errorBitmap == null) { - errorBitmap = new ColorDrawable(0xFFCFCFCF); - } - if (callback == null) { - callback = new BitmapCallBack() {}; - } doDisplay(imageView, imageUrl, width, height, loadBitmap, errorBitmap, callback); } From fa88a6a9e501cc0a0191c6143be2a00e87d85ddc Mon Sep 17 00:00:00 2001 From: kymjs Date: Wed, 2 Sep 2015 10:01:06 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/src/org/kymjs/kjframe/http/DiskCache.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java b/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java index 2c6aa7e4..d501809b 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java @@ -522,10 +522,10 @@ static Map readStringStringMap(InputStream is) Map result = (size == 0) ? Collections . emptyMap() : new HashMap(size); - String str = readString(is); + // String str = ; for (int i = 0; i < size; i++) { - String key = str.intern(); - String value = str.intern(); + String key = readString(is).intern(); + String value = readString(is).intern(); result.put(key, value); } return result; From 36783dd8db9d68c531c4d56232a1d77aeddf2da3 Mon Sep 17 00:00:00 2001 From: kymjs Date: Wed, 2 Sep 2015 10:02:29 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binrary/KJFrameForAndroid_v2.243.jar | Bin 233733 -> 233136 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/binrary/KJFrameForAndroid_v2.243.jar b/binrary/KJFrameForAndroid_v2.243.jar index edc12db4f03941cd6da5b7bf2a65d0387ce31f9c..135607cc57e8e800e99b52ee847c78fe3427a94e 100644 GIT binary patch delta 15879 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 KeIVr1x&56I^0-S8*5 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 From 71bf9261a9c1af13464eb6ca921f8d0d6ca58301 Mon Sep 17 00:00:00 2001 From: kymjs Date: Mon, 7 Sep 2015 16:49:08 +0800 Subject: [PATCH 08/14] =?UTF-8?q?KJBitmap=E6=B7=BB=E5=8A=A0=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=8A=A0=E5=AF=86=E4=BC=A0=E8=BE=93=E7=9A=84=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/src/org/kymjs/kjframe/KJBitmap.java | 6 +++++- .../src/org/kymjs/kjframe/bitmap/ImageDisplayer.java | 5 ++--- KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java | 10 ++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/KJFrame/src/org/kymjs/kjframe/KJBitmap.java b/KJFrame/src/org/kymjs/kjframe/KJBitmap.java index c2beef82..e3dc218a 100644 --- a/KJFrame/src/org/kymjs/kjframe/KJBitmap.java +++ b/KJFrame/src/org/kymjs/kjframe/KJBitmap.java @@ -69,8 +69,12 @@ public KJBitmap() { } public KJBitmap(BitmapConfig bitmapConfig) { + this(new HttpConfig(), bitmapConfig); + } + + public KJBitmap(HttpConfig httpConfig, BitmapConfig bitmapConfig) { this.mConfig = bitmapConfig; - displayer = new ImageDisplayer(mConfig); + displayer = new ImageDisplayer(httpConfig, mConfig); doLoadingViews = new Vector(30); } diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java index e5089fb9..b52fa70a 100755 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java @@ -52,15 +52,14 @@ public class ImageDisplayer { * * @param bitmapConfig */ - public ImageDisplayer(BitmapConfig bitmapConfig) { - HttpConfig config = new HttpConfig(); + public ImageDisplayer(HttpConfig httpConfig, BitmapConfig bitmapConfig) { // 靠,在这里踩了个坑。 最初写的是Integer.MAX_VALUE, // 结果把这个值*60000转成毫秒long以后溢出了 这次我给个死的值行不行。1000天,能不能算永久了 // 其实还有一种解决办法是直接在缓存读取的时候,看到是bitmap缓存不管是否失效都返回, // 但是这种不利于自定义扩展,就不用了,有兴趣的可以看CacheDispatcher的105行 // @kymjs记录于2015.4.30 // config.cacheTime = bitmapConfig.cacheTime; - mKJHttp = new KJHttp(config); + mKJHttp = new KJHttp(httpConfig); mMemoryCache = BitmapConfig.mMemoryCache; mResponseDelayMs = bitmapConfig.delayTime; } diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java index 4969fa99..5a9b0d6b 100755 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java @@ -16,9 +16,11 @@ package org.kymjs.kjframe.bitmap; +import java.util.HashMap; import java.util.Map; import org.kymjs.kjframe.http.HttpCallBack; +import org.kymjs.kjframe.http.HttpConfig; import org.kymjs.kjframe.http.HttpHeaderParser; import org.kymjs.kjframe.http.KJHttpException; import org.kymjs.kjframe.http.NetworkResponse; @@ -41,9 +43,12 @@ public class ImageRequest extends Request { // 用来保证当前对象只有一个线程在访问 private static final Object sDecodeLock = new Object(); + private final Map mHeaders = new HashMap(); + public ImageRequest(String url, int maxWidth, int maxHeight, HttpCallBack callback) { super(HttpMethod.GET, url, callback); + mHeaders.put("cookie", HttpConfig.sCookie); mMaxWidth = maxWidth; mMaxHeight = maxHeight; } @@ -58,6 +63,11 @@ public String getCacheKey() { return getUrl(); } + @Override + public Map getHeaders() { + return mHeaders; + } + /** * 框架会自动将大于设定值的bitmap转换成设定值,所以需要这个方法来判断应该显示默认大小或者是设定值大小。
* 本方法会根据maxPrimary与actualPrimary比较来判断,如果无法判断则会根据辅助值判断,辅助值一般是主要值对应的。 From 1ca78c2834f26320870fa986722cadd998f8e5ea Mon Sep 17 00:00:00 2001 From: kymjs Date: Mon, 7 Sep 2015 16:54:00 +0800 Subject: [PATCH 09/14] update to version 2.244 --- ...2.243.jar => KJFrameForAndroid_v2.244.jar} | Bin 233136 -> 233347 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename binrary/{KJFrameForAndroid_v2.243.jar => KJFrameForAndroid_v2.244.jar} (82%) diff --git a/binrary/KJFrameForAndroid_v2.243.jar b/binrary/KJFrameForAndroid_v2.244.jar similarity index 82% rename from binrary/KJFrameForAndroid_v2.243.jar rename to binrary/KJFrameForAndroid_v2.244.jar index 135607cc57e8e800e99b52ee847c78fe3427a94e..db5ef773f204f8f25055c761bdfa81c9000be406 100644 GIT binary patch delta 23807 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 fXU$LJxJ`vXafFoZ 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 D*L Date: Wed, 9 Sep 2015 15:14:56 +0800 Subject: [PATCH 10/14] update --- KJFrame/AndroidManifest.xml | 2 +- .../org/kymjs/kjframe/http/FileRequest.java | 14 ++++++++++---- .../src/org/kymjs/kjframe/utils/KJConfig.java | 2 +- ...2.244.jar => KJFrameForAndroid_v2.245.jar} | Bin 233347 -> 233444 bytes 4 files changed, 12 insertions(+), 6 deletions(-) rename binrary/{KJFrameForAndroid_v2.244.jar => KJFrameForAndroid_v2.245.jar} (95%) diff --git a/KJFrame/AndroidManifest.xml b/KJFrame/AndroidManifest.xml index c77adb51..0c991f3d 100644 --- a/KJFrame/AndroidManifest.xml +++ b/KJFrame/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionName="2.245" > { private final File mStoreFile; private final File mTemporaryFile; // 临时文件 + private Map mHeaders = new HashMap(); + public FileRequest(String storeFilePath, String url, HttpCallBack callback) { super(HttpMethod.GET, url, callback); mStoreFile = new File(storeFilePath); + mHeaders.put("cookie", HttpConfig.sCookie); File folder = mStoreFile.getParentFile(); if (folder != null) { folder.mkdirs(); @@ -92,10 +95,13 @@ public Response parseNetworkResponse(NetworkResponse response) { @Override public Map getHeaders() { - Map header = new HashMap(); - header.put("Range", "bytes=" + mTemporaryFile.length() + "-"); - header.put("Accept-Encoding", "identity"); - return header; + mHeaders.put("Range", "bytes=" + mTemporaryFile.length() + "-"); + mHeaders.put("Accept-Encoding", "identity"); + return mHeaders; + } + + public void setHeaders(Map mHeaders) { + this.mHeaders = mHeaders; } public byte[] handleResponse(HttpResponse response) throws IOException, diff --git a/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java b/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java index c97b0028..b1016b98 100644 --- a/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java +++ b/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java @@ -21,7 +21,7 @@ */ public final class KJConfig { - public static final double VERSION = 2.243; + public static final double VERSION = 2.245; /** 错误处理广播 */ public static final String RECEIVER_ERROR = KJConfig.class.getName() diff --git a/binrary/KJFrameForAndroid_v2.244.jar b/binrary/KJFrameForAndroid_v2.245.jar similarity index 95% rename from binrary/KJFrameForAndroid_v2.244.jar rename to binrary/KJFrameForAndroid_v2.245.jar index db5ef773f204f8f25055c761bdfa81c9000be406..db2ff90f8505cba14207eb35a9ef935ac708dcd7 100644 GIT binary patch delta 5897 zcmY+Ic|25q^#9Ghwrts#vBt+v*|)KT5VEfcMImAA24ON;vW?xfE0Qdw#h$1vlO?jG zNU~(#No4DHedqV++jsuCkJs~@^M0TAd7pFVF>|+w?ne07S2cAcN>`BY3fQMkt#VvTWFUm!dr>uNmG+`j3W@k@CL;O0E2X zwTpINAU8KbQeS6+5fY`pEoDSg?qL5_(93c18se?I?^v3GSK1?^A-#Qcn);yISdEc$ z-Z91%Q%fm1GXLw~VGe4y(0@JTcz*n7Y58gx#~Wmvy9kqn$>r-$VjnS5$Uce1(&6oN z-+6M^uLdlKKW}Ixgh_e0deXAsnivC=k&znG*UBU;HczqslEh}J()09m$ zz8oVzYaYz>)kxr$9miWONXdgR{UlsBceo|;i|ydEm#n8gJG^chI7;N~dOm{q8ZSr~ zt1cMrRTER}Oq0I-^1h-fZ`K6kKGti#Wau6NmH7U_ub4BMI&NaO_Ra7*DgnckV{PBO zLs7Jfl($@&$I&-B%OCP0hAUPf%I-E=ZFNfY@Km%(n-TE(OG5w_9b3og3X$&nQNu_AdjgAj~ zg0H{EU2DV@-)WgKfIk?1N6w8=rkvKzU!s~7n^$^x_g6G*{pJva!Y@`aQp&MtJ0B3^i$kD>PaAckNk5Oio zIwzE*s#E*Rxu<4tSN&}(rtB&Yo|E-7e|g(r?r3JD zLgFi%?602}TjH3f=+YZh(g_(6Z9HX6FRk;PeZTHe8lAOOuD(zrt!rU`iZqQGn^5Me zF!n!upB;GP6FtMYLh@=(O5%I|pI6%ZFZ~CG9bQ6cA*msU%m0uAy+SAg?b7fI@%#2fv{qQqd&Cz4J+P2#Ps#LtQNX&9_Ja%e#DVR4R%l{PH z`oivVXQuXrS>84ZIn}eaf!xmZy*uLbb}H)kPMgUSKHukJDD->1P{IjAu-^+3( zy-r?dQzJ3?)*fX-8)t(Zfv^#U`{Vhav#ZETus-`a=FQ(1H#!-XJNl`S?ADblDV)eN zx5sI8EYiDzyYI!Ugk@o)bvXof9K&!Q2jzEf-5h)$<*d)|DPhn((;mt7QAzywmnWY$ zx&|5QO7t2_NRScvZ)9BFmjxd38&j!1aw)A^E?uqSp3AJel%UGfi;9dhz)OVx%$C+Q z);RN+9xWfDbZ)6hp|>#4t$Mbj?C~B?UykFU*vs2Dx)o&eEYJK-#8&7Ou}DzwW;|7L z3@dCHpYGtWPL*VE$PF#Lwl(73jY?@&pgbgxxAO7#&A8JZjo^_5$qRL0^ss zEw1=eeU9dwQ|)$e`;6UjIgE+9$~JD9Ye4W88@6(ZJMTP{^_uQDbd!bL^4P~UGsi40 z%e5v|D5J#7H*Wc8&+8B$Q{5NG);-428RUKI(%bR%3iL3OyxxoJ5n z5gpYtd3)h@s4R~6k8&F8m@GU^zrPKd5G=gZCbd&Bc(<53O7Ctpl6&drzm0bw9LKaN1u_V`Xau)4POvj~ovL1P;#M}|&K zxHON;QRp^3Gb9|5R^QqaUAHK`8PFW_`CE1u1{7}-G;4aSvW?Z%`N+cHSQ#F7R+cAE zO0ub?h1t9C;cEl2hvrNsi{|;lpLCe2Z?Rf%psvpEKN-%R9iGh;tkiBPU0a+gwK%lY z_-;S&eZbN=!D7^^WR|7Cx=!e5I-RW9rdumW`(^0!Ms!Ymi!O9$L-$Imv{79L%Y|^a z7HdfG)`Z^Apn)iCgP`IPV@BTiyNm?&iGujE{@?T+ks){7|66FN?vZgon~|7t_jsZh z4d;X98m)YKXy*J&1HZPc9rL4GhxK0%x+t@p?r&@rU!ZcZ?p}?0sxR&;n^v=Nj9)|7 zvS#TTA>-F^vb{}m`m vp-9o4na0$;wl5(t)O-?27YR6y0Gvwo8UOFv5I(q5tg_c zlCsELF2(dSflnStYoArtQzjQ2wR4y#{sx;(MGYrk0p&qnpT7v~!uY{B;3-u-gW z>v!)eDz^gpf}&hgc*Ih@n|g1e{dk4T{l+yD&jk9qBuR5+ixh5cJ1&hcC7NaCmQLe^ zvpUjj9QxwNu_YVQD3uD5W*K>vZlz+9Z)8iANgtn1IT>}?-+1uWS6qJ4+CPwOx!zf{ z=ed@};&`9nhj>-rS0+IQtwOI1Fo!QM2aHPIrc8PL>eq|Zf?Z9|sA6A826*2 z<~+wQ6-I|mxY}+`cel-G-e)`Ev4?6xY2-(0+X%#z-^#4pH#zHiD^l^ZZERK9ow?)V z`%I4m^)TydWY|36g`7@Zx5|VsGUEX}1@j-d7jksh<@O6cQ1qK-akIanS1>z1WAL(Y z3sL#_E!7f(E;sTcPPb3-0r!Whw#qJFijcMTE6>R(u+Ql98k->dJcn>44H~7A@Nxb8 zfe3te{=nDw7RLC4ji5oBl(D-M8+7WlPru`~#8ul%H01)6u>#0*2kgVo%G2_6LY`ee zU$gZ@VOQjd2};2lX{nNqK`}0#)imahR2JTz^qe#Dmq|8vhZ+S5_B`x)AI8ku?HsQq z4H%sD!208Bs}5yfD%G?2X0a%po~Cadr;JCT!XO2U&7kx9$g)(W$g`#I-VZXNa)XBGv$KxG_TZ^bxAEoZz$0n^C9Zujh15&li2eb+64|^~}pA5=V z#C^_4(M@t3GG>Yke0XVmP2{u9gJ~r${WHSQC^7EBSi&Zwn z6BqMGhV}v`{m}tzelG5l0h7@=)g0cEJ{OQvQw$Z1k}txqy#3|2>8y!!*TY5NEtzc; z6tL1p{mh9!Fs=mAtG%P{afVi=4KlH$^HgPzbHs#M`(}Xh)`i~8fehhc{n_tRkXGD$ z-r33G`_;bzM}~mm1H2lU_vBW{p|$ZcEl<<7$8xRRsuF(q+$KA>QKW`avWDk2`Rn;j zVc}~kLHmYd?s3B2#i6ZTXJ|joQHS1b*~acIzexWQ%<$d+KK};hKf3*kYyI7j*Y z$k0Ps-nGU*$UP^Xi^tpMuc;cW6~D;r5R4}kIb$PqCJ__i_POMvCXqmF zo91`rQ?<}X6|~hoYhgR_Nbzc?bKw_$rRa*fCC(poV>wGbx%VT=e-`JvvNxZlnC0VUg-+$9 z1#V@lp5cW88%aXAcQ1+yuC|L`exlSdVIdh55DYJ8azCe5RN>WF5D5Zs96y4psuLEF-*Q{g?+ zwx_O+<4xI*-D8i465g=jD!F80Ac!}VH@+yTE_y~)u+qmIxk>Z2)6GSoFx@NI5gWQU z5p5w8Wzr?}nXus=H}f*BbN&WU1eGXBCQp)u@V4+J`yOlOV{zLie#h>88a)@x^*0}C zF@~rYF>|R2aAuR_s!C$IzR?_$ZJlyY4;u4qR2Ab?-r|!uJ(c$&F3Y1$bF?>VXAtS& zcUn(-5pcLNcWYLcZlCMI z2M@|ydwv}r!OdE0VJv~Ud(G9wj_Dg78}2V0B5OZ&Kh?$)c zvUfW5@kT*VG~n@>elK6X4uyKCXhUy3Po>pCJ*fC9+g=1$8lV47%(5sWRuJ3xeYCB( zgE{|`Tw&&eo{yg@%R}s9`jrx8gLh?0o1%1rODVHFs6?zvB7OwXRh~Jo(W~vcVemw> zS%#}OT_>SQgz(i*MQ*NdS5ZM8t6yi*6Dj*rj$!^hS^VtQ9f98{KP0=%bj>9fMuzm~ z9YHyAGrQz}*h~#6C?glp;%;=LBqRYmvC?sfdg(a8LL7vDtU}+c5CYJhSdbigRs+yu zJxqqKF-JI__28JD=HlbK_qgFf4Q`UQy+^{eDWIz^F(q5h<+dWKDXV!|>W~VXAdM)A z<2_vXGmzXl1myvp=hW2ZRp%F#`<0K5m$z+5sDq2|-*pnB54`SCKYEknN>Z**?)c5v zr=MjUTf0dkK0Fa$aAx;ZLgA;BBfh=&hBRvdW^Akr!DIW2#jlyK2a4rIG)H8z*IiF} zyh(Xq;KQOlwZG#M^Wb_$=DE?V%P&3`vD~v0&n-0{33ab_@qZML5Yt#0oM3J4 zB-<6we<18VtFW@OVrQ*`8-KGKw2DE0a51$JD_njxC8q5r=HR-}O=|V@T9ng|0D=Cm zPiO7 zgWt%uOJW$iu~~o7SYPcn8njHiHQhXiK>^vL9edN6_ zX!nLBWoQ4o`o^m+*{I~zOADs_qbU!jjQ@+pBFQPbc%&q5-Yc=`;BWiUn!9oLv&)237BLHt3NQk?g<6gjmRs(0p}a{d8Fk4kW-UdBZ#537U(E5R zc`|bpU$l4u7+#fXPQ7-!;peIJgNDM3t4=|u_w{;<`o`6|CBjoOb{CaJ7YWkmW1;_0 zx~Xynka18ddNJ|>PF=HVs`5<0{D=m#X*E1jgEZ&Xb1p-xrDT z1xQFZA+uiyZpbJYV1p)$0kV2bJ%A}b>CF1g%itI*9sDnf9n~9Oo)?`6KB(6Zltlz* z`Gc;&!$&7b6v_+$S)nq25D8qbJt5rCq(7(%guf;VQ~;;|P=6qblmJj3Fzq9XA1C6< z2&Cx?azK(J2nNW*7o>-*0^y}}zCpf$pfvzao;(GiwLnlBu?z79fi{5rJdqU!ff|74 z5>Xf|As8VWH#lAh10oSskR}Ga30SQWtsgPqdBhOJ5DeY`7=9Aj`Zj_SdgBF05Q5wo>l%n(-ycpj+y{g*I7ts4k(C>afVSB1dd;(v&?)e!h}WB}w84xb?<7_O#4D5wqy zkp3mqS)>3t)DQ_rwui!zf{aAd?@%~MlR zL#a5B4dRY~uPDNVD2z=2I_ORWyw$cTk?`2SL@N$nG%%%RfXf z5+-wQ|B#|cP#d6e|4W!4Ivgm5@PkrtAQHNO2L69j1i})q_dfBn0HS?Po*d9099)DE zUt;hhU%1eD0dU?ScVXYGU?Q%%3y*@)ZOADa6oTlYKpDgxNHYq&41{Be%pMC+LMA8I zI~E1UO-B+jS2Ua$?cEb$4Xnfw7yB3uZzz)ZS5QMzF>vXulm8*nG4Pf0r4q?T3P2An z#=tj&$vGh`|1<8;xmegIrsyAXxd=Y#?>IP%$FXo0j3s|<%+P8qNW%xe8}$CZKH#r3 zA(DST-)jE)lbw8}(Gf?<>>j*Uas5BG*n4oJuDtq(jNOArW%SKIBsmTq1Gsslft`2% z7pnnO9siK;?Eu3`(qcg7&_7rs9xnOzJdvct!{b!Ea?--gS-cXqr0p?r(3q-a6 zCdf7cUPT)LP2u5AFG>KFfjLT|Xr=@iA&NxU+{i#A$qb+b#A^@NDn1cD-%rNBm=@|w z1f>9JULuj?1F0dQBv1#i;rmM%pqwOlF?LBJnM{K78&V<)6du$EW>G}(3I#F~%h-X3 YCpQ^*zGIExts{uu=L+G-$z6axnb2O}ns%@@B4AM>jR#Ek$r zAp%cKdKAE|Vrdtlm)N!HODca&sdYl7m4T5eg0xgSL|{ECo9y4tI+vWNeTt~7t;F3& zDeXa{?e4WVi$~@IaENn|al+XeDY?;tcFBz?xxe+MDm9+SpGgRCa+j0u)g4MtGj$bD-6%PkWLhF zst&?yDI{@Bk6=ioE)_^9*h2_;FLd%{amOO}fBjH+Hl%JLkYJ|qktBYa4+XUTg+P$Ee*LG=y0!u?AOn22yzjx zbQGfyI{t=LPAb|pK1-3<01?;toP17P(Kr%(h*PMn;hABUWHar8vZ>r&C1Rhu;W85t zAEB{oEorudSo+c_|I92t>QO4zt{aop&(R0|QDe^v#VwP9?7rJ-)-D&dh~wL0_2PZ{ zyZ+5x($aE6fwjn7>YX2pLGm-gi9O^H|C3yl6M?rK$qeu8+W{{oDv=@v!=XYm5I=+G zZ5I9u66>yrr{8BJnDq5%5*m~{`xC!?IXF_m`VwL&Bg|(})%Rj^f$AX*<`kh3 zWJ^#W)A2|4K_#oMBJnBN`ooQgPoJMEP>32^{L-YQ=AephRG6w4$q;{H=-){kpQdff zin6>(Ncm9iWS^CDy$s=0o=fT)#u0So!B4!g(!tWM(S(u;U*zDUyHh}1#Y<2i}mImF&9(5GlW)`;GNi!+ZiZow^(NYi{q8zdlj&XtEJ?dNHz%@qM80 zX8&Hks;F(D9yiSwVz6x8YI`@kB_dPU!(~H+4(~C-oI2#IOSqGqGO*D8bfy$vcG3Zr zH^o@M94NR!ou!k@6vMJ9>n;)doSBGb!pcPwM^0JI#bTHlH{E?rNarUGAB}L7&F8CM zP37HcoVKLnJ=D4oQ6mP}I0PK76n!40Pm9Aek5PFeVov=7m3A7#$JgD0gMtw?Mr?=T zbe&qf;&onJmdn=b6M&a6&xi1`A4hJe{e?V4Bx1@#V&0GCNpw=In|03*@nyxjqr0y94|FQD@t&8h#Iah5RqqLycis~6znO1PJ4SBrGZ}zejq1yo zu1*ww>KPoAS2vfsY|kvBw84eQJ1j!^GQ+Pa_5sn(z12gZE+kr*5Ge!4tnWz0S|@&iYr z7;EDD?K#b5XL98_j?}h1!TLUZ6E&@_JY@PfqNQ}i>6vaNvL$QgPZWo`nPD4NOJq)t zv(bYGC7X(l@%?Up#Ik>PUE_Nsni^=k{D9jE*SbeRauUDTL7u27Oi$43m8>f29%Fx* z*AahK*xjbgBMs9T$vArad9?!<{3g~ABY*kXcY7{gR1oEcf;~e9i`WDK2NwQ#_&%e| zEB|Ir%JuvLr>4S%nyqR3J1;$dtTYZ-hX4AZGT7ulJGSNWB=U19f2doE9bL`zmBYEZ z9nS;raz~~bfp}f_`U1)$WAh*(ROU+Lhh_bHOPpv1Gw$4b}g5clwPlf%t z&t*mdMGA3EXK`cRwJh^prWLo#?~X9M;Sz`!TQIwGfnfeLdPkO71^n1&wPK^ zC9(_B*k-Zt_`SQr;B(fBvOSQ-^fWf3J@Y?XVl1#1ND{R)R8WyGV8&zO+NYn_#h2mu zMoOySQXkY?SkS+?-WvI7uqOg#s2N;Zy(p#YM!2`Xr$8|?E3~I%Y)>LQ+K9_(cHP0yxwaxN*tT4iDS77JacbKAuQ{?0 zM~tH;P5Diqjg+K1qE`(Zy?&(Hqg94d5$f^9&G$@#qLvn{GnTRqRDTUZBbF52)(N7p+!QNv8D@xM z?CX?Yr{&Dj?dm#DvV7{!^7bSw&oSAPT@RhN?-;CN?akWtP;OoK$&@+@@}SRAFN}X> zyxAcYR{F8N3q|DlG~@Q)f^2O5U%suo=KaUJ6q#=1v7a*s`n1JFW4a%ewN5vzl#mpu zZd0S0x7t(3d2XzxJ5N=jKE^2|B#n@+s-6^-uEb|`KWa$$@PY0vTBR4j zd7F`_I8p8(Q{v^W&1hZL=aveb>VK_`>@%w?1DVKtcj}q>>Y+~ddXzifOVLkuo=I%q zgNi1Cgesp)3_j1{ZZ@2%BIqrp<0Q`c7D*#?-x0KU#6BK$`+0z`Ms4`;w6QHx#1&&8 z|Ml8@#78j>CnX6a;h=u^pz)iM%ml(uU)lyuif1EsI3?TjWCbfl&-ShJF|P=HC|I;S z(Vq(n4i2xV({lG52+c#4*AFMntm|ig0@4-zJSiT07dEZ)mR)LC*?J@5*0c21dsp{2 z$35;0-EUeb<1uorst?CG?#bU2lPxNeLCVFF8c`?gRNBV2MD{(JgwtGh8p%lerB+gc zv{v$$&Y})G*OJF3I((6MB7zyg-Dk>TuNe`JBZ-m}N8P6t`PUjy6-`T=#~#_JE71g1 zM$KAU3swVS%P(HWeGJHul5B*UXZ#-xDWCmAO8D2s^V$6Tn|9m){{0e&$?6glhb`tQ zV?`jQsc7VH<%^{J-QxMnh$?7X8k@3#;DFPlgCKWCFPCYj;-Jo85cPfg_%5rwnf2PW zbn1^juf6-<65dh?My0c4N{#0^i0`Fd-3UL>79$W2Ho-~z6XawPNU87xlYDwQi3EIv zqi$i-;OS#kkfD8s7#3Vst!`*)1x{F|IRew zde>+lqRv$P11fhq0&2uiL%@~YH2q556k(vERQokDk8L~>rvKIv+kFHg_TNPbf6lKfmVR>2Fx z(cy0(JOjA;f*;zd;dZvMwnEM{di&&y5HYo(MNe&Uwk(sUqI zHFtIq-vfhjPG1szAzTm}kQ$tfM!MofZ?zoSdqwgdi8nR*c5P`$M*W`0mA;qW{x|=R zH+g@wJ}c075cxv;m0spgMv znZ(>2P$j2_%nt%211Of#-0sq~u`MA7nxI`Rr&EK0jJacFT>GVyiZEa?Q0~xik4}d) zznjV?Uf8cmDLq!C+b&}AnY)6p(K<4 zUTciRQ%+!S&i*Zz-(I$c_=<9TyY^fp-4nBo1PL9DfAz8QHx}}}@y+n>laIOCueH26aeBtM7f-~GYP-yB^m=FCpw_~|H?_kcsRwpF7RaA+|2Bj*CbY za;25XMva*7JdEzy|x-Q zj?DObN(2E0h9fP+hyX-_dqXj6dP4yk&e!;AM9``(CONbf23|p_smi~wx3sakZT{TM z$tm#uBdzN?M5hGM=DmdQDjy$)cTd90IdQ(h>iplwezce#TWi+ z>Z)cN$1GaB8jJc@@r2tA$@!csPbi;@N<__xn?M;=00k=0qRVb~;{N`^$>8rziJ()z zB)*D(s(v{})5j`{nzdnO4E0C+Z9{xLJ<#)BuF17QJcS~%{(J1U4VB~NV)r{R;^|#& zUSQXEf}6Uc@><_}>?YPGt3HGw>KcD;PQ6gn;LYbG({p1ovRF~i>Ahx5)zm{4!5fik zzv%v`R+kRd&KHKnGQS40g+#1X8dBdj_fsm_9ePJc8CS43iJY(XdykiU8n?laXGT39 z@|f)&yQz8N-6c)6PhPi|cFtsf(SJ#Bbat9c%w8W!xU78$=gKUb?~PBn>`#x zc7(r@$lnqo-rlEW*|QexyMH}xPr`X*;1e;k7Gnv>aVUu05UoV3I<35aYgX&@2mL9Q zffMmvyRd6I+3W_sIXsl#RZ69djsm*9jkL1&c|T#FFfVIaZPHmJNUQYx+e>}3w?LWM zrWQw=rII|L#`a02X73J@EPa~TX{Bs}B6I)d!W^!y`juddP*;p*{)bPu%|i`O;13$u zIJ!r)aZUgR#tj6-CxglV|HD`_Q3Jeu2f|-DYVH|=Q{+&l4T!+IG|A#f&=OMsdW!^g zfvsq$#U7-B?(}0Kpokbu0;tCryaw?&f^@*`*Oz=lj^I`3r6VW~6vh7+O8}Y~mk5Uw zD2z!4nK^-$fNbt12g(T+-ggGcAslCr8o;Q!B!MQ4KqAQe+(+GT+2;%j1NnUy zgb+G*204I@{tJSDLI*IfLfS4cJ{Uven6H?`P#+RjtagDF=V#FP$06o5=&uXN2V~A( zB3iB>ALbj#*A>(QhL=n2@X|cmpzZ2l1fy9-uVP3|dj3xZReFLVfHouASYZSx zAX+b2YJeS0EI9x&NbSXWr(STUuei{--V0O*LV3}Kk2i<|aeBjJal&^&?f~BeFAQv` z6A7RBfj2C1LkW$!UxA`P^j(NC6eNSZUctrQ&_Wv}S^yDb9RcUueg!Xx(MDsG9*p@> z@Ub=DyF|jyN#%n}Wd5A2J-i^d&gYp3dg23%5$a!HLWs;CE~@q%W0_rGY-q~|ZbiWS z63H|JaG{<6kOH#yg`-(4F8h4pXn(8Ao}=PmF7)F?D5Mgde%sV^)c5eQ45d@lvQ3IyeVLO(Q-_J?!5@Ph+S&jUk4 z(U>O)KC_PS3j-gD4T9x&qb`xoEUObNhnMH!MkQXL==LsWV5{QE)^tf<)@Mv=Y`ow6$eI0&N zPl!Q!X!;z#Cb_`G&`(1+S}_vdj3K&nY>T-MJ&pv~puI?V6KlB6*$qgG8ze From 38479e7776cad725950540dbc1ae4c8095594740 Mon Sep 17 00:00:00 2001 From: kymjs Date: Mon, 28 Sep 2015 14:56:07 +0800 Subject: [PATCH 11/14] 2.2451 --- KJFrame/AndroidManifest.xml | 2 +- .../kymjs/kjframe/bitmap/BitmapCreate.java | 2 +- .../kymjs/kjframe/bitmap/BitmapHelper.java | 216 ------------------ .../kymjs/kjframe/bitmap/ImageDisplayer.java | 10 +- .../org/kymjs/kjframe/utils/ImageUtils.java | 1 + .../src/org/kymjs/kjframe/utils/KJConfig.java | 2 +- 6 files changed, 9 insertions(+), 224 deletions(-) delete mode 100644 KJFrame/src/org/kymjs/kjframe/bitmap/BitmapHelper.java create mode 100644 KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java diff --git a/KJFrame/AndroidManifest.xml b/KJFrame/AndroidManifest.xml index 0c991f3d..a7826315 100644 --- a/KJFrame/AndroidManifest.xml +++ b/KJFrame/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionName="2.2451" > * * 创建时间 2014-7-11 * * @author kymjs (https://github.com/kymjs) * @version 1.1 */ public class BitmapCreate { /** * 获取一个指定大小的bitmap * * @param res * Resources * @param resId * 图片ID * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inJustDecodeBounds = true; // BitmapFactory.decodeResource(res, resId, options); // options = BitmapHelper.calculateInSampleSize(options, reqWidth, // reqHeight); // return BitmapFactory.decodeResource(res, resId, options); // 通过JNI的形式读取本地图片达到节省内存的目的 InputStream is = res.openRawResource(resId); return bitmapFromStream(is, null, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromFile(String pathName, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeFile(pathName); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); options = BitmapHelper.calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeFile(pathName, options); } /** * 获取一个指定大小的bitmap * * @param data * Bitmap的byte数组 * @param offset * image从byte数组创建的起始位置 * @param length * the number of bytes, 从offset处开始的长度 * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromByteArray(byte[] data, int offset, int length, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeByteArray(data, offset, length); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeByteArray(data, offset, length, options); options = BitmapHelper.calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeByteArray(data, offset, length, options); } /** * 获取一个指定大小的bitmap
* 实际调用的方法是bitmapFromByteArray(data, 0, data.length, w, h); * * @param is * 从输入流中读取Bitmap * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } byte[] data = FileUtils.input2byte(is); return bitmapFromByteArray(data, 0, data.length, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param is * 从输入流中读取Bitmap * @param outPadding * If not null, return the padding rect for the bitmap if it * exists, otherwise set padding to [-1,-1,-1,-1]. If no bitmap * is returned (null) then padding is unchanged. * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, Rect outPadding, int reqWidth, int reqHeight) { Bitmap bmp = null; if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeStream(is, outPadding, options); options = BitmapHelper.calculateInSampleSize(options, reqWidth, reqHeight); bmp = BitmapFactory.decodeStream(is, outPadding, options); return bmp; } } \ No newline at end of file +/* * Copyright (c) 2014,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.bitmap; import java.io.InputStream; import org.kymjs.kjframe.utils.FileUtils; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Rect; /** * 不会发生OOM的 BitmapFactory
*

* 创建时间 2014-7-11 * * @author kymjs (https://github.com/kymjs) * @version 1.1 */ public class BitmapCreate { /** * 获取一个指定大小的bitmap * * @param res * Resources * @param resId * 图片ID * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inJustDecodeBounds = true; // BitmapFactory.decodeResource(res, resId, options); // options = BitmapHelper.calculateInSampleSize(options, reqWidth, // reqHeight); // return BitmapFactory.decodeResource(res, resId, options); // 通过JNI的形式读取本地图片达到节省内存的目的 InputStream is = res.openRawResource(resId); return bitmapFromStream(is, null, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromFile(String pathName, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeFile(pathName); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeFile(pathName, options); } /** * 获取一个指定大小的bitmap * * @param data * Bitmap的byte数组 * @param offset * image从byte数组创建的起始位置 * @param length * the number of bytes, 从offset处开始的长度 * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromByteArray(byte[] data, int offset, int length, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeByteArray(data, offset, length); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeByteArray(data, offset, length, options); options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeByteArray(data, offset, length, options); } /** * 获取一个指定大小的bitmap
* 实际调用的方法是bitmapFromByteArray(data, 0, data.length, w, h); * * @param is * 从输入流中读取Bitmap * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } byte[] data = FileUtils.input2byte(is); return bitmapFromByteArray(data, 0, data.length, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param is * 从输入流中读取Bitmap * @param outPadding * If not null, return the padding rect for the bitmap if it * exists, otherwise set padding to [-1,-1,-1,-1]. If no bitmap * is returned (null) then padding is unchanged. * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, Rect outPadding, int reqWidth, int reqHeight) { Bitmap bmp = null; if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeStream(is, outPadding, options); options = calculateInSampleSize(options, reqWidth, reqHeight); bmp = BitmapFactory.decodeStream(is, outPadding, options); return bmp; } /** * 图片压缩处理(使用Options的方法) *

*
* 说明 使用方法: * 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。 * 然后将Options连同期望的宽度和高度一起传递到到本方法中。 * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。 *

*
* 说明 BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存 * ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数 * ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存 * ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、 * outHeight和outMimeType属性都会被赋值。 * * @param reqWidth * 目标宽度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 * @param reqHeight * 目标高度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 */ public static BitmapFactory.Options calculateInSampleSize( final BitmapFactory.Options options, final int reqWidth, final int reqHeight) { // 源图片的高度和宽度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 设置压缩比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; } } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapHelper.java b/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapHelper.java deleted file mode 100644 index 312e3dcf..00000000 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.kymjs.kjframe.bitmap; - -import java.io.ByteArrayOutputStream; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; - -/** - * Bitmap操作助手,包含了图片大小压缩、缩放
- * - * 创建时间 2014-6-30
- * 最后修改 2014-9-19
- * - * @author kymjs (https://github.com/kymjs) - * @version 1.1 - */ -public class BitmapHelper { - - /** - * 图片压缩处理(使用Options的方法) - * - *
- * 说明 使用方法: - * 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。 - * 然后将Options连同期望的宽度和高度一起传递到到本方法中。 - * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。 - * - *
- * 说明 BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存 - * ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数 - * ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存 - * ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、 - * outHeight和outMimeType属性都会被赋值。 - * - * @param reqWidth - * 目标宽度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 - * @param reqHeight - * 目标高度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 - */ - public static BitmapFactory.Options calculateInSampleSize( - final BitmapFactory.Options options, final int reqWidth, - final int reqHeight) { - // 源图片的高度和宽度 - final int height = options.outHeight; - final int width = options.outWidth; - int inSampleSize = 1; - if (height > reqHeight || width > reqWidth) { - // 计算出实际宽高和目标宽高的比率 - final int heightRatio = Math.round((float) height - / (float) reqHeight); - final int widthRatio = Math.round((float) width / (float) reqWidth); - // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 - // 一定都会大于等于目标的宽和高。 - inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; - } - // 设置压缩比例 - options.inSampleSize = inSampleSize; - options.inJustDecodeBounds = false; - return options; - } - - /** - * 图片压缩方法:(使用compress的方法)
- * - * 注意 bitmap实际并没有被回收,如果你不需要,请手动置空
- * 说明 如果bitmap本身的大小小于maxSize,则不作处理 - * - * @param bitmap - * 要压缩的图片 - * @param maxSize - * 压缩后的大小,单位kb - */ - public static Bitmap imageZoom(Bitmap bitmap, double maxSize) { - // 将bitmap放至数组中,意在获得bitmap的大小(与实际读取的原文件要大) - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - // 格式、质量、输出流 - bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos); - byte[] b = baos.toByteArray(); - // 将字节换成KB - double mid = b.length / 1024; - // 获取bitmap大小 是允许最大大小的多少倍 - double i = mid / maxSize; - // 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩 - if (i > 1) { - // 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍 - // (保持宽高不变,缩放后也达到了最大占用空间的大小) - bitmap = scaleWithWH(bitmap, bitmap.getWidth() / Math.sqrt(i), - bitmap.getHeight() / Math.sqrt(i)); - } - return bitmap; - } - - /*** - * 图片的缩放方法,如果参数宽高为0,则不处理
- * - * 注意 src实际并没有被回收,如果你不需要,请手动置空 - * - * @param src - * :源图片资源 - * @param w - * :缩放后宽度 - * @param h - * :缩放后高度 - */ - public static Bitmap scaleWithWH(Bitmap src, double w, double h) { - if (w == 0 || h == 0 || src == null) { - return src; - } else { - // 记录src的宽高 - int width = src.getWidth(); - int height = src.getHeight(); - // 创建一个matrix容器 - Matrix matrix = new Matrix(); - // 计算缩放比例 - float scaleWidth = (float) (w / width); - float scaleHeight = (float) (h / height); - // 开始缩放 - matrix.postScale(scaleWidth, scaleHeight); - // 创建缩放后的图片 - return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true); - } - } - - /** - * 图片的缩放方法
- * - * 注意 src实际并没有被回收,如果你不需要,请手动置空 - * - * @param src - * :源图片资源 - * @param scaleMatrix - * :缩放规则 - */ - public static Bitmap scaleWithMatrix(Bitmap src, Matrix scaleMatrix) { - return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), - scaleMatrix, true); - } - - /** - * 图片的缩放方法
- * - * 注意 src实际并没有被回收,如果你不需要,请手动置空 - * - * @param src - * :源图片资源 - * @param scaleX - * :横向缩放比例 - * @param scaleY - * :纵向缩放比例 - */ - public static Bitmap scaleWithXY(Bitmap src, float scaleX, float scaleY) { - Matrix matrix = new Matrix(); - matrix.postScale(scaleX, scaleY); - return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), - matrix, true); - } - - /** - * 图片的缩放方法
- * - * 注意 src实际并没有被回收,如果你不需要,请手动置空 - * - * @param src - * :源图片资源 - * @param scaleXY - * :缩放比例 - */ - public static Bitmap scaleWithXY(Bitmap src, float scaleXY) { - return scaleWithXY(src, scaleXY, scaleXY); - } - - /** - * 旋转图片
- * - * 注意 bitmap实际并没有被回收,如果你不需要,请手动置空 - * - * @param angle - * 旋转角度 - * @param bitmap - * 要旋转的图片 - * @return 旋转后的图片 - */ - public static Bitmap rotate(int angle, Bitmap bitmap) { - Matrix matrix = new Matrix(); - matrix.postRotate(angle); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), - bitmap.getHeight(), matrix, true); - } - - /** - * 回收一个未被回收的Bitmap - * - * @param bitmap - */ - public static void doRecycledIfNot(Bitmap bitmap) { - if (!bitmap.isRecycled()) { - bitmap.recycle(); - } - } -} diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java index b52fa70a..11c681b4 100755 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java @@ -34,7 +34,7 @@ * @author kymjs (https://www.kymjs.com/) */ public class ImageDisplayer { - private final KJHttp mKJHttp; // 使用KJHttp的线程池执行队列去加载图片 + private static KJHttp sKJHttp = new KJHttp(); // 使用KJHttp的线程池执行队列去加载图片 private final ImageCache mMemoryCache; // 内存缓存器 private final long mResponseDelayMs; @@ -59,7 +59,7 @@ public ImageDisplayer(HttpConfig httpConfig, BitmapConfig bitmapConfig) { // 但是这种不利于自定义扩展,就不用了,有兴趣的可以看CacheDispatcher的105行 // @kymjs记录于2015.4.30 // config.cacheTime = bitmapConfig.cacheTime; - mKJHttp = new KJHttp(httpConfig); + sKJHttp.setConfig(httpConfig); mMemoryCache = BitmapConfig.mMemoryCache; mResponseDelayMs = bitmapConfig.delayTime; } @@ -113,8 +113,8 @@ public ImageBale get(String requestUrl, int maxWidth, int maxHeight, Request newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight); - newRequest.setConfig(mKJHttp.getConfig()); - mKJHttp.doRequest(newRequest); + newRequest.setConfig(sKJHttp.getConfig()); + sKJHttp.doRequest(newRequest); mRequestsMap.put(requestUrl, new ImageRequestEven(newRequest, imageBale)); return imageBale; @@ -315,7 +315,7 @@ private void throwIfNotOnMainThread() { * @param url */ public void cancel(String url) { - mKJHttp.cancel(url); + sKJHttp.cancel(url); } /** diff --git a/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java b/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java new file mode 100644 index 00000000..475eaf1a --- /dev/null +++ b/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java @@ -0,0 +1 @@ +/* * Copyright (c) 2015,KJFrameForAndroid Open Source Project,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.utils; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Random; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; /** * 图片工具类 Created by kymjs on 15/9/8. */ public class ImageUtils { /** * 压缩图片 * * @param filePath * 源图片地址 * @param width * 想要的宽度 * @param height * 想要的高度 * @param isAdjust * 是否自动调整尺寸, true图片就不会拉伸,false严格按照你的尺寸压缩 * @return Bitmap */ public static File getSmallImageFile(Context cxt, String filePath, int width, int height, boolean isAdjust) { Bitmap bitmap = reduce(BitmapFactory.decodeFile(filePath), width, height, isAdjust); File file = new File(getRandomFileName(cxt.getCacheDir().getPath())); BufferedOutputStream outputStream = null; try { outputStream = new BufferedOutputStream(new FileOutputStream(file)); bitmap.compress(Bitmap.CompressFormat.JPEG, 60, outputStream); outputStream.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return file; } /*** * 获取一个随机图片文件名,含路径 * * @param filePath * @return */ public static String getRandomFileName(String filePath) { SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault()); Date date = new Date(); String key = format.format(date); Random r = new Random(); key = key + r.nextInt(); key = key.substring(0, 15); return filePath + "/" + key + ".jpeg"; } /** * 压缩图片 * * @param bitmap * 源图片 * @param width * 想要的宽度 * @param height * 想要的高度 * @param isAdjust * 是否自动调整尺寸, true图片就不会拉伸,false严格按照你的尺寸压缩 * @return Bitmap */ public static Bitmap reduce(Bitmap bitmap, int width, int height, boolean isAdjust) { // 如果想要的宽度和高度都比源图片小,就不压缩了,直接返回原图 if (bitmap.getWidth() < width && bitmap.getHeight() < height) { return bitmap; } if (width == 0 && height == 0) { width = bitmap.getWidth(); height = bitmap.getHeight(); } // 根据想要的尺寸精确计算压缩比例, 方法详解:public BigDecimal divide(BigDecimal divisor, // int scale, int // roundingMode); // scale表示要保留的小数位, roundingMode表示如何处理多余的小数位,BigDecimal.ROUND_DOWN表示自动舍弃 float sx = new BigDecimal(width).divide( new BigDecimal(bitmap.getWidth()), 4, BigDecimal.ROUND_DOWN) .floatValue(); float sy = new BigDecimal(height).divide( new BigDecimal(bitmap.getHeight()), 4, BigDecimal.ROUND_DOWN) .floatValue(); if (isAdjust) {// 如果想自动调整比例,不至于图片会拉伸 sx = (sx < sy ? sx : sy); sy = sx;// 哪个比例小一点,就用哪个比例 } Matrix matrix = new Matrix(); matrix.postScale(sx, sy);// 调用api中的方法进行压缩,就大功告成了 return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java b/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java index b1016b98..0ae9ace0 100644 --- a/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java +++ b/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java @@ -21,7 +21,7 @@ */ public final class KJConfig { - public static final double VERSION = 2.245; + public static final double VERSION = 2.2451; /** 错误处理广播 */ public static final String RECEIVER_ERROR = KJConfig.class.getName() From a49260d005a6b1cb590e27d848a648bc5a30cce1 Mon Sep 17 00:00:00 2001 From: kymjs Date: Mon, 28 Sep 2015 14:58:19 +0800 Subject: [PATCH 12/14] 2.2451 --- ....245.jar => KJFrameForAndroid_v2.2451.jar} | Bin 233444 -> 233986 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename binrary/{KJFrameForAndroid_v2.245.jar => KJFrameForAndroid_v2.2451.jar} (92%) diff --git a/binrary/KJFrameForAndroid_v2.245.jar b/binrary/KJFrameForAndroid_v2.2451.jar similarity index 92% rename from binrary/KJFrameForAndroid_v2.245.jar rename to binrary/KJFrameForAndroid_v2.2451.jar index db2ff90f8505cba14207eb35a9ef935ac708dcd7..0dfe7a00f69a45d80695231ab15304d8328c0572 100644 GIT binary patch delta 9732 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 delta 9238 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 From 4ac7a323e409e9e1b48fbe31b371a2ad78d52fdb Mon Sep 17 00:00:00 2001 From: kymjs Date: Fri, 30 Oct 2015 14:24:12 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0Andro?= =?UTF-8?q?idStudio=E7=89=88=E6=9C=AC2.251?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KJFrame/src/org/kymjs/kjframe/KJActivity.java | 202 ++++++++- KJFrame/src/org/kymjs/kjframe/KJBitmap.java | 402 +++++++++--------- KJFrame/src/org/kymjs/kjframe/KJHttp.java | 124 +++++- .../kymjs/kjframe/bitmap/BitmapConfig.java | 2 - .../kymjs/kjframe/bitmap/BitmapCreate.java | 2 +- .../kjframe/bitmap/DiskImageRequest.java | 2 +- .../kymjs/kjframe/bitmap/ImageDisplayer.java | 35 +- .../kymjs/kjframe/bitmap/ImageRequest.java | 38 +- .../kymjs/kjframe/database/CursorHelper.java | 7 +- .../kjframe/database/utils/Property.java | 10 +- KJFrame/src/org/kymjs/kjframe/http/Cache.java | 23 +- .../kymjs/kjframe/http/CacheDispatcher.java | 31 +- .../src/org/kymjs/kjframe/http/Delivery.java | 2 +- .../kymjs/kjframe/http/DeliveryExecutor.java | 23 +- .../kymjs/kjframe/http/DeliveryResponse.java | 17 +- .../src/org/kymjs/kjframe/http/DiskCache.java | 62 ++- .../kjframe/http/DownloadController.java | 18 +- .../kymjs/kjframe/http/DownloadTaskQueue.java | 13 +- .../org/kymjs/kjframe/http/FileRequest.java | 20 +- .../org/kymjs/kjframe/http/FormRequest.java | 13 +- .../org/kymjs/kjframe/http/HttpCallBack.java | 18 +- .../kymjs/kjframe/http/HttpClientStack.java | 163 ------- .../org/kymjs/kjframe/http/HttpConfig.java | 88 ++-- .../kymjs/kjframe/http/HttpConnectStack.java | 79 ++-- .../kymjs/kjframe/http/HttpHeaderParser.java | 21 +- .../org/kymjs/kjframe/http/HttpParams.java | 88 ++-- .../src/org/kymjs/kjframe/http/HttpStack.java | 22 +- .../src/org/kymjs/kjframe/http/HttpUtils.java | 47 +- .../org/kymjs/kjframe/http/KJAsyncTask.java | 2 +- .../kymjs/kjframe/http/KJHttpException.java | 2 + .../src/org/kymjs/kjframe/http/Network.java | 69 ++- .../kymjs/kjframe/http/NetworkDispatcher.java | 14 +- .../kymjs/kjframe/http/NetworkResponse.java | 8 +- .../http/PoolingByteArrayOutputStream.java | 9 +- .../src/org/kymjs/kjframe/http/Request.java | 53 ++- .../src/org/kymjs/kjframe/http/Response.java | 19 +- .../org/kymjs/kjframe/ui/AnnotateUtil.java | 8 +- .../src/org/kymjs/kjframe/ui/BindView.java | 7 +- .../org/kymjs/kjframe/ui/FrameActivity.java | 196 --------- .../org/kymjs/kjframe/ui/FrameFragment.java | 129 ------ .../org/kymjs/kjframe/ui/I_BroadcastReg.java | 4 +- .../org/kymjs/kjframe/ui/I_KJActivity.java | 33 +- .../src/org/kymjs/kjframe/ui/KJFragment.java | 136 ++++-- .../org/kymjs/kjframe/ui/SupportFragment.java | 2 +- .../src/org/kymjs/kjframe/ui/ViewInject.java | 42 +- .../org/kymjs/kjframe/utils/ImageUtils.java | 128 +++++- .../src/org/kymjs/kjframe/utils/KJConfig.java | 15 +- .../kymjs/kjframe/widget/AdapterHolder.java | 6 +- .../org/kymjs/kjframe/widget/KJAdapter.java | 26 +- .../kymjs/kjframe/widget/KJScrollView.java | 1 + .../kymjs/kjframe/widget/ScaleImageView.java | 391 ----------------- .../res/layout/aty_kjlistview.xml | 24 +- KJLibraryExample/res/layout/aty_main.xml | 62 +-- .../res/layout/aty_new_listview.xml | 54 +-- KJLibraryExample/res/layout/example_db.xml | 106 ++--- KJLibraryExample/res/layout/frag_plugin.xml | 32 +- .../res/layout/layout_main_content.xml | 38 +- KJLibraryExample/res/layout/plugin_item.xml | 42 +- KJLibraryExample/res/layout/scale_img.xml | 50 +-- KJLibraryExample/res/values-v11/styles.xml | 20 +- KJLibraryExample/res/values-v14/styles.xml | 20 +- .../org/kymjs/kjframe/demo/DBActivity.java | 246 +++++------ .../kymjs/kjframe/demo/WidgetActivity.java | 9 +- .../kymjs/kjframe/demo/bean/PluginBean.java | 68 +-- .../src/org/kymjs/kjframe/demo/bean/User.java | 98 ++--- .../kjframe/demo/widget/ScaleImageDemo.java | 1 - 66 files changed, 1657 insertions(+), 2085 deletions(-) delete mode 100755 KJFrame/src/org/kymjs/kjframe/http/HttpClientStack.java delete mode 100644 KJFrame/src/org/kymjs/kjframe/ui/FrameActivity.java delete mode 100644 KJFrame/src/org/kymjs/kjframe/ui/FrameFragment.java delete mode 100644 KJFrame/src/org/kymjs/kjframe/widget/ScaleImageView.java delete mode 100644 KJLibraryExample/src/org/kymjs/kjframe/demo/widget/ScaleImageDemo.java diff --git a/KJFrame/src/org/kymjs/kjframe/KJActivity.java b/KJFrame/src/org/kymjs/kjframe/KJActivity.java index 6236da62..aa6c7fda 100644 --- a/KJFrame/src/org/kymjs/kjframe/KJActivity.java +++ b/KJFrame/src/org/kymjs/kjframe/KJActivity.java @@ -15,40 +15,154 @@ */ package org.kymjs.kjframe; -import org.kymjs.kjframe.ui.FrameActivity; +import java.lang.ref.SoftReference; + +import org.kymjs.kjframe.ui.AnnotateUtil; +import org.kymjs.kjframe.ui.I_BroadcastReg; +import org.kymjs.kjframe.ui.I_KJActivity; +import org.kymjs.kjframe.ui.I_SkipActivity; import org.kymjs.kjframe.ui.KJActivityStack; +import org.kymjs.kjframe.ui.KJFragment; +import org.kymjs.kjframe.ui.SupportFragment; import org.kymjs.kjframe.utils.KJLoger; import android.app.Activity; +import android.app.FragmentTransaction; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.FragmentActivity; +import android.view.View; /** + * Activity's framework,the developer shouldn't extends it
+ * 创建时间 2014-3-1
+ * 最后修改时间 2014-10-17
+ * * @author kymjs (https://github.com/kymjs) + * @version 1.8 */ -public abstract class KJActivity extends FrameActivity { +public abstract class KJActivity extends FragmentActivity implements + View.OnClickListener, I_BroadcastReg, I_KJActivity, I_SkipActivity { + + public static final int WHICH_MSG = 0X37210; + + public Activity aty; + + protected KJFragment currentKJFragment; + protected SupportFragment currentSupportFragment; + private ThreadDataCallBack callback; + private final KJActivityHandle threadHandle = new KJActivityHandle(this); + + /** + * Activity状态 + */ + public int activityState = DESTROY; /** - * 当前Activity状态 + * 一个私有回调类,线程中初始化数据完成后的回调 */ - public static enum ActivityState { - RESUME, PAUSE, STOP, DESTROY + private interface ThreadDataCallBack { + void onSuccess(); } - public Activity aty; - /** Activity状态 */ - public ActivityState activityState = ActivityState.DESTROY; + private static class KJActivityHandle extends Handler { + private final SoftReference mOuterInstance; + + KJActivityHandle(KJActivity outer) { + mOuterInstance = new SoftReference(outer); + } + + // 当线程中初始化的数据初始化完成后,调用回调方法 + @Override + public void handleMessage(android.os.Message msg) { + if (msg.what == WHICH_MSG && mOuterInstance.get() != null) { + mOuterInstance.get().callback.onSuccess(); + } + } + } + + /** + * 如果调用了initDataFromThread(),则当数据初始化完成后将回调该方法。 + */ + protected void threadDataInited() {} + + /** + * 在线程中初始化数据,注意不能在这里执行UI操作 + */ + @Override + public void initDataFromThread() { + callback = new ThreadDataCallBack() { + @Override + public void onSuccess() { + threadDataInited(); + } + }; + } + + @Override + public void initData() {} + + @Override + public void initWidget() {} + + // 仅仅是为了代码整洁点 + private void initializer() { + new Thread(new Runnable() { + @Override + public void run() { + initDataFromThread(); + threadHandle.sendEmptyMessage(WHICH_MSG); + } + }).start(); + initData(); + initWidget(); + } + + /** + * listened widget's click method + */ + @Override + public void widgetClick(View v) {} + + @Override + public void onClick(View v) { + widgetClick(v); + } + + @SuppressWarnings("unchecked") + protected T bindView(int id) { + return (T) findViewById(id); + } + + @SuppressWarnings("unchecked") + protected T bindView(int id, boolean click) { + T view = (T) findViewById(id); + if (click) { + view.setOnClickListener(this); + } + return view; + } + + @Override + public void registerBroadcast() {} + + @Override + public void unRegisterBroadcast() {} /*************************************************************************** - * * print Activity callback methods - * ***************************************************************************/ @Override protected void onCreate(Bundle savedInstanceState) { aty = this; KJActivityStack.create().addActivity(this); KJLoger.state(this.getClass().getName(), "---------onCreat "); + + setRootView(); // 必须放在annotate之前调用 + AnnotateUtil.initBindView(this); + initializer(); + registerBroadcast(); super.onCreate(savedInstanceState); } @@ -61,21 +175,21 @@ protected void onStart() { @Override protected void onResume() { super.onResume(); - activityState = ActivityState.RESUME; + activityState = RESUME; KJLoger.state(this.getClass().getName(), "---------onResume "); } @Override protected void onPause() { super.onPause(); - activityState = ActivityState.PAUSE; + activityState = PAUSE; KJLoger.state(this.getClass().getName(), "---------onPause "); } @Override protected void onStop() { super.onStop(); - activityState = ActivityState.STOP; + activityState = STOP; KJLoger.state(this.getClass().getName(), "---------onStop "); } @@ -87,7 +201,8 @@ protected void onRestart() { @Override protected void onDestroy() { - activityState = ActivityState.DESTROY; + unRegisterBroadcast(); + activityState = DESTROY; KJLoger.state(this.getClass().getName(), "---------onDestroy "); super.onDestroy(); KJActivityStack.create().finishActivity(this); @@ -148,4 +263,63 @@ public void showActivity(Activity aty, Class cls, Bundle extras) { intent.setClass(aty, cls); aty.startActivity(intent); } + + /** + * 用Fragment替换视图 + * + * @param resView + * 将要被替换掉的视图 + * @param targetFragment + * 用来替换的Fragment + */ + public void changeFragment(int resView, KJFragment targetFragment) { + if (targetFragment.equals(currentKJFragment)) { + return; + } + FragmentTransaction transaction = getFragmentManager() + .beginTransaction(); + if (!targetFragment.isAdded()) { + transaction.add(resView, targetFragment, targetFragment.getClass() + .getName()); + } + if (targetFragment.isHidden()) { + transaction.show(targetFragment); + targetFragment.onChange(); + } + if (currentKJFragment != null && currentKJFragment.isVisible()) { + transaction.hide(currentKJFragment); + } + currentKJFragment = targetFragment; + transaction.commit(); + } + + /** + * 用Fragment替换视图 + * + * @param resView + * 将要被替换掉的视图 + * @param targetFragment + * 用来替换的Fragment + */ + public void changeFragment(int resView, SupportFragment targetFragment) { + if (targetFragment.equals(currentSupportFragment)) { + return; + } + android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager() + .beginTransaction(); + if (!targetFragment.isAdded()) { + transaction.add(resView, targetFragment, targetFragment.getClass() + .getName()); + } + if (targetFragment.isHidden()) { + transaction.show(targetFragment); + targetFragment.onChange(); + } + if (currentSupportFragment != null + && currentSupportFragment.isVisible()) { + transaction.hide(currentSupportFragment); + } + currentSupportFragment = targetFragment; + transaction.commit(); + } } diff --git a/KJFrame/src/org/kymjs/kjframe/KJBitmap.java b/KJFrame/src/org/kymjs/kjframe/KJBitmap.java index e3dc218a..8f8506b5 100644 --- a/KJFrame/src/org/kymjs/kjframe/KJBitmap.java +++ b/KJFrame/src/org/kymjs/kjframe/KJBitmap.java @@ -15,13 +15,18 @@ */ package org.kymjs.kjframe; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import java.util.Vector; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.provider.MediaStore; +import android.view.View; +import android.widget.ImageView; import org.kymjs.kjframe.bitmap.BitmapCallBack; import org.kymjs.kjframe.bitmap.BitmapConfig; @@ -36,24 +41,17 @@ import org.kymjs.kjframe.utils.StringUtils; import org.kymjs.kjframe.utils.SystemTool; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.provider.MediaStore; -import android.view.View; -import android.widget.ImageView; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; /** * The BitmapLibrary's core classes
* 创建时间 2014-6-11
- * 最后修改 2015-4-23
- * + * 最后修改 2015-9-25
+ * * @author kymjs (https://github.com/kymjs) * @version 2.4 */ @@ -62,8 +60,6 @@ public class KJBitmap { private final BitmapConfig mConfig; private final ImageDisplayer displayer; - private final List doLoadingViews; - public KJBitmap() { this(new BitmapConfig()); } @@ -73,62 +69,122 @@ public KJBitmap(BitmapConfig bitmapConfig) { } public KJBitmap(HttpConfig httpConfig, BitmapConfig bitmapConfig) { + if (httpConfig == null) { + httpConfig = new HttpConfig(); + } + if (bitmapConfig == null) { + bitmapConfig = new BitmapConfig(); + } this.mConfig = bitmapConfig; displayer = new ImageDisplayer(httpConfig, mConfig); - doLoadingViews = new Vector(30); + } + + + public static class Builder { + private View imageView; + private String imageUrl; + private int width; + private int height; + private Drawable loadBitmap; + private Drawable errorBitmap; + private BitmapCallBack callback; + private BitmapConfig bitmapConfig; + private HttpConfig httpConfig; + + public Builder bitmapConfig(BitmapConfig bitmapConfig) { + this.bitmapConfig = bitmapConfig; + return this; + } + + public Builder httpConfig(HttpConfig httpConfig) { + this.httpConfig = httpConfig; + return this; + } + + public Builder view(View imageView) { + this.imageView = imageView; + return this; + } + + public Builder imageUrl(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + public Builder width(int width) { + this.width = width; + return this; + } + + public Builder height(int height) { + this.height = height; + return this; + } + + public Builder loadBitmap(Drawable loadBitmap) { + this.loadBitmap = loadBitmap; + return this; + } + + public Builder errorBitmap(Drawable errorBitmap) { + this.errorBitmap = errorBitmap; + return this; + } + + public Builder callback(BitmapCallBack callback) { + this.callback = callback; + return this; + } + + public void display() { + display(new KJBitmap(httpConfig, bitmapConfig)); + } + + public void display(KJBitmap kjb) { + kjb.display(imageView, imageUrl, width, height, loadBitmap, errorBitmap, callback); + } } /** * 使用默认配置加载网络图片(屏幕的一半显示图片) - * - * @param imageView - * 要显示图片的控件(ImageView设置src,普通View设置bg) - * @param imageUrl - * 图片的URL + * + * @param imageView 要显示图片的控件(ImageView设置src,普通View设置bg) + * @param imageUrl 图片的URL */ public void display(View imageView, String imageUrl) { displayWithDefWH(imageView, imageUrl, null, null, null); } /** - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param width - * 要显示的图片的最大宽度 - * @param height - * 要显示图片的最大高度 + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param width 要显示的图片的最大宽度 + * @param height 要显示图片的最大高度 */ + @Deprecated public void display(View imageView, String imageUrl, int width, int height) { display(imageView, imageUrl, width, height, null, null, null); } /** - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param callback - * 加载过程的回调 + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param callback 加载过程的回调 */ + @Deprecated public void display(View imageView, String imageUrl, BitmapCallBack callback) { displayWithDefWH(imageView, imageUrl, null, null, callback); } /** * 如果内存缓存有图片,则显示内存缓存的图片,否则显示默认图片 - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param defaultImage - * 如果没有内存缓存,则显示默认图片 + * + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param defaultImage 如果没有内存缓存,则显示默认图片 */ public void displayCacheOrDefult(View imageView, String imageUrl, - int defaultImage) { + int defaultImage) { Bitmap cache = getMemoryCache(imageUrl); if (cache == null) { setViewImage(imageView, defaultImage); @@ -138,64 +194,68 @@ public void displayCacheOrDefult(View imageView, String imageUrl, } /** - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param width - * 要显示的图片的最大宽度 - * @param height - * 要显示图片的最大高度 - * @param loadBitmap - * 加载中图片 + * 如果内存缓存有图片,则显示内存缓存的图片,否则显示默认图片 + * + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param defaultImage 如果没有内存缓存,则显示默认图片 */ + public void displayCacheOrDefult(View imageView, String imageUrl, + Drawable defaultImage) { + Bitmap cache = getMemoryCache(imageUrl); + if (cache == null) { + setViewImage(imageView, defaultImage); + } else { + setViewImage(imageView, cache); + } + } + + /** + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param width 要显示的图片的最大宽度 + * @param height 要显示图片的最大高度 + * @param loadBitmap 加载中图片 + */ + @Deprecated public void display(View imageView, String imageUrl, int width, int height, - int loadBitmap) { + int loadBitmap) { display(imageView, imageUrl, width, height, imageView.getResources() .getDrawable(loadBitmap), null, null); } /** - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param loadBitmap - * 加载中的图片 + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param loadBitmap 加载中的图片 */ + @Deprecated public void displayWithLoadBitmap(View imageView, String imageUrl, - int loadBitmap) { + int loadBitmap) { displayWithDefWH(imageView, imageUrl, imageView.getResources() .getDrawable(loadBitmap), null, null); } /** - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param errorBitmap - * 加载出错时设置的默认图片 + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param errorBitmap 加载出错时设置的默认图片 */ + @Deprecated public void displayWithErrorBitmap(View imageView, String imageUrl, - int errorBitmap) { + int errorBitmap) { displayWithDefWH(imageView, imageUrl, null, imageView.getResources() .getDrawable(errorBitmap), null); } /** - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param errorBitmap - * 加载出错时设置的默认图片 + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param errorBitmap 加载出错时设置的默认图片 */ + @Deprecated public void displayLoadAndErrorBitmap(View imageView, String imageUrl, - int loadBitmap, int errorBitmap) { + int loadBitmap, int errorBitmap) { Resources res = imageView.getResources(); displayWithDefWH(imageView, imageUrl, res.getDrawable(loadBitmap), res.getDrawable(errorBitmap), null); @@ -203,21 +263,19 @@ public void displayLoadAndErrorBitmap(View imageView, String imageUrl, /** * 如果不指定宽高,则使用默认宽高计算方法 - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param loadBitmap - * 加载中图片 - * @param errorBitmap - * 加载失败的图片 - * @param callback - * 加载过程的回调 + * + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param loadBitmap 加载中图片 + * @param errorBitmap 加载失败的图片 + * @param callback 加载过程的回调 */ + @Deprecated public void displayWithDefWH(View imageView, String imageUrl, - Drawable loadBitmap, Drawable errorBitmap, BitmapCallBack callback) { - int w = imageView.getWidth();; + Drawable loadBitmap, Drawable errorBitmap, BitmapCallBack + callback) { + int w = imageView.getWidth(); + ; int h = imageView.getHeight(); if (w == 0) { w = DensityUtils.getScreenW(imageView.getContext()) / 2; @@ -229,21 +287,16 @@ public void displayWithDefWH(View imageView, String imageUrl, } /** - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param width - * 要显示的图片的最大宽度 - * @param height - * 要显示图片的最大高度 - * @param loadOrErrorBitmap - * 加载中或加载失败都显示这张图片 - * @param callback - * 加载过程的回调 + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param width 要显示的图片的最大宽度 + * @param height 要显示图片的最大高度 + * @param loadOrErrorBitmap 加载中或加载失败都显示这张图片 + * @param callback 加载过程的回调 */ + @Deprecated public void display(View imageView, String imageUrl, int loadOrErrorBitmap, - int width, int height, BitmapCallBack callback) { + int width, int height, BitmapCallBack callback) { display(imageView, imageUrl, width, height, imageView.getResources() .getDrawable(loadOrErrorBitmap), imageView.getResources() .getDrawable(loadOrErrorBitmap), callback); @@ -251,24 +304,18 @@ public void display(View imageView, String imageUrl, int loadOrErrorBitmap, /** * 显示网络图片(core) - * - * @param imageView - * 要显示的View - * @param imageUrl - * 网络图片地址 - * @param width - * 要显示的图片的最大宽度 - * @param height - * 要显示图片的最大高度 - * @param loadBitmap - * 加载中图片 - * @param errorBitmap - * 加载失败的图片 - * @param callback - * 加载过程的回调 + * + * @param imageView 要显示的View + * @param imageUrl 网络图片地址 + * @param width 要显示的图片的最大宽度 + * @param height 要显示图片的最大高度 + * @param loadBitmap 加载中图片 + * @param errorBitmap 加载失败的图片 + * @param callback 加载过程的回调 */ + @Deprecated public void display(View imageView, String imageUrl, int width, int height, - Drawable loadBitmap, Drawable errorBitmap, BitmapCallBack callback) { + Drawable loadBitmap, Drawable errorBitmap, BitmapCallBack callback) { if (imageView == null) { showLogIfOpen("imageview is null"); return; @@ -279,6 +326,9 @@ public void display(View imageView, String imageUrl, int width, int height, if (StringUtils.isEmpty(imageUrl)) { showLogIfOpen("image url is empty"); setViewImage(imageView, errorBitmap); + if (callback != null) { + callback.onFailure(new RuntimeException("image url is empty")); + } return; } @@ -293,15 +343,14 @@ public void display(View imageView, String imageUrl, int width, int height, * 真正去加载一个图片 */ private void doDisplay(final View imageView, final String imageUrl, - int width, int height, final Drawable loadBitmap, - final Drawable errorBitmap, final BitmapCallBack callback) { - checkViewExist(imageView); - + int width, int height, final Drawable loadBitmap, + final Drawable errorBitmap, final BitmapCallBack callback) { imageView.setTag(imageUrl); BitmapCallBack mCallback = new BitmapCallBack() { @Override public void onPreLoad() { + setViewImage(imageView, loadBitmap); if (callback != null) { callback.onPreLoad(); } @@ -327,10 +376,6 @@ public void onFailure(Exception e) { @Override public void onFinish() { - try { - doLoadingViews.remove(imageView); - } catch (Exception e) { - } if (callback != null) { callback.onFinish(); } @@ -348,7 +393,7 @@ public void onDoHttp() { if (imageUrl.startsWith("http")) { displayer.get(imageUrl, width, height, mCallback); } else { - new DiskImageRequest().load(imageUrl, width, width, mCallback); + new DiskImageRequest().load(imageUrl, width, height, mCallback); } } @@ -372,9 +417,8 @@ private void doSuccess(View view, Bitmap bitmap, Drawable defaultImage) { /** * 移除一个缓存 - * - * @param url - * 哪条url的缓存 + * + * @param url 哪条url的缓存 */ public void removeCache(String url) { BitmapConfig.mMemoryCache.remove(url); @@ -391,10 +435,9 @@ public void cleanCache() { /** * 获取缓存数据 - * - * @param url - * 哪条url的缓存 - * @return + * + * @param url 哪条url的缓存 + * @return 缓存数据的二进制数组 */ public byte[] getCache(String url) { Cache cache = HttpConfig.mCache; @@ -409,28 +452,18 @@ public byte[] getCache(String url) { /** * 获取内存缓存 - * - * @param url - * @return + * + * @param url key + * @return 缓存的bitmap或null */ public Bitmap getMemoryCache(String url) { return BitmapConfig.mMemoryCache.getBitmap(url); } - /** - * 取消一个加载请求(拼写错误,请使用cancel(url)) - * - * @param url - */ - @Deprecated - public void cancle(String url) { - displayer.cancel(url); - } - /** * 取消一个加载请求 - * - * @param url + * + * @param url 要取消的url */ public void cancel(String url) { displayer.cancel(url); @@ -438,12 +471,9 @@ public void cancel(String url) { /** * 保存一张图片到本地,并自动通知图库刷新 - * - * @param cxt - * @param url - * 网络图片链接 - * @param path - * 保存到本地的绝对路径 + * + * @param url 网络图片链接 + * @param path 保存到本地的绝对路径 */ public void saveImage(Context cxt, String url, String path) { saveImage(cxt, url, path, true, null); @@ -451,16 +481,13 @@ public void saveImage(Context cxt, String url, String path) { /** * 保存一张图片到本地 - * - * @param url - * 网络图片链接 - * @param path - * 保存到本地的绝对路径 - * @param cb - * 保存过程监听器 + * + * @param url 网络图片链接 + * @param path 保存到本地的绝对路径 + * @param cb 保存过程监听器 */ public void saveImage(final Context cxt, String url, final String path, - final boolean isRefresh, HttpCallBack cb) { + final boolean isRefresh, HttpCallBack cb) { if (cb == null) { cb = new HttpCallBack() { @Override @@ -509,13 +536,11 @@ public void onSuccess(byte[] t) { /** * 刷新图库 - * - * @param cxt - * @param path + * + * @param path 要刷新的文件的绝对路径 */ public void refresh(Context cxt, String path) { - String name = ""; - name = path.substring(path.lastIndexOf('/')); + String name = path.substring(path.lastIndexOf('/')); try { MediaStore.Images.Media.insertImage(cxt.getContentResolver(), path, name, null); @@ -527,7 +552,9 @@ public void refresh(Context cxt, String path) { .parse("file://" + path))); } - /********************* private method *********************/ + /********************* + * private method + *********************/ @SuppressLint("NewApi") @SuppressWarnings("deprecation") @@ -572,19 +599,4 @@ private void showLogIfOpen(String msg) { KJLoger.debugLog(getClass().getSimpleName(), msg); } } - - /** - * 检测一个View是否已经有任务了,如果是,则取消之前的任务 - * - * @param view - */ - private void checkViewExist(View view) { - if (doLoadingViews.contains(view)) { - String url = (String) view.getTag(); - if (!StringUtils.isEmpty(url)) { - cancle(url); - } - } - doLoadingViews.add(view); - } } diff --git a/KJFrame/src/org/kymjs/kjframe/KJHttp.java b/KJFrame/src/org/kymjs/kjframe/KJHttp.java index f5c796e7..524ada5d 100755 --- a/KJFrame/src/org/kymjs/kjframe/KJHttp.java +++ b/KJFrame/src/org/kymjs/kjframe/KJHttp.java @@ -26,7 +26,6 @@ import org.kymjs.kjframe.http.Cache; import org.kymjs.kjframe.http.CacheDispatcher; -import org.kymjs.kjframe.http.Delivery; import org.kymjs.kjframe.http.DownloadController; import org.kymjs.kjframe.http.DownloadTaskQueue; import org.kymjs.kjframe.http.FileRequest; @@ -44,21 +43,25 @@ * 本类工作流程: 每当发起一次Request,会对这个Request标记一个唯一值。
* 并加入当前请求的Set中(保证唯一;方便控制)。
* 同时判断是否启用缓存,若启用则加入缓存队列,否则加入执行队列。
- * * Note:
* 整个KJHttp工作流程:采用责任链设计模式,由三部分组成,类似设计可以类比Handle...Looper...MessageQueue
- * * 1、KJHttp负责不停向NetworkQueue(或CacheQueue实际还是NetworkQueue, 具体逻辑请查看 * {@link CacheDispatcher})添加Request
* 2、另一边由TaskThread不停从NetworkQueue中取Request并交给Network执行器(逻辑请查看 * {@link NetworkDispatcher} ),
* 3、Network执行器将执行成功的NetworkResponse返回给TaskThead,并通过Request的定制方法 - * Request.parseNetworkResponse()封装成Response,最终交给分发器 {@link Delivery} + * Request.parseNetworkResponse()封装成Response,最终交给分发器 Delivery * 分发到主线程并调用HttpCallback相应的方法 * * @author kymjs (https://www.kymjs.com/) */ public class KJHttp { + + public interface ContentType { + int FORM = 0; + int JSON = 1; + } + // 请求缓冲区 private final Map>> mWaitingRequests = new HashMap>>(); // 请求的序列化生成器 @@ -77,16 +80,100 @@ public class KJHttp { private HttpConfig mConfig; public KJHttp() { - this(new HttpConfig()); + this(null); } public KJHttp(HttpConfig config) { + if (config == null) { + config = new HttpConfig(); + } this.mConfig = config; mConfig.mController.setRequestQueue(this); mTaskThreads = new NetworkDispatcher[HttpConfig.NETWORK_POOL_SIZE]; start(); } + public static class Builder { + private String url; + private HttpCallBack callBack; + private HttpParams params; + private boolean useCache; + private int httpMethod; + private int contentType; + private HttpConfig httpConfig; + + public Builder config(HttpConfig httpConfig) { + this.httpConfig = httpConfig; + return this; + } + + public Builder url(String url) { + this.url = url; + return this; + } + + public Builder callback(HttpCallBack callBack) { + this.callBack = callBack; + return this; + } + + public Builder params(HttpParams params) { + this.params = params; + return this; + } + + public Builder useCache(boolean useCache) { + this.useCache = useCache; + return this; + } + + public Builder httpMethod(int method) { + this.httpMethod = method; + return this; + } + + public Builder contentType(int contentType) { + this.contentType = contentType; + return this; + } + + public void request() { + request(new KJHttp(httpConfig)); + } + + public void request(KJHttp kjHttp) { + switch (httpMethod) { + case HttpMethod.GET: + if (contentType == ContentType.FORM) { + kjHttp.get(url, params, useCache, callBack); + } else if (contentType == ContentType.JSON) { + kjHttp.jsonGet(url, params, useCache, callBack); + } + break; + case HttpMethod.POST: + if (contentType == ContentType.FORM) { + kjHttp.post(url, params, useCache, callBack); + } else if (contentType == ContentType.JSON) { + kjHttp.jsonPost(url, params, useCache, callBack); + } + break; + default: + if (contentType == ContentType.FORM) { + FormRequest request = new FormRequest(httpMethod, url, + params, callBack); + request.setShouldCache(useCache); + kjHttp.doRequest(request); + } else if (contentType == ContentType.JSON) { + JsonRequest request = new JsonRequest(httpMethod, url, + params, callBack); + request.setShouldCache(useCache); + kjHttp.doRequest(request); + } + break; + } + } + } + /** * 发起get请求 * @@ -96,7 +183,7 @@ public KJHttp(HttpConfig config) { * 请求中的回调方法 */ public Request get(String url, HttpCallBack callback) { - return get(url, new HttpParams(), callback); + return get(url, null, callback); } /** @@ -111,6 +198,8 @@ public Request get(String url, HttpCallBack callback) { */ public Request get(String url, HttpParams params, HttpCallBack callback) { + if (params == null) + params = new HttpParams(); return get(url, params, true, callback); } @@ -286,7 +375,7 @@ public void resumeTask(String storeFilePath, String url) { /** * 返回下载总控制器 * - * @return + * @return 下载控制器 */ public DownloadController getDownloadController(String storeFilePath, String url) { @@ -301,6 +390,7 @@ public void cancleAll() { * 执行一个自定义请求 * * @param request + * 要执行的自定义请求 */ public void doRequest(Request request) { request.setConfig(mConfig); @@ -312,7 +402,7 @@ public void doRequest(Request request) { * * @param url * 哪条url的缓存 - * @return + * @return 缓存的二进制数组 */ public byte[] getCache(String url) { Cache cache = HttpConfig.mCache; @@ -358,9 +448,6 @@ public String getStringCache(String url, HttpParams params) { /** * 只有你确定cache是一个String时才可以使用这个方法,否则还是应该使用getCache(String); - * - * @param url - * @return */ public String getStringCache(String url) { return new String(getCache(url)); @@ -387,14 +474,6 @@ public HttpConfig getConfig() { return mConfig; } - /** - * 已过期,请更换为setConfig() - */ - @Deprecated - public void setHttpConfig(HttpConfig config) { - setConfig(config); - } - public void setConfig(HttpConfig config) { this.mConfig = config; } @@ -425,11 +504,12 @@ private void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } - for (int i = 0; i < mTaskThreads.length; i++) { - if (mTaskThreads[i] != null) { - mTaskThreads[i].quit(); + for (NetworkDispatcher thread : mTaskThreads) { + if (thread != null) { + thread.quit(); } } + } public void cancel(String url) { diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapConfig.java b/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapConfig.java index 1935d1f8..3068eaf5 100644 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapConfig.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapConfig.java @@ -33,8 +33,6 @@ public class BitmapConfig { @Deprecated public static String CACHEPATH = HttpConfig.CACHEPATH; - /** 磁盘缓存大小 */ - public static int DISK_CACHE_SIZE = 10 * 1024 * 1024; /** 磁盘缓存器 **/ public static ImageCache mMemoryCache; diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapCreate.java b/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapCreate.java index 09db7337..b7c4df2d 100644 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapCreate.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/BitmapCreate.java @@ -1 +1 @@ -/* * Copyright (c) 2014,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.bitmap; import java.io.InputStream; import org.kymjs.kjframe.utils.FileUtils; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Rect; /** * 不会发生OOM的 BitmapFactory
*

* 创建时间 2014-7-11 * * @author kymjs (https://github.com/kymjs) * @version 1.1 */ public class BitmapCreate { /** * 获取一个指定大小的bitmap * * @param res * Resources * @param resId * 图片ID * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inJustDecodeBounds = true; // BitmapFactory.decodeResource(res, resId, options); // options = BitmapHelper.calculateInSampleSize(options, reqWidth, // reqHeight); // return BitmapFactory.decodeResource(res, resId, options); // 通过JNI的形式读取本地图片达到节省内存的目的 InputStream is = res.openRawResource(resId); return bitmapFromStream(is, null, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromFile(String pathName, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeFile(pathName); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeFile(pathName, options); } /** * 获取一个指定大小的bitmap * * @param data * Bitmap的byte数组 * @param offset * image从byte数组创建的起始位置 * @param length * the number of bytes, 从offset处开始的长度 * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromByteArray(byte[] data, int offset, int length, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeByteArray(data, offset, length); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeByteArray(data, offset, length, options); options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeByteArray(data, offset, length, options); } /** * 获取一个指定大小的bitmap
* 实际调用的方法是bitmapFromByteArray(data, 0, data.length, w, h); * * @param is * 从输入流中读取Bitmap * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } byte[] data = FileUtils.input2byte(is); return bitmapFromByteArray(data, 0, data.length, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param is * 从输入流中读取Bitmap * @param outPadding * If not null, return the padding rect for the bitmap if it * exists, otherwise set padding to [-1,-1,-1,-1]. If no bitmap * is returned (null) then padding is unchanged. * @param reqWidth * 目标宽度 * @param reqHeight * 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, Rect outPadding, int reqWidth, int reqHeight) { Bitmap bmp = null; if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeStream(is, outPadding, options); options = calculateInSampleSize(options, reqWidth, reqHeight); bmp = BitmapFactory.decodeStream(is, outPadding, options); return bmp; } /** * 图片压缩处理(使用Options的方法) *

*
* 说明 使用方法: * 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。 * 然后将Options连同期望的宽度和高度一起传递到到本方法中。 * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。 *

*
* 说明 BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存 * ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数 * ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存 * ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、 * outHeight和outMimeType属性都会被赋值。 * * @param reqWidth * 目标宽度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 * @param reqHeight * 目标高度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 */ public static BitmapFactory.Options calculateInSampleSize( final BitmapFactory.Options options, final int reqWidth, final int reqHeight) { // 源图片的高度和宽度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 设置压缩比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; } } \ No newline at end of file +/* * Copyright (c) 2014,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.bitmap; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Rect; import org.kymjs.kjframe.utils.FileUtils; import java.io.InputStream; /** * 不会发生OOM的 BitmapFactory
* 创建时间 2014-7-11 * * @author kymjs (https://github.com/kymjs) * @version 1.1 */ public class BitmapCreate { /** * 获取一个指定大小的bitmap * * @param res Resources * @param resId 图片ID * @param reqWidth 目标宽度 * @param reqHeight 目标高度 */ public static Bitmap bitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inJustDecodeBounds = true; // BitmapFactory.decodeResource(res, resId, options); // options = BitmapHelper.calculateInSampleSize(options, reqWidth, // reqHeight); // return BitmapFactory.decodeResource(res, resId, options); // 通过JNI的形式读取本地图片达到节省内存的目的 InputStream is = res.openRawResource(resId); return bitmapFromStream(is, null, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param reqWidth 目标宽度 * @param reqHeight 目标高度 */ public static Bitmap bitmapFromFile(String pathName, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeFile(pathName); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeFile(pathName, options); } /** * 获取一个指定大小的bitmap * * @param data Bitmap的byte数组 * @param offset image从byte数组创建的起始位置 * @param length the number of bytes, 从offset处开始的长度 * @param reqWidth 目标宽度 * @param reqHeight 目标高度 */ public static Bitmap bitmapFromByteArray(byte[] data, int offset, int length, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeByteArray(data, offset, length); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeByteArray(data, offset, length, options); options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeByteArray(data, offset, length, options); } /** * 获取一个指定大小的bitmap
* 实际调用的方法是bitmapFromByteArray(data, 0, data.length, w, h); * * @param is 从输入流中读取Bitmap * @param reqWidth 目标宽度 * @param reqHeight 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, int reqWidth, int reqHeight) { if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } byte[] data = FileUtils.input2byte(is); return bitmapFromByteArray(data, 0, data.length, reqWidth, reqHeight); } /** * 获取一个指定大小的bitmap * * @param is 从输入流中读取Bitmap * @param outPadding If not null, return the padding rect for the bitmap if it * exists, otherwise set padding to [-1,-1,-1,-1]. If no bitmap * is returned (null) then padding is unchanged. * @param reqWidth 目标宽度 * @param reqHeight 目标高度 */ public static Bitmap bitmapFromStream(InputStream is, Rect outPadding, int reqWidth, int reqHeight) { Bitmap bmp = null; if (reqHeight == 0 || reqWidth == 0) { try { return BitmapFactory.decodeStream(is); } catch (OutOfMemoryError e) { } } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPurgeable = true; BitmapFactory.decodeStream(is, outPadding, options); options = calculateInSampleSize(options, reqWidth, reqHeight); bmp = BitmapFactory.decodeStream(is, outPadding, options); return bmp; } /** * 图片压缩处理(使用Options的方法) *
* 说明 使用方法: * 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。 * 然后将Options连同期望的宽度和高度一起传递到到本方法中。 * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。 *
* 说明 BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存 * ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数 * ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存 * ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、 * outHeight和outMimeType属性都会被赋值。 * * @param reqWidth 目标宽度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 * @param reqHeight 目标高度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 */ public static BitmapFactory.Options calculateInSampleSize( final BitmapFactory.Options options, final int reqWidth, final int reqHeight) { // 源图片的高度和宽度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 设置压缩比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; } } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/DiskImageRequest.java b/KJFrame/src/org/kymjs/kjframe/bitmap/DiskImageRequest.java index a8e91034..b019b715 100644 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/DiskImageRequest.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/DiskImageRequest.java @@ -1 +1 @@ -package org.kymjs.kjframe.bitmap; import java.io.FileInputStream; import java.io.FileNotFoundException; import org.kymjs.kjframe.http.KJAsyncTask; import org.kymjs.kjframe.utils.FileUtils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Looper; /** * 本地图片加载 * * @author kymjs (https://www.kymjs.com/) */ public class DiskImageRequest { private final Handler handle = new Handler(Looper.getMainLooper()); private String mPath; class DiskImageRequestTask extends KJAsyncTask { private final int mMaxWidth; private final int mMaxHeight; private final BitmapCallBack mCallback; public DiskImageRequestTask(int maxWidth, int maxHeight, BitmapCallBack callback) { mMaxHeight = maxHeight; mMaxWidth = maxWidth; mCallback = callback; } @Override protected void onPreExecute() { super.onPreExecute(); if (mCallback != null) { mCallback.onPreLoad(); } } @Override protected byte[] doInBackground(Void... params) { return loadFromFile(mPath, mMaxWidth, mMaxHeight, mCallback); } } public void load(String path, int maxWidth, int maxHeight, BitmapCallBack callback) { mPath = path; DiskImageRequestTask task = new DiskImageRequestTask(maxWidth, maxHeight, callback); task.execute(); } /** * 从本地载入一张图片 * * @param imagePath * 图片的地址 * @throws FileNotFoundException */ private byte[] loadFromFile(String path, int maxWidth, int maxHeight, BitmapCallBack callback) { byte[] data = null; FileInputStream fis = null; try { fis = new FileInputStream(path); if (fis != null) { data = FileUtils.input2byte(fis); } handleBitmap(data, maxWidth, maxHeight, callback); } catch (Exception e) { doFailure(callback, e); } finally { FileUtils.closeIO(fis); } return data; } private Bitmap handleBitmap(byte[] data, int maxWidth, int maxHeight, BitmapCallBack callback) { BitmapFactory.Options option = new BitmapFactory.Options(); Bitmap bitmap = null; if (maxWidth <= 0 && maxHeight <= 0) { bitmap = BitmapFactory .decodeByteArray(data, 0, data.length, option); } else { option.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data, 0, data.length, option); int actualWidth = option.outWidth; int actualHeight = option.outHeight; // 计算出图片应该显示的宽高 int desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight); int desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth); option.inJustDecodeBounds = false; option.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, option); // 做缩放 if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap .getHeight() > desiredHeight)) { bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true); tempBitmap.recycle(); } else { bitmap = tempBitmap; } } if (bitmap == null) { doFailure(callback, new RuntimeException("bitmap create error")); } else { BitmapConfig.mMemoryCache.putBitmap(mPath, bitmap); doSuccess(callback, bitmap); } return bitmap; } /** * 框架会自动将大于设定值的bitmap转换成设定值,所以需要这个方法来判断应该显示默认大小或者是设定值大小。
* 本方法会根据maxPrimary与actualPrimary比较来判断,如果无法判断则会根据辅助值判断,辅助值一般是主要值对应的。 * 比如宽为主值则高为辅值 * * @param maxPrimary * 需要判断的值,用作主要判断 * @param maxSecondary * 需要判断的值,用作辅助判断 * @param actualPrimary * 真实宽度 * @param actualSecondary * 真实高度 * @return 获取图片需要显示的大小 */ private int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) { if (maxPrimary == 0 && maxSecondary == 0) { return actualPrimary; } if (maxPrimary == 0) { double ratio = (double) maxSecondary / (double) actualSecondary; return (int) (actualPrimary * ratio); } if (maxSecondary == 0) { return maxPrimary; } double ratio = (double) actualSecondary / (double) actualPrimary; int resized = maxPrimary; if (resized * ratio > maxSecondary) { resized = (int) (maxSecondary / ratio); } return resized; } /** * 关于本方法的判断,可以查看我的博客:http://blog.kymjs.com/kjframeforandroid/2014/12/05/02/ * * @param actualWidth * @param actualHeight * @param desiredWidth * @param desiredHeight * @return */ static int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { double wr = (double) actualWidth / desiredWidth; double hr = (double) actualHeight / desiredHeight; double ratio = Math.min(wr, hr); float n = 1.0f; while ((n * 2) <= ratio) { n *= 2; } return (int) n; } private void doSuccess(final BitmapCallBack callback, final Bitmap bitmap) { if (callback != null) { handle.post(new Runnable() { @Override public void run() { callback.onSuccess(bitmap); callback.onFinish(); } }); } } private void doFailure(final BitmapCallBack callback, final Exception e) { if (callback != null) { handle.post(new Runnable() { @Override public void run() { callback.onFailure(e); callback.onFinish(); } }); } } } \ No newline at end of file +/* * Copyright (c) 2015, 张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.bitmap; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Looper; import org.kymjs.kjframe.http.KJAsyncTask; import org.kymjs.kjframe.utils.FileUtils; import java.io.FileInputStream; import java.io.FileNotFoundException; /** * 本地图片加载 * * @author kymjs (https://www.kymjs.com/) */ public class DiskImageRequest { private final Handler handle = new Handler(Looper.getMainLooper()); private String mPath; class DiskImageRequestTask extends KJAsyncTask { private final int mMaxWidth; private final int mMaxHeight; private final BitmapCallBack mCallback; public DiskImageRequestTask(int maxWidth, int maxHeight, BitmapCallBack callback) { mMaxHeight = maxHeight; mMaxWidth = maxWidth; mCallback = callback; } @Override protected void onPreExecute() { super.onPreExecute(); if (mCallback != null) { mCallback.onPreLoad(); } } @Override protected byte[] doInBackground(Void... params) { return loadFromFile(mPath, mMaxWidth, mMaxHeight, mCallback); } } public void load(String path, int maxWidth, int maxHeight, BitmapCallBack callback) { mPath = path; DiskImageRequestTask task = new DiskImageRequestTask(maxWidth, maxHeight, callback); task.execute(); } /** * 从本地载入一张图片 * * @param path * 图片的地址 * @throws FileNotFoundException */ private byte[] loadFromFile(String path, int maxWidth, int maxHeight, BitmapCallBack callback) { byte[] data = null; FileInputStream fis = null; try { fis = new FileInputStream(path); if (fis != null) { data = FileUtils.input2byte(fis); } handleBitmap(data, maxWidth, maxHeight, callback); } catch (Exception e) { doFailure(callback, e); } finally { FileUtils.closeIO(fis); } return data; } private Bitmap handleBitmap(byte[] data, int maxWidth, int maxHeight, BitmapCallBack callback) { BitmapFactory.Options option = new BitmapFactory.Options(); Bitmap bitmap = null; if (maxWidth <= 0 && maxHeight <= 0) { bitmap = BitmapFactory .decodeByteArray(data, 0, data.length, option); } else { option.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data, 0, data.length, option); int actualWidth = option.outWidth; int actualHeight = option.outHeight; // 计算出图片应该显示的宽高 int desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight); int desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth); option.inJustDecodeBounds = false; option.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, option); // 做缩放 if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap .getHeight() > desiredHeight)) { bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true); tempBitmap.recycle(); } else { bitmap = tempBitmap; } } if (bitmap == null) { doFailure(callback, new RuntimeException("bitmap create error")); } else { BitmapConfig.mMemoryCache.putBitmap(mPath, bitmap); doSuccess(callback, bitmap); } return bitmap; } /** * 框架会自动将大于设定值的bitmap转换成设定值,所以需要这个方法来判断应该显示默认大小或者是设定值大小。
* 本方法会根据maxPrimary与actualPrimary比较来判断,如果无法判断则会根据辅助值判断,辅助值一般是主要值对应的。 * 比如宽为主值则高为辅值 * * @param maxPrimary * 需要判断的值,用作主要判断 * @param maxSecondary * 需要判断的值,用作辅助判断 * @param actualPrimary * 真实宽度 * @param actualSecondary * 真实高度 * @return 获取图片需要显示的大小 */ private int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) { if (maxPrimary == 0 && maxSecondary == 0) { return actualPrimary; } if (maxPrimary == 0) { double ratio = (double) maxSecondary / (double) actualSecondary; return (int) (actualPrimary * ratio); } if (maxSecondary == 0) { return maxPrimary; } double ratio = (double) actualSecondary / (double) actualPrimary; int resized = maxPrimary; if (resized * ratio > maxSecondary) { resized = (int) (maxSecondary / ratio); } return resized; } /** * 关于本方法的判断,可以查看我的博客:http://blog.kymjs.com/kjframeforandroid/2014/12/05/02/ * * @param actualWidth * @param actualHeight * @param desiredWidth * @param desiredHeight * @return */ static int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { double wr = (double) actualWidth / desiredWidth; double hr = (double) actualHeight / desiredHeight; double ratio = Math.min(wr, hr); float n = 1.0f; while ((n * 2) <= ratio) { n *= 2; } return (int) n; } private void doSuccess(final BitmapCallBack callback, final Bitmap bitmap) { if (callback != null) { handle.post(new Runnable() { @Override public void run() { callback.onSuccess(bitmap); callback.onFinish(); } }); } } private void doFailure(final BitmapCallBack callback, final Exception e) { if (callback != null) { handle.post(new Runnable() { @Override public void run() { callback.onFailure(e); callback.onFinish(); } }); } } } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java index 11c681b4..603a39d2 100755 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageDisplayer.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2013 The Android Open Source Project +/* + * Copyright (c) 2015, 张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,13 +51,14 @@ public class ImageDisplayer { * 创建一个图片显示器 * * @param bitmapConfig + * KJBitmap配置器 */ public ImageDisplayer(HttpConfig httpConfig, BitmapConfig bitmapConfig) { - // 靠,在这里踩了个坑。 最初写的是Integer.MAX_VALUE, + // 在这里踩了个坑。 最初写的是Integer.MAX_VALUE, // 结果把这个值*60000转成毫秒long以后溢出了 这次我给个死的值行不行。1000天,能不能算永久了 // 其实还有一种解决办法是直接在缓存读取的时候,看到是bitmap缓存不管是否失效都返回, // 但是这种不利于自定义扩展,就不用了,有兴趣的可以看CacheDispatcher的105行 - // @kymjs记录于2015.4.30 + // kymjs记录于2015.4.30 // config.cacheTime = bitmapConfig.cacheTime; sKJHttp.setConfig(httpConfig); mMemoryCache = BitmapConfig.mMemoryCache; @@ -69,7 +70,7 @@ public ImageDisplayer(HttpConfig httpConfig, BitmapConfig bitmapConfig) { * * @param requestUrl * 图片地址 - * @return + * @return 图片是否已经被缓存 */ public boolean isCached(String requestUrl) { throwIfNotOnMainThread(); @@ -86,7 +87,8 @@ public boolean isCached(String requestUrl) { * @param maxHeight * 图片最大高度 * @param callback - * @return + * 回调 + * @return 加载的图片封装 */ public ImageBale get(String requestUrl, int maxWidth, int maxHeight, BitmapCallBack callback) { @@ -183,7 +185,6 @@ protected void onGetImageError(String url, KJHttpException error) { * 对一个图片的封装,包含了这张图片所需要携带的信息 * * @author kymjs - * */ public class ImageBale { private Bitmap mBitmap; @@ -232,7 +233,6 @@ public String getRequestUrl() { * 图片从网络请求并获取到相应的事件 * * @author kymjs - * */ private class ImageRequestEven { private final Request mRequest; @@ -313,33 +313,24 @@ private void throwIfNotOnMainThread() { * 取消一个加载请求 * * @param url + * key */ public void cancel(String url) { sKJHttp.cancel(url); } - /** - * 取消一个加载请求(拼写错误,请使用cancel(url)) - * - * @param url - */ - @Deprecated - public void cancle(String url) { - this.cancel(url); - } - /** * 内存缓存接口定义 * * @author kymjs */ public interface ImageCache { - public Bitmap getBitmap(String url); + Bitmap getBitmap(String url); - public void remove(String key); + void remove(String key); - public void clean(); + void clean(); - public void putBitmap(String url, Bitmap bitmap); + void putBitmap(String url, Bitmap bitmap); } } diff --git a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java index 5a9b0d6b..f3d406ef 100755 --- a/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java +++ b/KJFrame/src/org/kymjs/kjframe/bitmap/ImageRequest.java @@ -16,8 +16,8 @@ package org.kymjs.kjframe.bitmap; -import java.util.HashMap; -import java.util.Map; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import org.kymjs.kjframe.http.HttpCallBack; import org.kymjs.kjframe.http.HttpConfig; @@ -28,15 +28,15 @@ import org.kymjs.kjframe.http.Response; import org.kymjs.kjframe.utils.KJLoger; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; +import java.util.HashMap; +import java.util.Map; /** * 从网络下载一张bitmap - * + * * @author kymjs (https://www.kymjs.com/) */ -public class ImageRequest extends Request { +public class ImageRequest extends Request implements Persistence { private final int mMaxWidth; private final int mMaxHeight; @@ -46,7 +46,7 @@ public class ImageRequest extends Request { private final Map mHeaders = new HashMap(); public ImageRequest(String url, int maxWidth, int maxHeight, - HttpCallBack callback) { + HttpCallBack callback) { super(HttpMethod.GET, url, callback); mHeaders.put("cookie", HttpConfig.sCookie); mMaxWidth = maxWidth; @@ -72,19 +72,15 @@ public Map getHeaders() { * 框架会自动将大于设定值的bitmap转换成设定值,所以需要这个方法来判断应该显示默认大小或者是设定值大小。
* 本方法会根据maxPrimary与actualPrimary比较来判断,如果无法判断则会根据辅助值判断,辅助值一般是主要值对应的。 * 比如宽为主值则高为辅值 - * - * @param maxPrimary - * 需要判断的值,用作主要判断 - * @param maxSecondary - * 需要判断的值,用作辅助判断 - * @param actualPrimary - * 真实宽度 - * @param actualSecondary - * 真实高度 + * + * @param maxPrimary 需要判断的值,用作主要判断 + * @param maxSecondary 需要判断的值,用作辅助判断 + * @param actualPrimary 真实宽度 + * @param actualSecondary 真实高度 * @return 获取图片需要显示的大小 */ private static int getResizedDimension(int maxPrimary, int maxSecondary, - int actualPrimary, int actualSecondary) { + int actualPrimary, int actualSecondary) { if (maxPrimary == 0 && maxSecondary == 0) { return actualPrimary; } @@ -121,7 +117,7 @@ public Response parseNetworkResponse(NetworkResponse response) { /** * 关于本函数的详细解释,可以看我的博客: * http://blog.kymjs.com/kjframeforandroid/2014/12/05/02/ - * + * * @param response * @return */ @@ -153,7 +149,7 @@ private Response doParse(NetworkResponse response) { // 做缩放 if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap - .getHeight() > desiredHeight)) { + .getHeight() > desiredHeight)) { bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true); tempBitmap.recycle(); @@ -179,7 +175,7 @@ protected void deliverResponse(Map header, Bitmap response) { /** * 关于本方法的判断,可以查看我的博客:http://blog.kymjs.com/kjframeforandroid/2014/12/05/02/ - * + * * @param actualWidth * @param actualHeight * @param desiredWidth @@ -187,7 +183,7 @@ protected void deliverResponse(Map header, Bitmap response) { * @return */ static int findBestSampleSize(int actualWidth, int actualHeight, - int desiredWidth, int desiredHeight) { + int desiredWidth, int desiredHeight) { double wr = (double) actualWidth / desiredWidth; double hr = (double) actualHeight / desiredHeight; double ratio = Math.min(wr, hr); diff --git a/KJFrame/src/org/kymjs/kjframe/database/CursorHelper.java b/KJFrame/src/org/kymjs/kjframe/database/CursorHelper.java index 091844f7..837a0497 100644 --- a/KJFrame/src/org/kymjs/kjframe/database/CursorHelper.java +++ b/KJFrame/src/org/kymjs/kjframe/database/CursorHelper.java @@ -15,8 +15,7 @@ */ package org.kymjs.kjframe.database; -import java.util.HashMap; -import java.util.Map.Entry; +import android.database.Cursor; import org.kymjs.kjframe.KJDB; import org.kymjs.kjframe.database.utils.ManyToOne; @@ -24,7 +23,8 @@ import org.kymjs.kjframe.database.utils.Property; import org.kymjs.kjframe.database.utils.TableInfo; -import android.database.Cursor; +import java.util.HashMap; +import java.util.Map.Entry; /** * 游标操作的帮助类
@@ -126,7 +126,6 @@ public static DbModel getDbModel(Cursor cursor) { * @param dbModel * @param clazz * 待生成的JavaBean对象 - * @return */ public static T dbModel2Entity(DbModel dbModel, Class clazz) { if (dbModel != null) { diff --git a/KJFrame/src/org/kymjs/kjframe/database/utils/Property.java b/KJFrame/src/org/kymjs/kjframe/database/utils/Property.java index e801d606..10722361 100644 --- a/KJFrame/src/org/kymjs/kjframe/database/utils/Property.java +++ b/KJFrame/src/org/kymjs/kjframe/database/utils/Property.java @@ -15,6 +15,8 @@ */ package org.kymjs.kjframe.database.utils; +import android.annotation.SuppressLint; + import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -22,8 +24,6 @@ import java.text.SimpleDateFormat; import java.util.Date; -import android.annotation.SuppressLint; - /** * 属性 ,【非主键】的【基本数据类型】 都是属性
* @@ -80,14 +80,12 @@ public void setValue(Object receiver, Object value) { set.invoke(receiver, value); } } catch (Exception e) { - e.printStackTrace(); } } else { try { field.setAccessible(true); field.set(receiver, value); } catch (Exception e) { - e.printStackTrace(); } } } @@ -104,11 +102,8 @@ public T getValue(Object obj) { try { return (T) get.invoke(obj); } catch (IllegalArgumentException e) { - e.printStackTrace(); } catch (IllegalAccessException e) { - e.printStackTrace(); } catch (InvocationTargetException e) { - e.printStackTrace(); } } return null; @@ -123,7 +118,6 @@ private static Date stringToDateTime(String strDate) { try { return sdf.parse(strDate); } catch (ParseException e) { - e.printStackTrace(); } } return null; diff --git a/KJFrame/src/org/kymjs/kjframe/http/Cache.java b/KJFrame/src/org/kymjs/kjframe/http/Cache.java index f88fb14e..08a767af 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/Cache.java +++ b/KJFrame/src/org/kymjs/kjframe/http/Cache.java @@ -23,33 +23,31 @@ */ public interface Cache { - public Entry get(String key); + Entry get(String key); - public void put(String key, Entry entry); + void put(String key, Entry entry); - public void remove(String key); + void remove(String key); - public void clean(); + void clean(); /** * 执行在线程中 */ - public void initialize(); + void initialize(); /** * 让一个缓存过期 - * - * @param key - * Cache key - * @param fullExpire - * True to fully expire the entry, false to soft expire + * + * @param key Cache key + * @param fullExpire True to fully expire the entry, false to soft expire */ - public void invalidate(String key, boolean fullExpire); + void invalidate(String key, boolean fullExpire); /** * cache真正缓存的数据bean,这个是会被保存的缓存对象 */ - public static class Entry { + class Entry { public byte[] data; public String etag; // 为cache标记一个tag @@ -65,5 +63,4 @@ public boolean isExpired() { return this.ttl < System.currentTimeMillis(); } } - } diff --git a/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java b/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java index 30e1c771..937a6cc6 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java +++ b/KJFrame/src/org/kymjs/kjframe/http/CacheDispatcher.java @@ -15,28 +15,24 @@ */ package org.kymjs.kjframe.http; -import java.util.concurrent.BlockingQueue; +import android.os.Process; import org.kymjs.kjframe.KJHttp; -import org.kymjs.kjframe.bitmap.ImageRequest; +import org.kymjs.kjframe.bitmap.Persistence; import org.kymjs.kjframe.utils.KJLoger; -import android.os.Process; +import java.util.concurrent.BlockingQueue; /** - * 缓存调度器
- * - * Note:
+ * 缓存调度器 * 工作描述: 缓存逻辑同样也采用责任链模式,参考注释{@link KJHttp }, - * 由缓存任务队列CacheQueue,缓存调度器CacheDispatcher,缓存器Cache组成
- * + * 由缓存任务队列CacheQueue,缓存调度器CacheDispatcher,缓存器Cache组成 * 调度器不停的从CacheQueue中取request,并把这个request尝试从缓存器中获取缓存响应。
* 如果缓存器有有效且及时的缓存则直接返回缓存;
* 如果缓存器有有效但待刷新的有效缓存,则交给分发器去分发一次中介相应,并再去添加到工作队列中执行网络请求获取最新的数据;
* 如果缓存器中没有有效缓存,则把请求添加到mNetworkQueue工作队列中去执行网络请求;
- * - * Note:
- * 关于中介相应查看Response.intermediate() + * + * @author kymjs (http://www.kymjs.com/) . */ public class CacheDispatcher extends Thread { @@ -50,14 +46,13 @@ public class CacheDispatcher extends Thread { /** * 创建分发器(必须手动调用star()方法启动分发任务) - * - * @param cacheQueue - * 缓存队列 - * @param networkQueue - * 正在执行的队列 + * + * @param cacheQueue 缓存队列 + * @param networkQueue 正在执行的队列 + * @param config 配置器 */ public CacheDispatcher(BlockingQueue> cacheQueue, - BlockingQueue> networkQueue, HttpConfig config) { + BlockingQueue> networkQueue, HttpConfig config) { mCacheQueue = cacheQueue; mNetworkQueue = networkQueue; mCache = HttpConfig.mCache; @@ -96,7 +91,7 @@ public void run() { } // 如果缓存过期,去网络请求,图片缓存永久有效 - if (entry.isExpired() && !(request instanceof ImageRequest)) { + if (entry.isExpired() && !(request instanceof Persistence)) { request.setCacheEntry(entry); mNetworkQueue.put(request); continue; diff --git a/KJFrame/src/org/kymjs/kjframe/http/Delivery.java b/KJFrame/src/org/kymjs/kjframe/http/Delivery.java index ae78b632..75a3f68c 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/Delivery.java +++ b/KJFrame/src/org/kymjs/kjframe/http/Delivery.java @@ -18,7 +18,7 @@ /** * 分发器,将异步线程中的结果响应到UI线程中 * - * @author kymjs + * @author kymjs (http://www.kymjs.com/). * */ public interface Delivery { diff --git a/KJFrame/src/org/kymjs/kjframe/http/DeliveryExecutor.java b/KJFrame/src/org/kymjs/kjframe/http/DeliveryExecutor.java index d58e9ceb..742e7a9a 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/DeliveryExecutor.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DeliveryExecutor.java @@ -16,12 +16,14 @@ package org.kymjs.kjframe.http; -import java.util.concurrent.Executor; - import android.os.Handler; +import java.util.concurrent.Executor; + /** * Http响应的分发器,这里用于把异步线程中的响应分发到UI线程中执行 + * + * @author kymjs (http://www.kymjs.com/) . */ public class DeliveryExecutor implements Delivery { @@ -46,14 +48,16 @@ public void postResponse(Request request, Response response) { } /** - * 当有中介响应的时候,会被调用,首先返回中介响应,并执行runnable(实际就是再去请求网络)
- * Note:所谓中介响应:当本地有一个未过期缓存的时候会优先返回一个缓存,但如果这个缓存又是需要刷新的时候,会再次去请求网络, - * 那么之前返回的那个有效但需要刷新的就是中介响应 + * 如果请求成功,则将这个结果分发到主线程 + * 10.09添加:分发结果之前先在异步执行一次onSuccessInAsync()回调 */ @Override public void postResponse(Request request, Response response, - Runnable runnable) { + Runnable runnable) { request.markDelivered(); + if (response.isSuccess() && response.result instanceof byte[]) { + request.onAsyncSuccess((byte[]) response.result); + } mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); } @@ -75,7 +79,7 @@ private class ResponseDeliveryRunnable implements Runnable { private final Runnable mRunnable; public ResponseDeliveryRunnable(Request request, Response response, - Runnable runnable) { + Runnable runnable) { mRequest = request; mResponse = response; mRunnable = runnable; @@ -104,10 +108,11 @@ public void run() { @Override public void postDownloadProgress(Request request, long fileSize, - long downloadedSize) { + long downloadedSize) { request.mCallback.onLoading(fileSize, downloadedSize); } @Override - public void postCancel(Request request) {} + public void postCancel(Request request) { + } } diff --git a/KJFrame/src/org/kymjs/kjframe/http/DeliveryResponse.java b/KJFrame/src/org/kymjs/kjframe/http/DeliveryResponse.java index a2a29d21..812d59b8 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/DeliveryResponse.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DeliveryResponse.java @@ -17,14 +17,13 @@ /** * 分发器,将异步线程中的结果响应到UI线程中 - * - * @author kymjs (http://blog.kymjs.com) - * + * + * @author kymjs (http://www.kymjs.com/) . */ public interface DeliveryResponse { /** * 分发响应结果 - * + * * @param request * @param response */ @@ -32,11 +31,9 @@ public interface DeliveryResponse { /** * 分发Failure事件 - * - * @param request - * 请求 - * @param error - * 异常原因 + * + * @param request 请求 + * @param error 异常原因 */ public void postError(Request request, KJHttpException error); @@ -46,6 +43,6 @@ public interface DeliveryResponse { * 那么之前返回的那个有效但需要刷新的就是中介响应 */ public void postResponse(Request request, Response response, - Runnable runnable); + Runnable runnable); } diff --git a/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java b/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java index d501809b..6a78620e 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DiskCache.java @@ -15,6 +15,10 @@ */ package org.kymjs.kjframe.http; +import android.os.SystemClock; + +import org.kymjs.kjframe.utils.KJLoger; + import java.io.BufferedInputStream; import java.io.EOFException; import java.io.File; @@ -30,10 +34,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import org.kymjs.kjframe.utils.KJLoger; - -import android.os.SystemClock; - /** * 缓存器实现类,磁盘缓存器 */ @@ -50,10 +50,8 @@ public class DiskCache implements Cache { private static final int CACHE_MAGIC = 0x20150423; /** - * @param rootDirectory - * 缓存文件夹 - * @param maxCacheSizeInBytes - * 缓存大小 + * @param rootDirectory 缓存文件夹 + * @param maxCacheSizeInBytes 缓存大小 */ public DiskCache(File rootDirectory, int maxCacheSizeInBytes) { mRootDirectory = rootDirectory; @@ -62,9 +60,8 @@ public DiskCache(File rootDirectory, int maxCacheSizeInBytes) { /** * 使用默认缓存大小(5MB)构造磁盘缓存器 - * - * @param rootDirectory - * The root directory of the cache. + * + * @param rootDirectory The root directory of the cache. */ public DiskCache(File rootDirectory) { this(rootDirectory, 5 * 1024 * 1024); @@ -88,7 +85,7 @@ public synchronized void clean() { /** * 获取一个cache - * + * * @return 如果不存在返回null */ @Override @@ -159,11 +156,9 @@ public synchronized void initialize() { /** * 刷新缓存 - * + * * @param key - * - * @param fullExpire - * True to fully expire the entry, false to soft expire + * @param fullExpire True to fully expire the entry, false to soft expire */ @Override public synchronized void invalidate(String key, boolean fullExpire) { @@ -214,9 +209,8 @@ public synchronized void remove(String key) { /** * Creates a pseudo-unique filename for the specified cache key. - * - * @param key - * The key to generate a file name for. + * + * @param key The key to generate a file name for. * @return A pseudo-unique filename. */ private String getFilenameForKey(String key) { @@ -237,9 +231,8 @@ public File getFileForKey(String key) { /** * Prunes the cache to fit the amount of bytes specified. - * - * @param neededSpace - * The amount of bytes we are trying to fit into the cache. + * + * @param neededSpace The amount of bytes we are trying to fit into the cache. */ private void pruneIfNeeded(int neededSpace) { if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { @@ -284,11 +277,9 @@ private void pruneIfNeeded(int neededSpace) { /** * Puts the entry with the specified key into the cache. - * - * @param key - * The key to identify the entry by. - * @param entry - * The entry to cache. + * + * @param key The key to identify the entry by. + * @param entry The entry to cache. */ private void putEntry(String key, CacheHeader entry) { if (!mEntries.containsKey(key)) { @@ -313,7 +304,7 @@ private void removeEntry(String key) { /** * Reads the contents of an InputStream into a byte[]. - * */ + */ private static byte[] streamToBytes(InputStream in, int length) throws IOException { byte[] bytes = new byte[length]; @@ -342,7 +333,8 @@ static class CacheHeader { public long softTtl; public Map responseHeaders; - private CacheHeader() {} + private CacheHeader() { + } public CacheHeader(String key, Entry entry) { this.key = key; @@ -356,9 +348,8 @@ public CacheHeader(String key, Entry entry) { /** * Reads the header off of an InputStream and returns a CacheHeader * object. - * - * @param is - * The InputStream to read from. + * + * @param is The InputStream to read from. * @throws IOException */ public static CacheHeader readHeader(InputStream is) throws IOException { @@ -519,10 +510,8 @@ static void writeStringStringMap(Map map, OutputStream os) static Map readStringStringMap(InputStream is) throws IOException { int size = readInt(is); - Map result = (size == 0) ? Collections - . emptyMap() - : new HashMap(size); - // String str = ; + Map result = (size == 0) ? Collections.emptyMap() : new + HashMap(size); for (int i = 0; i < size; i++) { String key = readString(is).intern(); String value = readString(is).intern(); @@ -530,5 +519,4 @@ static Map readStringStringMap(InputStream is) } return result; } - } diff --git a/KJFrame/src/org/kymjs/kjframe/http/DownloadController.java b/KJFrame/src/org/kymjs/kjframe/http/DownloadController.java index b51fa42e..3af029ee 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/DownloadController.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DownloadController.java @@ -17,6 +17,11 @@ import android.util.Log; +/** + * KJHttp.download()控制器 + * + * @author kymjs (http://www.kymjs.com/) . + */ public class DownloadController { private final FileRequest mRequest; @@ -45,7 +50,7 @@ public class DownloadController { /** * 如果当前任务是等待态,让他转入运行态 - * + * * @return */ /* package */boolean doLoadOnWait() { @@ -66,7 +71,7 @@ public class DownloadController { /** * 这个控制器负责的Request - * + * * @return */ public FileRequest getRequest() { @@ -91,11 +96,12 @@ public boolean isDownloading() { /** * 暂停任务 - * + * * @return */ public boolean pause() { - if ((mStatus == STATUS_DOWNLOADING || mStatus == STATUS_WAITING) && mRequest != null && mQueue != null) { + if ((mStatus == STATUS_DOWNLOADING || mStatus == STATUS_WAITING) && mRequest != null && + mQueue != null) { mStatus = STATUS_PAUSE; mRequest.cancel(); mQueue.wake(); @@ -106,7 +112,7 @@ public boolean pause() { /** * 恢复处于暂停态的任务 - * + * * @return 如果mQueue为null或当前状态不是STATUS_PAUSE,返回false * @deprecated 不推荐直接调用本方法,建议直接再次调用{@link DownloadTaskQueue#add(FileRequest)} */ @@ -123,7 +129,7 @@ public boolean resume() { /** * 废弃当前下载任务 - * + * * @return */ public boolean removeTask() { diff --git a/KJFrame/src/org/kymjs/kjframe/http/DownloadTaskQueue.java b/KJFrame/src/org/kymjs/kjframe/http/DownloadTaskQueue.java index 9be33d73..295a01e3 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/DownloadTaskQueue.java +++ b/KJFrame/src/org/kymjs/kjframe/http/DownloadTaskQueue.java @@ -26,8 +26,7 @@ * 负责维护当前正在下载状态
* 对每个下载请求提供一个控制器,来控制下载的各种状态改变 * - * @author kymjs - * + * @author kymjs (http://www.kymjs.com/) . */ public class DownloadTaskQueue { private final int mParallelTaskCount; // 最大同时下载量 @@ -61,6 +60,7 @@ public void clearAll() { * 添加一个下载请求,如果这个请求已经存在,则尝试唤醒这个请求 * * @param request + * 要添加的下载请求 */ public void add(FileRequest request) { throwIfNotOnMainThread(); @@ -78,6 +78,7 @@ public void add(FileRequest request) { * 移除一个下载任务 * * @param url + * 要移除的url */ public void remove(String url) { for (DownloadController controller : mTaskQueue) { @@ -92,10 +93,11 @@ public void remove(String url) { } /** - * * @param storeFilePath + * 下载后文件在本地的地址 * @param url - * @return + * 下载的url + * @return DownloadController下载控制器 */ public DownloadController get(String storeFilePath, String url) { synchronized (mTaskQueue) { @@ -152,7 +154,8 @@ private void throwIfNotOnMainThread() { * 如果这个请求本身就存在,则直接返回这个请求 * * @param request - * @return + * 要被判断的request + * @return 如果这个请求本身就存在,则直接返回这个请求 */ private DownloadController requestExist(FileRequest request) { for (DownloadController task : mTaskQueue) { diff --git a/KJFrame/src/org/kymjs/kjframe/http/FileRequest.java b/KJFrame/src/org/kymjs/kjframe/http/FileRequest.java index 6ea5cf7a..39e5ecc9 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/FileRequest.java +++ b/KJFrame/src/org/kymjs/kjframe/http/FileRequest.java @@ -23,12 +23,15 @@ import java.util.Map; import java.util.zip.GZIPInputStream; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.kymjs.kjframe.utils.KJLoger; import android.text.TextUtils; +/** + * 请求文件方法类 + * + * @author kymjs (http://www.kymjs.com/) . + */ public class FileRequest extends Request { private final File mStoreFile; private final File mTemporaryFile; // 临时文件 @@ -104,10 +107,9 @@ public void setHeaders(Map mHeaders) { this.mHeaders = mHeaders; } - public byte[] handleResponse(HttpResponse response) throws IOException, + public byte[] handleResponse(KJHttpResponse response) throws IOException, KJHttpException { - HttpEntity entity = response.getEntity(); - long fileSize = entity.getContentLength(); + long fileSize = response.getContentLength(); if (fileSize <= 0) { KJLoger.debug("Response doesn't present Content-Length!"); } @@ -117,8 +119,7 @@ public byte[] handleResponse(HttpResponse response) throws IOException, if (isSupportRange) { fileSize += downloadedSize; - String realRangeValue = HttpUtils.getHeader(response, - "Content-Range"); + String realRangeValue = response.getHeaders().get("Content-Range"); if (!TextUtils.isEmpty(realRangeValue)) { String assumeRangeValue = "bytes " + downloadedSize + "-" + (fileSize - 1); @@ -149,7 +150,7 @@ public byte[] handleResponse(HttpResponse response) throws IOException, } try { - InputStream in = entity.getContent(); + InputStream in = response.getContentStream(); if (HttpUtils.isGzipContent(response) && !(in instanceof GZIPInputStream)) { in = new GZIPInputStream(in); @@ -170,8 +171,7 @@ public byte[] handleResponse(HttpResponse response) throws IOException, } } finally { try { - if (entity != null) - entity.consumeContent(); + response.getContentStream().close(); } catch (Exception e) { KJLoger.debug("Error occured when calling consumingContent"); } diff --git a/KJFrame/src/org/kymjs/kjframe/http/FormRequest.java b/KJFrame/src/org/kymjs/kjframe/http/FormRequest.java index 14d1433c..e67fe36b 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/FormRequest.java +++ b/KJFrame/src/org/kymjs/kjframe/http/FormRequest.java @@ -15,17 +15,16 @@ */ package org.kymjs.kjframe.http; +import org.kymjs.kjframe.utils.KJLoger; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; -import android.util.Log; - /** * Form表单形式的Http请求 - * + * * @author kymjs - * */ public class FormRequest extends Request { @@ -36,7 +35,7 @@ public FormRequest(String url, HttpCallBack callback) { } public FormRequest(int httpMethod, String url, HttpParams params, - HttpCallBack callback) { + HttpCallBack callback) { super(httpMethod, url, callback); if (params == null) { params = new HttpParams(); @@ -56,7 +55,7 @@ public String getCacheKey() { @Override public String getBodyContentType() { if (mParams.getContentType() != null) { - return mParams.getContentType().getValue(); + return mParams.getContentType(); } else { return super.getBodyContentType(); } @@ -73,7 +72,7 @@ public byte[] getBody() { try { mParams.writeTo(bos); } catch (IOException e) { - Log.e("kymjs", "IOException writing to ByteArrayOutputStream"); + KJLoger.debug("FormRequest75--->IOException writing to ByteArrayOutputStream"); } return bos.toByteArray(); } diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpCallBack.java b/KJFrame/src/org/kymjs/kjframe/http/HttpCallBack.java index fa66623f..daf1d20c 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpCallBack.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpCallBack.java @@ -15,16 +15,16 @@ */ package org.kymjs.kjframe.http; -import java.util.Map; - import android.graphics.Bitmap; +import java.util.Map; + /** * Http请求回调类
* * 创建时间 2014-8-7 - * - * @author kymjs (https://github.com/kymjs) + * + * @author kymjs (http://www.kymjs.com/) . * @version 1.4 */ public abstract class HttpCallBack { @@ -33,14 +33,16 @@ public abstract class HttpCallBack { * 请求开始之前回调 */ public void onPreStart() { - onPreStar(); } /** - * 拼写错误,请使用onPreStart() + * 注意:本方法将在异步调用。 + * Http异步请求成功时在异步回调,并且仅当本方法执行完成才会继续调用onSuccess() + * + * @param t 返回的信息 */ - @Deprecated - public void onPreStar() {} + public void onSuccessInAsync(byte[] t) { + } /** * Http请求成功时回调 diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpClientStack.java b/KJFrame/src/org/kymjs/kjframe/http/HttpClientStack.java deleted file mode 100755 index 2cc74fea..00000000 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpClientStack.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2014, 张涛. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.kymjs.kjframe.http; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpOptions; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpTrace; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.kymjs.kjframe.http.Request.HttpMethod; - -/** - * HttpClient请求端实现 - */ -public class HttpClientStack implements HttpStack { - protected final HttpClient mClient; - - private final static String HEADER_CONTENT_TYPE = "Content-Type"; - - public HttpClientStack(HttpClient client) { - mClient = client; - } - - private static void addHeaders(HttpUriRequest httpRequest, - Map headers) { - for (String key : headers.keySet()) { - httpRequest.setHeader(key, headers.get(key)); - } - } - - @SuppressWarnings("unused") - private static List getPostParameterPairs( - Map postParams) { - List result = new ArrayList( - postParams.size()); - for (String key : postParams.keySet()) { - result.add(new BasicNameValuePair(key, postParams.get(key))); - } - return result; - } - - @Override - public HttpResponse performRequest(Request request, - Map additionalHeaders) throws IOException { - HttpUriRequest httpRequest = createHttpRequest(request, - additionalHeaders); - addHeaders(httpRequest, additionalHeaders); - addHeaders(httpRequest, request.getHeaders()); - onPrepareRequest(httpRequest); - HttpParams httpParams = httpRequest.getParams(); - int timeoutMs = request.getTimeoutMs(); - HttpConnectionParams.setConnectionTimeout(httpParams, 5000); - HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); - return mClient.execute(httpRequest); - } - - /* protected */static HttpUriRequest createHttpRequest(Request request, - Map additionalHeaders) { - switch (request.getMethod()) { - case HttpMethod.GET: - return new HttpGet(request.getUrl()); - case HttpMethod.DELETE: - return new HttpDelete(request.getUrl()); - case HttpMethod.POST: { - HttpPost postRequest = new HttpPost(request.getUrl()); - postRequest.addHeader(HEADER_CONTENT_TYPE, - request.getBodyContentType()); - setEntityIfNonEmptyBody(postRequest, request); - return postRequest; - } - case HttpMethod.PUT: { - HttpPut putRequest = new HttpPut(request.getUrl()); - putRequest.addHeader(HEADER_CONTENT_TYPE, - request.getBodyContentType()); - setEntityIfNonEmptyBody(putRequest, request); - return putRequest; - } - case HttpMethod.HEAD: - return new HttpHead(request.getUrl()); - case HttpMethod.OPTIONS: - return new HttpOptions(request.getUrl()); - case HttpMethod.TRACE: - return new HttpTrace(request.getUrl()); - case HttpMethod.PATCH: { - HttpPatch patchRequest = new HttpPatch(request.getUrl()); - patchRequest.addHeader(HEADER_CONTENT_TYPE, - request.getBodyContentType()); - setEntityIfNonEmptyBody(patchRequest, request); - return patchRequest; - } - default: - throw new IllegalStateException("Unknown request method."); - } - } - - private static void setEntityIfNonEmptyBody( - HttpEntityEnclosingRequestBase httpRequest, Request request) { - byte[] body = request.getBody(); - if (body != null) { - HttpEntity entity = new ByteArrayEntity(body); - httpRequest.setEntity(entity); - } - } - - protected void onPrepareRequest(HttpUriRequest request) throws IOException {} - - public static final class HttpPatch extends HttpEntityEnclosingRequestBase { - - public final static String METHOD_NAME = "PATCH"; - - public HttpPatch() { - super(); - } - - public HttpPatch(final URI uri) { - super(); - setURI(uri); - } - - public HttpPatch(final String uri) { - super(); - setURI(URI.create(uri)); - } - - @Override - public String getMethod() { - return METHOD_NAME; - } - - } -} diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpConfig.java b/KJFrame/src/org/kymjs/kjframe/http/HttpConfig.java index 69f0a64c..e7e8a049 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpConfig.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpConfig.java @@ -15,57 +15,84 @@ */ package org.kymjs.kjframe.http; -import java.io.File; +import android.os.Handler; +import android.os.Looper; import org.kymjs.kjframe.utils.FileUtils; -import android.net.http.AndroidHttpClient; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; +import java.io.File; + +import javax.net.ssl.SSLSocketFactory; /** * Http配置器 - * - * @author kymjs - * + * + * @author kymjs (http://www.kymjs.com/) . */ public class HttpConfig { public static boolean DEBUG = true; - /** 缓存文件夹 **/ + /** + * 缓存文件夹 + **/ public static String CACHEPATH = "KJLibrary/cache"; - /** 线程池大小 **/ + /** + * 线程池大小 + **/ public static int NETWORK_POOL_SIZE = 4; - /** Http请求超时时间 **/ + /** + * Http请求超时时间 + **/ public static int TIMEOUT = 5000; - /** 磁盘缓存大小 */ + /** + * 磁盘缓存大小 + */ public static int DISK_CACHE_SIZE = 5 * 1024 * 1024; - /** 缓存有效时间: 默认5分钟 */ + /** + * 缓存有效时间: 默认5分钟 + */ public int cacheTime = 5; - /** 在Http请求中,如果服务器也声明了对缓存时间的控制,那么是否优先使用服务器设置: 默认false */ + /** + * 在Http请求中,如果服务器也声明了对缓存时间的控制,那么是否优先使用服务器设置: 默认false + */ public static boolean useServerControl = false; - /** 为了更真实的模拟网络请求。如果启用,在读取完成以后,并不立即返回而是延迟500毫秒再返回 */ + /** + * 为了更真实的模拟网络请求。如果启用,在读取完成以后,并不立即返回而是延迟500毫秒再返回 + */ public boolean useDelayCache = false; - /** 如果启用了useDelayCache,本属性才有效。单位:ms */ + /** + * 如果启用了useDelayCache,本属性才有效。单位:ms + */ public long delayTime = 500; - /** 同时允许多少个下载任务,建议不要太大(注意:本任务最大值不能超过NETWORK_POOL_SIZE) */ + /** + * 同时允许多少个下载任务,建议不要太大(注意:本任务最大值不能超过NETWORK_POOL_SIZE) + */ public static int MAX_DOWNLOAD_TASK_SIZE = 2; - /** 缓存器 **/ + /** + * 缓存器 + **/ public static Cache mCache; - /** 网络请求执行器 **/ + /** + * 网络请求执行器 + **/ public Network mNetwork; - /** Http响应的分发器 **/ + /** + * Http响应的分发器 + **/ public Delivery mDelivery; - /** 下载控制器队列,对每个下载任务都有一个控制器负责控制下载 */ + /** + * 下载控制器队列,对每个下载任务都有一个控制器负责控制下载 + */ public DownloadTaskQueue mController; - /** 全局的cookie,如果每次Http请求都需要传递固定的cookie,可以设置本项 */ + /** + * 全局的cookie,如果每次Http请求都需要传递固定的cookie,可以设置本项 + */ public static String sCookie; public HttpConfig() { @@ -80,16 +107,21 @@ public HttpConfig() { /** * 创建HTTP请求端的生产器(将抽象工厂缩减为方法) - * + * * @return */ public HttpStack httpStackFactory() { - if (Build.VERSION.SDK_INT >= 11) { return new HttpConnectStack(); - } else { - return new HttpClientStack( - AndroidHttpClient.newInstance("kjframe/0")); - } +// if (Build.VERSION.SDK_INT >= 11) { +// return new HttpConnectStack(); +// } else { +// return new HttpClientStack( +// AndroidHttpClient.newInstance("kjframe/0")); +// } + } + + public HttpStack httpStackFactory(SSLSocketFactory ssl) { + return new HttpConnectStack(null, ssl); } public void setCookieString(String cookie) { diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpConnectStack.java b/KJFrame/src/org/kymjs/kjframe/http/HttpConnectStack.java index 26389633..dfd6c844 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpConnectStack.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpConnectStack.java @@ -29,19 +29,12 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.ProtocolVersion; -import org.apache.http.StatusLine; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicHttpResponse; -import org.apache.http.message.BasicStatusLine; import org.kymjs.kjframe.http.Request.HttpMethod; /** * HttpUrlConnection方式实现 + * + * @author kymjs (http://www.kymjs.com/) . */ public class HttpConnectStack implements HttpStack { @@ -72,7 +65,7 @@ public HttpConnectStack(UrlRewriter urlRewriter, } @Override - public HttpResponse performRequest(Request request, + public KJHttpResponse performRequest(Request request, Map additionalHeaders) throws IOException { String url = request.getUrl(); HashMap map = new HashMap(); @@ -92,18 +85,34 @@ public HttpResponse performRequest(Request request, connection.addRequestProperty(headerName, map.get(headerName)); } setConnectionParametersForRequest(connection, request); + KJHttpResponse response = responseFromConnection(connection); + return response; + } - ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); + private KJHttpResponse responseFromConnection(HttpURLConnection connection) + throws IOException { + KJHttpResponse response = new KJHttpResponse(); int responseCode = connection.getResponseCode(); if (responseCode == -1) { throw new IOException( "Could not retrieve response code from HttpUrlConnection."); } - StatusLine responseStatus = new BasicStatusLine(protocolVersion, - connection.getResponseCode(), connection.getResponseMessage()); + response.setResponseCode(responseCode); + response.setResponseMessage(connection.getResponseMessage()); + // contentStream + InputStream inputStream; + try { + inputStream = connection.getInputStream(); + } catch (IOException ioe) { + inputStream = connection.getErrorStream(); + } + response.setContentStream(inputStream); - BasicHttpResponse response = new BasicHttpResponse(responseStatus); - response.setEntity(entityFromConnection(connection)); + response.setContentLength(connection.getContentLength()); + response.setContentEncoding(connection.getContentEncoding()); + response.setContentType(connection.getContentType()); + // header + Map headerMap = new HashMap(); for (Entry> header : connection.getHeaderFields() .entrySet()) { if (header.getKey() != null) { @@ -111,31 +120,13 @@ public HttpResponse performRequest(Request request, for (String v : header.getValue()) { value += (v + "; "); } - Header h = new BasicHeader(header.getKey(), value); - response.addHeader(h); + headerMap.put(header.getKey(), value); } } + response.setHeaders(headerMap); return response; } - /** - * 从给定的HttpurlConnection创建HttpEntity - */ - private static HttpEntity entityFromConnection(HttpURLConnection connection) { - BasicHttpEntity entity = new BasicHttpEntity(); - InputStream inputStream; - try { - inputStream = connection.getInputStream(); - } catch (IOException ioe) { - inputStream = connection.getErrorStream(); - } - entity.setContent(inputStream); - entity.setContentLength(connection.getContentLength()); - entity.setContentEncoding(connection.getContentEncoding()); - entity.setContentType(connection.getContentType()); - return entity; - } - private HttpURLConnection openConnection(URL url, Request request) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); @@ -147,17 +138,21 @@ private HttpURLConnection openConnection(URL url, Request request) connection.setDoInput(true); // use caller-provided custom SslSocketFactory, if any, for HTTPS - if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) { - ((HttpsURLConnection) connection) - .setSSLSocketFactory(mSslSocketFactory); + if ("https".equals(url.getProtocol())) { + if (mSslSocketFactory != null) { + ((HttpsURLConnection) connection) + .setSSLSocketFactory(mSslSocketFactory); + } else { + // 信任所有证书 + HTTPSTrustManager.allowAllSSL(); + } } - return connection; } - /* package */static void setConnectionParametersForRequest( - HttpURLConnection connection, Request request) - throws IOException { + /* package */ + static void setConnectionParametersForRequest(HttpURLConnection connection, + Request request) throws IOException { switch (request.getMethod()) { case HttpMethod.GET: connection.setRequestMethod("GET"); diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpHeaderParser.java b/KJFrame/src/org/kymjs/kjframe/http/HttpHeaderParser.java index 6443962f..b6b4c0bc 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpHeaderParser.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpHeaderParser.java @@ -16,19 +16,19 @@ package org.kymjs.kjframe.http; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Map; -import org.apache.http.impl.cookie.DateParseException; -import org.apache.http.impl.cookie.DateUtils; -import org.apache.http.protocol.HTTP; - /** * 用于解析HttpHeader的工具类 + * + * @author kymjs (http://www.kymjs.com/) . */ public class HttpHeaderParser { public static Cache.Entry parseCacheHeaders(HttpConfig httpconfig, - NetworkResponse response) { + NetworkResponse response) { long now = System.currentTimeMillis(); Map headers = response.headers; @@ -97,13 +97,14 @@ public static Cache.Entry parseCacheHeaders(HttpConfig httpconfig, /** * 使用RFC1123格式解析服务器返回的时间 - * + * * @return 如果解析异常,返回null */ public static long parseDateAsEpoch(String dateStr) { + SimpleDateFormat sdf = new SimpleDateFormat(); try { - return DateUtils.parseDate(dateStr).getTime(); - } catch (DateParseException e) { + return sdf.parse(dateStr).getTime(); + } catch (ParseException e) { return 0; } } @@ -112,7 +113,7 @@ public static long parseDateAsEpoch(String dateStr) { * 返回这个内容头的编码,如果没有则使用HTTP默认(ISO-8859-1)指定的字符集 */ public static String parseCharset(Map headers) { - String contentType = headers.get(HTTP.CONTENT_TYPE); + String contentType = headers.get("Content-Type"); if (contentType != null) { String[] params = contentType.split(";"); for (int i = 1; i < params.length; i++) { @@ -124,6 +125,6 @@ public static String parseCharset(Map headers) { } } } - return HTTP.DEFAULT_CONTENT_CHARSET; + return "ISO-8859-1"; } } diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpParams.java b/KJFrame/src/org/kymjs/kjframe/http/HttpParams.java index 99047eed..526a3710 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpParams.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpParams.java @@ -16,6 +16,12 @@ package org.kymjs.kjframe.http; +import android.text.TextUtils; +import android.util.Log; + +import org.kymjs.kjframe.utils.FileUtils; +import org.kymjs.kjframe.utils.StringUtils; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -24,35 +30,35 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.message.BasicHeader; -import org.kymjs.kjframe.utils.FileUtils; - -import android.text.TextUtils; -import android.util.Log; - /** * Http请求的参数集合 + * + * @author kymjs (http://www.kymjs.com/) . */ -public class HttpParams implements HttpEntity { +public class HttpParams implements Serializable { - private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - .toCharArray(); + private final static char[] MULTIPART_CHARS = + "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + .toCharArray(); private String mBoundary = null; private final String NEW_LINE_STR = "\r\n"; private final String CONTENT_TYPE = "Content-Type: "; private final String CONTENT_DISPOSITION = "Content-Disposition: "; - /** 文本参数和字符集 */ + /** + * 文本参数和字符集 + */ private final String TYPE_TEXT_CHARSET = "text/plain; charset=UTF-8"; - /** 字节流参数 */ + /** + * 字节流参数 + */ private final String TYPE_OCTET_STREAM = "application/octet-stream"; /** * 二进制参数 @@ -81,10 +87,8 @@ public HttpParams() { /** * 生成分隔符 - * - * @return */ - private final String generateBoundary() { + private String generateBoundary() { final StringBuffer buf = new StringBuffer(); final Random rand = new Random(); for (int i = 0; i < 30; i++) { @@ -111,9 +115,6 @@ public void putJsonParams(String json) { /** * 添加文本参数 - * - * @param key - * @param value */ public void put(final String key, final String value) { urlParams.put(key, value); @@ -123,9 +124,6 @@ public void put(final String key, final String value) { /** * 添加二进制参数, 例如Bitmap的字节流参数 - * - * @param paramName - * @param rawData */ public void put(String paramName, final byte[] rawData) { hasFile = true; @@ -135,9 +133,6 @@ public void put(String paramName, final byte[] rawData) { /** * 添加文件参数,可以实现文件上传功能 - * - * @param key - * @param file */ public void put(final String key, final File file) { try { @@ -152,15 +147,9 @@ public void put(final String key, final File file) { /** * 将数据写入到输出流中 - * - * @param key - * @param rawData - * @param type - * @param encodingBytes - * @param fileName */ private void writeToOutputStream(String paramName, byte[] rawData, - String type, byte[] encodingBytes, String fileName) { + String type, byte[] encodingBytes, String fileName) { try { writeFirstBoundary(); mOutputStream @@ -177,7 +166,7 @@ private void writeToOutputStream(String paramName, byte[] rawData, /** * 参数开头的分隔符 - * + * * @throws IOException */ private void writeFirstBoundary() throws IOException { @@ -186,52 +175,42 @@ private void writeFirstBoundary() throws IOException { private byte[] getContentDispositionBytes(String paramName, String fileName) { StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("--").append(mBoundary).append("\r\n"); - stringBuilder.append(CONTENT_DISPOSITION + "form-data; name=\"" - + paramName + "\""); + stringBuilder.append("--").append(mBoundary).append("\r\n").append(CONTENT_DISPOSITION) + .append("form-data; name=\"").append(paramName).append("\""); if (!TextUtils.isEmpty(fileName)) { - stringBuilder.append("; filename=\"" + fileName + "\""); + stringBuilder.append("; filename=\"").append(fileName).append("\""); } return stringBuilder.append(NEW_LINE_STR).toString().getBytes(); } - @Override public long getContentLength() { return mOutputStream.toByteArray().length; } - @Override - public Header getContentType() { - if (contentType != null) { - return new BasicHeader("Content-Type", contentType); - } - if (hasFile) { - return new BasicHeader("Content-Type", - "multipart/form-data; boundary=" + mBoundary); + public String getContentType() { + //如果contentType没有被自定义,且参数集包含文件,则使用有文件的contentType + if (hasFile && contentType == null) { + contentType = "multipart/form-data; boundary=" + mBoundary; } - return null; + return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } - @Override public boolean isChunked() { return false; } - @Override public boolean isRepeatable() { return false; } - @Override public boolean isStreaming() { return false; } - @Override public void writeTo(final OutputStream outstream) throws IOException { if (hasFile) { // 参数最末尾的结束符 @@ -240,12 +219,11 @@ public void writeTo(final OutputStream outstream) throws IOException { mOutputStream.write(endString.getBytes()); // outstream.write(mOutputStream.toByteArray()); - } else { + } else if (!StringUtils.isEmpty(getUrlParams())) { outstream.write(getUrlParams().substring(1).getBytes()); } } - @Override public void consumeContent() throws IOException, UnsupportedOperationException { if (isStreaming()) { @@ -254,7 +232,6 @@ public void consumeContent() throws IOException, } } - @Override public InputStream getContent() { return new ByteArrayInputStream(mOutputStream.toByteArray()); } @@ -285,8 +262,7 @@ public Map getHeaders() { return mHeaders; } - @Override - public Header getContentEncoding() { + public Map getContentEncoding() { return null; } } diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpStack.java b/KJFrame/src/org/kymjs/kjframe/http/HttpStack.java index 0b7383e6..82faba5d 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpStack.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpStack.java @@ -19,28 +19,22 @@ import java.io.IOException; import java.util.Map; -import org.apache.http.HttpResponse; - /** * Http请求端,已知实现类: - * + * + * @author kymjs (http://www.kymjs.com/) . * @see HttpConnectStack - * @see HttpClientStack - * - * @author kymjs - * + * 过时移除 HttpClientStack */ public interface HttpStack { /** * 让Http请求端去发起一个Request - * - * @param request - * 一次实际请求集合 - * @param additionalHeaders - * Http请求头 + * + * @param request 一次实际请求集合 + * @param additionalHeaders Http请求头 * @return 一个Http响应 */ - public HttpResponse performRequest(Request request, - Map additionalHeaders) throws IOException; + KJHttpResponse performRequest(Request request, + Map additionalHeaders) throws IOException; } diff --git a/KJFrame/src/org/kymjs/kjframe/http/HttpUtils.java b/KJFrame/src/org/kymjs/kjframe/http/HttpUtils.java index b59faf87..ddfebe93 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/HttpUtils.java +++ b/KJFrame/src/org/kymjs/kjframe/http/HttpUtils.java @@ -15,28 +15,29 @@ */ package org.kymjs.kjframe.http; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.GZIPInputStream; +import android.text.TextUtils; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.protocol.HTTP; import org.kymjs.kjframe.utils.KJLoger; -import android.text.TextUtils; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.zip.GZIPInputStream; +/** + * Http请求工具类 + * + * @author kymjs (http://www.kymjs.com/) . + */ public class HttpUtils { - public static byte[] responseToBytes(HttpResponse response) + public static byte[] responseToBytes(KJHttpResponse response) throws IOException, KJHttpException { - HttpEntity entity = response.getEntity(); PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream( - ByteArrayPool.get(), (int) entity.getContentLength()); + ByteArrayPool.get(), (int) response.getContentLength()); byte[] buffer = null; try { - InputStream in = entity.getContent(); + InputStream in = response.getContentStream(); if (isGzipContent(response) && !(in instanceof GZIPInputStream)) { in = new GZIPInputStream(in); } @@ -55,7 +56,8 @@ public static byte[] responseToBytes(HttpResponse response) try { // Close the InputStream and release the resources by // "consuming the content". - entity.consumeContent(); +// entity.consumeContent(); + response.getContentStream().close(); } catch (IOException e) { // This can happen if there was an exception above that left the // entity in @@ -67,11 +69,13 @@ public static byte[] responseToBytes(HttpResponse response) } } - /** Returns the charset specified in the Content-Type of this header. */ - public static String getCharset(HttpResponse response) { - Header header = response.getFirstHeader(HTTP.CONTENT_TYPE); + /** + * Returns the charset specified in the Content-Type of this header. + */ + public static String getCharset(KJHttpResponse response) { + Map header = response.getHeaders(); if (header != null) { - String contentType = header.getValue(); + String contentType = header.get("Content-Type"); if (!TextUtils.isEmpty(contentType)) { String[] params = contentType.split(";"); for (int i = 1; i < params.length; i++) { @@ -87,12 +91,11 @@ public static String getCharset(HttpResponse response) { return null; } - public static String getHeader(HttpResponse response, String key) { - Header header = response.getFirstHeader(key); - return header == null ? null : header.getValue(); + public static String getHeader(KJHttpResponse response, String key) { + return response.getHeaders().get(key); } - public static boolean isSupportRange(HttpResponse response) { + public static boolean isSupportRange(KJHttpResponse response) { if (TextUtils.equals(getHeader(response, "Accept-Ranges"), "bytes")) { return true; } @@ -100,7 +103,7 @@ public static boolean isSupportRange(HttpResponse response) { return value != null && value.startsWith("bytes"); } - public static boolean isGzipContent(HttpResponse response) { + public static boolean isGzipContent(KJHttpResponse response) { return TextUtils .equals(getHeader(response, "Content-Encoding"), "gzip"); } diff --git a/KJFrame/src/org/kymjs/kjframe/http/KJAsyncTask.java b/KJFrame/src/org/kymjs/kjframe/http/KJAsyncTask.java index 13514e4e..256ba001 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/KJAsyncTask.java +++ b/KJFrame/src/org/kymjs/kjframe/http/KJAsyncTask.java @@ -1 +1 @@ -/* * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.http; import java.util.ArrayDeque; import java.util.Stack; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.kymjs.kjframe.utils.KJLoger; import android.os.Handler; import android.os.Message; /** * 本类修改自 The Android Open SourceProject
* 使用高并发的线程池并发执行异步任务,用于替换Android自带的AsyncTask,达到多线程执行的最大效率
* 使用适配器设计思想如果开发者需要使用串行执行Task任务,可手动调用 * setDefaultExecutor(KJTaskExecutor.mSerialExecutor)方法
* * 优化点:采用并发替代了系统的串行执行,同时修复了2.3之前并行执行大量数据是FC的问题。
* 创建时间 2014-2-28
* 修改时间 2014-10-24
* * @param * 启动参数类型 * @param * 进度返回类型 * @param * 结果返回类型 * @author kymjs (https://github.com/kymjs) * @version 1.2 */ public abstract class KJAsyncTask { private static final int CPU_COUNT = Runtime.getRuntime() .availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT;// 长期保持活的跃线程数。 private static final int MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;// 线程池最大容量 // 当前线程数大于活跃线程数时,此为终止多余的空闲线程等待新任务的最长时间 private static final int KEEP_ALIVE = 10; private static final int MESSAGE_POST_RESULT = 0x1;// 消息类型:发送结果 private static final int MESSAGE_POST_PROGRESS = 0x2;// 消息类型:更新进度 private static final int MESSAGE_POST_FINISH = 0x3;// 消息类型:异步执行完成 // 用来发送结果和进度通知,采用UI线程的Looper来处理消息 这就是为什么Task必须在UI线程调用 private static final InternalHandler mHandler = new InternalHandler(); // 工作线程 private final WorkerRunnable mWorker; // 待执行的runnable private final FutureTask mFuture; // 静态阻塞式队列,用来存放待执行的任务,初始容量:8个 private static final BlockingQueue mPoolWorkQueue = new LinkedBlockingQueue( 8); // 原子布尔型,支持高并发访问,标识任务是否被取消 private final AtomicBoolean mCancelled = new AtomicBoolean(); // 原子布尔型,支持高并发访问,标识任务是否被使用过 private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); private static OnFinishedListener finishedListener; // 任务的状态 默认为挂起,即等待执行,其类型标识为易变的(volatile) private volatile Status mStatus = Status.PENDING; // 任务的三种状态 public enum Status { /** 任务等待执行 */ PENDING, /** 任务正在执行 */ RUNNING, /** 任务已经执行结束 */ FINISHED, } // ThreadFactory,通过工厂方法newThread来获取新线程 private static final ThreadFactory mThreadFactory = new ThreadFactory() { // 原子级整数,可以在超高并发下正常工作 private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "KJLibrary->KJTaskExecutor #" + mCount.getAndIncrement()); } }; /************************** 三种任务执行器的定义 *******************************/ /** * 并发线程池任务执行器,它实际控制并执行线程任务,与mSerialExecutor(串行)相对应
* 优化: 不限制并发总线程数!让任务总能得到执行,且高效执行少量(不大于活跃线程数)的异步任务。
* 线程完成任务后保持10秒销毁,这段时间内可重用以应付短时间内较大量并发,提升性能。 */ public static final ThreadPoolExecutor mThreadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, mPoolWorkQueue, mThreadFactory); /** * 并发量控制: 根据cpu能力控制一段时间内并发数量,并发过量大时采用Lru方式移除旧的异步任务,默认采用LIFO策略调度线程运作, * 开发者可选调度策略有LIFO、FIFO。 */ public static final Executor mLruSerialExecutor = new SmartSerialExecutor(); /** * 串行任务执行器,其内部实现了串行控制, 循环的取出一个个任务交给上述的并发线程池去执行
* 与mThreadPoolExecutor(并行)相对应 */ public static final Executor mSerialExecutor = new SerialExecutor(); // 设置默认任务执行器为并行执行 private static volatile Executor mDefaultExecutor = mLruSerialExecutor; /** 为KJTaskExecutor设置默认执行器 */ public static void setDefaultExecutor(Executor exec) { mDefaultExecutor = exec; } /** * 创建一个asynchronous task,这个构造器必须运行于UI线程 */ public KJAsyncTask() { mWorker = new WorkerRunnable() { @Override public Result call() throws Exception { mTaskInvoked.set(true); // 设置线程优先级 android.os.Process .setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); return postResult(doInBackground(mParams)); } }; mFuture = new FutureTask(mWorker) { @Override protected void done() { try { if (!mTaskInvoked.get()) { postResult(get()); } } catch (InterruptedException e) { KJLoger.debugLog(getClass().getName(), e.getMessage()); } catch (ExecutionException e) { throw new RuntimeException( "An error occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { if (!mTaskInvoked.get()) { postResult(null); } } } }; } /** * doInBackground执行完毕,发送消息 * * @param result * @return */ private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT, new KJTaskResult(this, result)); message.sendToTarget(); return result; } /*********************** method ***************************/ /** * 耗时执行监听器 * * @return */ public OnFinishedListener getFinishedListener() { return finishedListener; } /** * 设置耗时执行监听器 * * @param finishedListener */ public static void setOnFinishedListener(OnFinishedListener finishedListener) { KJAsyncTask.finishedListener = finishedListener; } /** * 返回任务的状态 */ public final Status getStatus() { return mStatus; } /** * 返回该线程是否已经被取消 * * @see #cancel(boolean) */ public final boolean isCancelled() { return mCancelled.get(); } /** * 如果task已经执行完成,或被某些其他原因取消,再调用本方法将返回false;
* 当本task还没有启动就调用cancel(boolean),那么这个task将从来没有运行,此时会返回true。
* 如果任务已经启动,则由参数决定执行此任务是否被中断。
* * @param mayInterruptIfRunning * true 表示取消task的执行 * @return 如果线程不能被取消返回false, 比如它已经正常完成 */ public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); } /** * Waits if necessary for the computation to complete, and then retrieves * its result. * * @return The computed result. * * @throws CancellationException * If the computation was cancelled. * @throws ExecutionException * If the computation threw an exception. * @throws InterruptedException * If the current thread was interrupted while waiting. */ public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } /** * Waits if necessary for at most the given time for the computation to * complete, and then retrieves its result. * * @param timeout * Time to wait before cancelling the operation. * @param unit * The time unit for the timeout. * * @return The computed result. * * @throws CancellationException * If the computation was cancelled. * @throws ExecutionException * If the computation threw an exception. * @throws InterruptedException * If the current thread was interrupted while waiting. * @throws TimeoutException * If the wait timed out. */ public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return mFuture.get(timeout, unit); } /*********************** start 一个完整的执行周期 ***************************/ /** * 在doInBackground之前调用,用来做初始化工作 所在线程:UI线程 */ protected void onPreExecute() {} /** * 这个方法是我们必须要重写的,用来做后台计算 所在线程:后台线程 */ protected abstract Result doInBackground(Params... params); /** * 打印后台计算进度,onProgressUpdate会被调用
* 使用内部handle发送一个进度消息,让onProgressUpdate被调用 */ protected final void publishProgress(Progress... values) { if (!isCancelled()) { mHandler.obtainMessage(MESSAGE_POST_PROGRESS, new KJTaskResult(this, values)).sendToTarget(); } } /** * 在publishProgress之后调用,用来更新计算进度 所在线程:UI线程 */ protected void onProgressUpdate(Progress... values) {} /** * 任务结束的时候会进行判断:如果任务没有被取消,则调用onPostExecute;否则调用onCancelled */ private void finish(Result result) { if (isCancelled()) { onCancelled(result); if (finishedListener != null) { finishedListener.onCancelled(); } } else { onPostExecute(result); if (finishedListener != null) { finishedListener.onPostExecute(); } } mStatus = Status.FINISHED; } /** * 在doInBackground之后调用,用来接受后台计算结果更新UI 所在线程:UI线程 */ protected void onPostExecute(Result result) {} /** * 所在线程:UI线程
* doInBackground执行结束并且{@link #cancel(boolean)} 被调用。
* 如果本函数被调用则表示任务已被取消,这个时候onPostExecute不会再被调用。 */ protected void onCancelled(Result result) {} /*********************** end 一个完整的执行周期 ***************************/ /*********************** core method ***************************/ /** * 这个方法必须在UI线程中调用
* Note:这个函数将按照任务队列去串行执行后台线程或并发执行线程,这依赖于platform * version,从1.6到3.0是并行,3.0以后为串行(为了避免AsyncTask所带来的并发错误), 如果你一定要并行执行,你可以调用 * {@link #executeOnExecutor}替代这个方法,并将默认的执行器改为{@link #mThreadPoolExecutor} * * @param params * The parameters of the task. * @return This instance of KJTaskExecutor. * @throws IllegalStateException * If {@link #getStatus()} returns either */ public final KJAsyncTask execute(Params... params) { return executeOnExecutor(mDefaultExecutor, params); } /** * 必须在UI线程调用此方法
* 通过这个方法我们可以自定义KJTaskExecutor的执行方式,串行or并行,甚至可以采用自己的Executor 为了实现并行, * asyncTask.executeOnExecutor(KJTaskExecutor.mThreadPoolExecutor, params); */ public final KJAsyncTask executeOnExecutor( Executor exec, Params... params) { if (mStatus != Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); default: break; } } mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture);// 原理{@link #execute(Runnable runnable)} // 接着会有#onProgressUpdate被调用,最后是#onPostExecute return this; } /** * 提供一个静态方法,方便在外部直接执行一个runnable
* 用于瞬间大量并发的场景,比如,假设用户拖动ListView时如果需要启动大量异步线程,而拖动过去时间很久的用户已经看不到,允许任务丢失。 */ public static void execute(Runnable runnable) { mDefaultExecutor.execute(runnable); } /** * KJTaskExecutor内部Handler,用来发送后台计算进度更新消息和计算完成消息 */ private static class InternalHandler extends Handler { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public void handleMessage(Message msg) { KJTaskResult result = (KJTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; case MESSAGE_POST_FINISH: if (finishedListener != null) { finishedListener.onPostExecute(); } break; } } } private static abstract class WorkerRunnable implements Callable { Params[] mParams; } private static class KJTaskResult { final Data[] mData; final KJAsyncTask mTask; KJTaskResult(KJAsyncTask task, Data... data) { mTask = task; mData = data; } } /** * 串行执行器的实现
* 如果采用串行执行,asyncTask.execute(Params ...)实际上会调用 SerialExecutor的execute方法。 * {@link #executeOnExecutor} */ private static class SerialExecutor implements Executor { // 线性双向队列,用来存储所有的AsyncTask任务 final ArrayDeque mTasks = new ArrayDeque(); /** 当前正在执行的AsyncTask任务 */ Runnable mActive = null; @Override public synchronized void execute(final Runnable r) { // 将task任务加入到SerialExecutor的双向队列中,也就是让task排队执行 mTasks.offer(new Runnable() { @Override public void run() { try { r.run(); } finally { // 当前task执行完毕后,安排下一个执行 scheduleNext(); } } }); // 如果当前没有任务在执行,直接进入执行逻辑 if (mActive == null) { scheduleNext(); } } /** * 类似适配器设计模式,如果是并行执行任务就不调用上面的方法而直接使用并发执行者执行任务
* 如果是串行执行任务, 就配合上面的函数将原本是并发执行的代码转换成串行执行 */ protected synchronized void scheduleNext() { // 从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行 if ((mActive = mTasks.poll()) != null) { mThreadPoolExecutor.execute(mActive); } } } /** * 用于替换掉原生的mThreadPoolExecutor,可以大大改善Android自带异步任务框架的处理能力和速度。 * 默认使用LIFO(后进先出)策略来调度线程,可将最新的任务快速执行,当然你自己可以换为FIFO调度策略。 * 这有助于用户当前任务优先完成(比如加载图片时,很容易做到当前屏幕上的图片优先加载)。 */ private static class SmartSerialExecutor implements Executor { /** * 这里使用{@link ArrayDequeCompat}作为栈比{@link Stack}性能高 */ private final ArrayDeque mQueue = new ArrayDeque( serialMaxCount); private final ScheduleStrategy mStrategy = ScheduleStrategy.LIFO; private enum ScheduleStrategy { LIFO, FIFO; } /** * 一次同时并发的数量,根据处理器数量调节
* cpu count : 1 2 3 4 8 16 32
* once(base*2): 1 2 3 4 8 16 32
* 一个时间段内最多并发线程个数: 双核手机:2 四核手机:4 ... 计算公式如下: */ private static int serialOneTime; /** * 并发最大数量,当投入的任务过多大于此值时,根据Lru规则,将最老的任务移除(将得不到执行)
* cpu count : 1 2 3 4 8 16 32
* base(cpu+3) : 4 5 6 7 11 19 35
* max(base*16): 64 80 96 112 176 304 560
*/ private static int serialMaxCount; private void reSettings(int cpuCount) { serialOneTime = cpuCount; serialMaxCount = (cpuCount + 3) * 16; } public SmartSerialExecutor() { reSettings(CPU_COUNT); } @Override public synchronized void execute(final Runnable command) { Runnable r = new Runnable() { @Override public void run() { command.run(); next(); mHandler.sendEmptyMessage(MESSAGE_POST_FINISH); } }; if ((mThreadPoolExecutor).getActiveCount() < serialOneTime) { // 小于单次并发量直接运行 mThreadPoolExecutor.execute(r); } else { // 如果大于并发上限,那么移除最老的任务 if (mQueue.size() >= serialMaxCount) { mQueue.pollFirst(); } // 新任务放在队尾 mQueue.offerLast(r); } } public synchronized void next() { Runnable mActive; switch (mStrategy) { case LIFO: mActive = mQueue.pollLast(); break; case FIFO: mActive = mQueue.pollFirst(); break; default: mActive = mQueue.pollLast(); break; } if (mActive != null) { mThreadPoolExecutor.execute(mActive); } } } public static abstract class OnFinishedListener { public void onCancelled() {} public void onPostExecute() {} } } \ No newline at end of file +/* * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.http; import android.os.Handler; import android.os.Message; import org.kymjs.kjframe.utils.KJLoger; import java.util.ArrayDeque; import java.util.Stack; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * 本类修改自 The Android Open SourceProject
* 使用高并发的线程池并发执行异步任务,用于替换Android自带的AsyncTask,达到多线程执行的最大效率
* 使用适配器设计思想如果开发者需要使用串行执行Task任务,可手动调用 * setDefaultExecutor(KJTaskExecutor.mSerialExecutor)方法
* * 优化点:采用并发替代了系统的串行执行,同时修复了2.3之前并行执行大量数据是FC的问题。
* 创建时间 2014-2-28
* 修改时间 2014-10-24
* * @param 启动参数类型 * @param 进度返回类型 * @param 结果返回类型 * @author kymjs (http://www.kymjs.com/) . * @version 1.2 */ public abstract class KJAsyncTask { private static final int CPU_COUNT = Runtime.getRuntime() .availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT;// 长期保持活的跃线程数。 private static final int MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;// 线程池最大容量 // 当前线程数大于活跃线程数时,此为终止多余的空闲线程等待新任务的最长时间 private static final int KEEP_ALIVE = 10; private static final int MESSAGE_POST_RESULT = 0x1;// 消息类型:发送结果 private static final int MESSAGE_POST_PROGRESS = 0x2;// 消息类型:更新进度 // 用来发送结果和进度通知,采用UI线程的Looper来处理消息 这就是为什么Task必须在UI线程调用 private static final InternalHandler mHandler = new InternalHandler(); // 工作线程 private final WorkerRunnable mWorker; // 待执行的runnable private final FutureTask mFuture; // 静态阻塞式队列,用来存放待执行的任务,初始容量:8个 private static final BlockingQueue mPoolWorkQueue = new LinkedBlockingQueue( 8); // 原子布尔型,支持高并发访问,标识任务是否被取消 private final AtomicBoolean mCancelled = new AtomicBoolean(); // 原子布尔型,支持高并发访问,标识任务是否被使用过 private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); // 任务的状态 默认为挂起,即等待执行,其类型标识为易变的(volatile) private volatile Status mStatus = Status.PENDING; // 任务的三种状态 public enum Status { /** * 任务等待执行 */ PENDING, /** * 任务正在执行 */ RUNNING, /** * 任务已经执行结束 */ FINISHED, } // ThreadFactory,通过工厂方法newThread来获取新线程 private static final ThreadFactory mThreadFactory = new ThreadFactory() { // 原子级整数,可以在超高并发下正常工作 private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "KJLibrary->KJTaskExecutor #" + mCount.getAndIncrement()); } }; /************************** 三种任务执行器的定义 *******************************/ /** * 并发线程池任务执行器,它实际控制并执行线程任务,与mSerialExecutor(串行)相对应
* 优化: 不限制并发总线程数!让任务总能得到执行,且高效执行少量(不大于活跃线程数)的异步任务。
* 线程完成任务后保持10秒销毁,这段时间内可重用以应付短时间内较大量并发,提升性能。 */ public static final ThreadPoolExecutor mThreadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, mPoolWorkQueue, mThreadFactory); /** * 并发量控制: 根据cpu能力控制一段时间内并发数量,并发过量大时采用Lru方式移除旧的异步任务,默认采用LIFO策略调度线程运作, * 开发者可选调度策略有LIFO、FIFO。 */ public static final Executor mLruSerialExecutor = new SmartSerialExecutor(); /** * 串行任务执行器,其内部实现了串行控制, 循环的取出一个个任务交给上述的并发线程池去执行
* 与mThreadPoolExecutor(并行)相对应 */ public static final Executor mSerialExecutor = new SerialExecutor(); // 设置默认任务执行器为并行执行 private static volatile Executor mDefaultExecutor = mLruSerialExecutor; /** * 为KJTaskExecutor设置默认执行器 */ public static void setDefaultExecutor(Executor exec) { mDefaultExecutor = exec; } /** * 创建一个asynchronous task,这个构造器必须运行于UI线程 */ public KJAsyncTask() { mWorker = new WorkerRunnable() { @Override public Result call() throws Exception { mTaskInvoked.set(true); // 设置线程优先级 android.os.Process .setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); return postResult(doInBackground(mParams)); } }; mFuture = new FutureTask(mWorker) { @Override protected void done() { try { if (!mTaskInvoked.get()) { postResult(get()); } } catch (InterruptedException e) { KJLoger.debugLog(getClass().getName(), e.getMessage()); } catch (ExecutionException e) { throw new RuntimeException( "An error occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { if (!mTaskInvoked.get()) { postResult(null); } } } }; } /** * doInBackground执行完毕,发送消息 * * @param result * @return */ private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT, new KJTaskResult(this, result)); message.sendToTarget(); return result; } /*********************** method ***************************/ /** * 返回任务的状态 */ public final Status getStatus() { return mStatus; } /** * 返回该线程是否已经被取消 * * @see #cancel(boolean) */ public final boolean isCancelled() { return mCancelled.get(); } /** * 如果task已经执行完成,或被某些其他原因取消,再调用本方法将返回false;
* 当本task还没有启动就调用cancel(boolean),那么这个task将从来没有运行,此时会返回true。
* 如果任务已经启动,则由参数决定执行此任务是否被中断。
* * @param mayInterruptIfRunning true 表示取消task的执行 * @return 如果线程不能被取消返回false, 比如它已经正常完成 */ public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); } /** * Waits if necessary for the computation to complete, and then retrieves * its result. * * @return The computed result. * @throws CancellationException If the computation was cancelled. * @throws ExecutionException If the computation threw an exception. * @throws InterruptedException If the current thread was interrupted while waiting. */ public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } /** * Waits if necessary for at most the given time for the computation to * complete, and then retrieves its result. * * @param timeout Time to wait before cancelling the operation. * @param unit The time unit for the timeout. * @return The computed result. * @throws CancellationException If the computation was cancelled. * @throws ExecutionException If the computation threw an exception. * @throws InterruptedException If the current thread was interrupted while waiting. * @throws TimeoutException If the wait timed out. */ public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return mFuture.get(timeout, unit); } /*********************** start 一个完整的执行周期 ***************************/ /** * 在doInBackground之前调用,用来做初始化工作 所在线程:UI线程 */ protected void onPreExecute() { } /** * 这个方法是我们必须要重写的,用来做后台计算 所在线程:后台线程 */ protected abstract Result doInBackground(Params... params); /** * 打印后台计算进度,onProgressUpdate会被调用
* 使用内部handle发送一个进度消息,让onProgressUpdate被调用 */ protected final void publishProgress(Progress... values) { if (!isCancelled()) { mHandler.obtainMessage(MESSAGE_POST_PROGRESS, new KJTaskResult(this, values)).sendToTarget(); } } /** * 在publishProgress之后调用,用来更新计算进度 所在线程:UI线程 */ protected void onProgressUpdate(Progress... values) { } /** * 任务结束的时候会进行判断:如果任务没有被取消,则调用onPostExecute;否则调用onCancelled */ private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; } /** * 在doInBackground之后调用,用来接受后台计算结果更新UI 所在线程:UI线程 */ protected void onPostExecute(Result result) { } /** * 所在线程:UI线程
* doInBackground执行结束并且{@link #cancel(boolean)} 被调用。
* 如果本函数被调用则表示任务已被取消,这个时候onPostExecute不会再被调用。 */ protected void onCancelled(Result result) { } /*********************** end 一个完整的执行周期 ***************************/ /*********************** core method ***************************/ /** * 这个方法必须在UI线程中调用
* Note:这个函数将按照任务队列去串行执行后台线程或并发执行线程,这依赖于platform * version,从1.6到3.0是并行,3.0以后为串行(为了避免AsyncTask所带来的并发错误), 如果你一定要并行执行,你可以调用 * {@link #executeOnExecutor}替代这个方法,并将默认的执行器改为{@link #mThreadPoolExecutor} * * @param params The parameters of the task. * @return This instance of KJTaskExecutor. * @throws IllegalStateException If {@link #getStatus()} returns either */ public final KJAsyncTask execute(Params... params) { return executeOnExecutor(mDefaultExecutor, params); } /** * 必须在UI线程调用此方法
* 通过这个方法我们可以自定义KJTaskExecutor的执行方式,串行or并行,甚至可以采用自己的Executor 为了实现并行, * asyncTask.executeOnExecutor(KJTaskExecutor.mThreadPoolExecutor, params); */ public final KJAsyncTask executeOnExecutor( Executor exec, Params... params) { if (mStatus != Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); default: break; } } mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture);// 原理{@link #execute(Runnable runnable)} // 接着会有#onProgressUpdate被调用,最后是#onPostExecute return this; } /** * 提供一个静态方法,方便在外部直接执行一个runnable
* 用于瞬间大量并发的场景,比如,假设用户拖动ListView时如果需要启动大量异步线程,而拖动过去时间很久的用户已经看不到,允许任务丢失。 */ public static void execute(Runnable runnable) { mDefaultExecutor.execute(runnable); } /** * KJTaskExecutor内部Handler,用来发送后台计算进度更新消息和计算完成消息 */ private static class InternalHandler extends Handler { @Override @SuppressWarnings({"unchecked", "rawtypes"}) public void handleMessage(Message msg) { KJTaskResult result = (KJTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } } private static abstract class WorkerRunnable implements Callable { Params[] mParams; } private static class KJTaskResult { final Data[] mData; final KJAsyncTask mTask; KJTaskResult(KJAsyncTask task, Data... data) { mTask = task; mData = data; } } /** * 串行执行器的实现
* 如果采用串行执行,asyncTask.execute(Params ...)实际上会调用 SerialExecutor的execute方法。 * {@link #executeOnExecutor} */ private static class SerialExecutor implements Executor { // 线性双向队列,用来存储所有的AsyncTask任务 final ArrayDeque mTasks = new ArrayDeque(); /** * 当前正在执行的AsyncTask任务 */ Runnable mActive = null; @Override public synchronized void execute(final Runnable r) { // 将task任务加入到SerialExecutor的双向队列中,也就是让task排队执行 mTasks.offer(new Runnable() { @Override public void run() { try { r.run(); } finally { // 当前task执行完毕后,安排下一个执行 scheduleNext(); } } }); // 如果当前没有任务在执行,直接进入执行逻辑 if (mActive == null) { scheduleNext(); } } /** * 类似适配器设计模式,如果是并行执行任务就不调用上面的方法而直接使用并发执行者执行任务
* 如果是串行执行任务, 就配合上面的函数将原本是并发执行的代码转换成串行执行 */ protected synchronized void scheduleNext() { // 从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行 if ((mActive = mTasks.poll()) != null) { mThreadPoolExecutor.execute(mActive); } } } /** * 用于替换掉原生的mThreadPoolExecutor,可以大大改善Android自带异步任务框架的处理能力和速度。 * 默认使用LIFO(后进先出)策略来调度线程,可将最新的任务快速执行,当然你自己可以换为FIFO调度策略。 * 这有助于用户当前任务优先完成(比如加载图片时,很容易做到当前屏幕上的图片优先加载)。 */ private static class SmartSerialExecutor implements Executor { /** * 这里使用 ArrayDequeCompat 作为栈比{@link Stack}性能高 */ private final ArrayDeque mQueue = new ArrayDeque( serialMaxCount); private final ScheduleStrategy mStrategy = ScheduleStrategy.LIFO; private enum ScheduleStrategy { LIFO, FIFO; } /** * 一次同时并发的数量,根据处理器数量调节
* cpu count : 1 2 3 4 8 16 32
* once(base*2): 1 2 3 4 8 16 32
* 一个时间段内最多并发线程个数: 双核手机:2 四核手机:4 ... 计算公式如下: */ private static int serialOneTime; /** * 并发最大数量,当投入的任务过多大于此值时,根据Lru规则,将最老的任务移除(将得不到执行)
* cpu count : 1 2 3 4 8 16 32
* base(cpu+3) : 4 5 6 7 11 19 35
* max(base*16): 64 80 96 112 176 304 560
*/ private static int serialMaxCount; private void reSettings(int cpuCount) { serialOneTime = cpuCount; serialMaxCount = (cpuCount + 3) * 16; } public SmartSerialExecutor() { reSettings(CPU_COUNT); } @Override public synchronized void execute(final Runnable command) { Runnable r = new Runnable() { @Override public void run() { command.run(); next(); } }; if ((mThreadPoolExecutor).getActiveCount() < serialOneTime) { // 小于单次并发量直接运行 mThreadPoolExecutor.execute(r); } else { // 如果大于并发上限,那么移除最老的任务 if (mQueue.size() >= serialMaxCount) { mQueue.pollFirst(); } // 新任务放在队尾 mQueue.offerLast(r); } } public synchronized void next() { Runnable mActive; switch (mStrategy) { case LIFO: mActive = mQueue.pollLast(); break; case FIFO: mActive = mQueue.pollFirst(); break; default: mActive = mQueue.pollLast(); break; } if (mActive != null) { mThreadPoolExecutor.execute(mActive); } } } public static abstract class OnFinishedListener { public void onCancelled() { } public void onPostExecute() { } } } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/http/KJHttpException.java b/KJFrame/src/org/kymjs/kjframe/http/KJHttpException.java index 2548ddc9..e057e7de 100644 --- a/KJFrame/src/org/kymjs/kjframe/http/KJHttpException.java +++ b/KJFrame/src/org/kymjs/kjframe/http/KJHttpException.java @@ -18,6 +18,8 @@ /** * 整个框架异常的基类 + * + * @author kymjs (http://www.kymjs.com/) . */ @SuppressWarnings("serial") public class KJHttpException extends Exception { diff --git a/KJFrame/src/org/kymjs/kjframe/http/Network.java b/KJFrame/src/org/kymjs/kjframe/http/Network.java index b1e2cc08..3f127f2d 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/Network.java +++ b/KJFrame/src/org/kymjs/kjframe/http/Network.java @@ -16,25 +16,22 @@ package org.kymjs.kjframe.http; +import org.kymjs.kjframe.utils.KJLoger; + import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.SocketTimeoutException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.impl.cookie.DateUtils; -import org.kymjs.kjframe.utils.KJLoger; - /** * 网络请求执行器,将传入的Request使用HttpStack客户端发起网络请求,并返回一个NetworkRespond结果 + * + * @author kymjs (http://www.kymjs.com/) . */ public class Network { protected static final boolean DEBUG = HttpConfig.DEBUG; @@ -46,16 +43,15 @@ public Network(HttpStack httpStack) { /** * 实际执行一个请求的方法 - * - * @param request - * 一个请求任务 + * + * @param request 一个请求任务 * @return 一个不会为null的响应 * @throws KJHttpException */ public NetworkResponse performRequest(Request request) throws KJHttpException { while (true) { - HttpResponse httpResponse = null; + KJHttpResponse httpResponse = null; byte[] responseContents = null; Map responseHeaders = new HashMap(); try { @@ -64,9 +60,8 @@ public NetworkResponse performRequest(Request request) addCacheHeaders(headers, request.getCacheEntry()); httpResponse = mHttpStack.performRequest(request, headers); - StatusLine statusLine = httpResponse.getStatusLine(); - int statusCode = statusLine.getStatusCode(); - responseHeaders = convertHeaders(httpResponse.getAllHeaders()); + int statusCode = httpResponse.getResponseCode(); + responseHeaders = httpResponse.getHeaders(); if (statusCode == HttpStatus.SC_NOT_MODIFIED) { // 304 return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, request.getCacheEntry() == null ? null : request @@ -74,13 +69,12 @@ public NetworkResponse performRequest(Request request) responseHeaders, true); } - if (httpResponse.getEntity() != null) { + if (httpResponse.getContentStream() != null) { if (request instanceof FileRequest) { responseContents = ((FileRequest) request) .handleResponse(httpResponse); } else { - responseContents = entityToBytes(httpResponse - .getEntity()); + responseContents = entityToBytes(httpResponse); } } else { responseContents = new byte[0]; @@ -94,16 +88,13 @@ public NetworkResponse performRequest(Request request) } catch (SocketTimeoutException e) { throw new KJHttpException(new SocketTimeoutException( "socket timeout")); - } catch (ConnectTimeoutException e) { - throw new KJHttpException(new SocketTimeoutException( - "socket timeout")); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { - statusCode = httpResponse.getStatusLine().getStatusCode(); + statusCode = httpResponse.getResponseCode(); } else { throw new KJHttpException("NoConnection error", e); } @@ -121,7 +112,7 @@ public NetworkResponse performRequest(Request request) networkResponse); } } else { - throw new KJHttpException(networkResponse); + throw new KJHttpException(); } } } @@ -129,7 +120,7 @@ public NetworkResponse performRequest(Request request) /** * 标记Respondeader响应头在Cache中的tag - * + * * @param headers * @param entry */ @@ -142,25 +133,25 @@ private void addCacheHeaders(Map headers, Cache.Entry entry) { } if (entry.serverDate > 0) { Date refTime = new Date(entry.serverDate); - headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); + DateFormat sdf = SimpleDateFormat.getDateTimeInstance(); + headers.put("If-Modified-Since", sdf.format(refTime)); + } } /** * 把HttpEntry转换为byte[] - * - * @param entity - * @return + * * @throws IOException * @throws KJHttpException */ - private byte[] entityToBytes(HttpEntity entity) throws IOException, + private byte[] entityToBytes(KJHttpResponse kjHttpResponse) throws IOException, KJHttpException { PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream( - ByteArrayPool.get(), (int) entity.getContentLength()); + ByteArrayPool.get(), (int) kjHttpResponse.getContentLength()); byte[] buffer = null; try { - InputStream in = entity.getContent(); + InputStream in = kjHttpResponse.getContentStream(); if (in == null) { throw new KJHttpException("server error"); } @@ -172,7 +163,8 @@ private byte[] entityToBytes(HttpEntity entity) throws IOException, return bytes.toByteArray(); } finally { try { - entity.consumeContent(); +// entity.consumeContent(); + kjHttpResponse.getContentStream().close(); } catch (IOException e) { KJLoger.debug("Error occured when calling consumingContent"); } @@ -180,15 +172,4 @@ private byte[] entityToBytes(HttpEntity entity) throws IOException, bytes.close(); } } - - /** - * 转换RespondHeader为Map类型 - */ - private static Map convertHeaders(Header[] headers) { - Map result = new HashMap(); - for (int i = 0; i < headers.length; i++) { - result.put(headers[i].getName(), headers[i].getValue()); - } - return result; - } } diff --git a/KJFrame/src/org/kymjs/kjframe/http/NetworkDispatcher.java b/KJFrame/src/org/kymjs/kjframe/http/NetworkDispatcher.java index b5999169..3c0bb1f0 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/NetworkDispatcher.java +++ b/KJFrame/src/org/kymjs/kjframe/http/NetworkDispatcher.java @@ -16,18 +16,20 @@ package org.kymjs.kjframe.http; -import java.util.concurrent.BlockingQueue; - -import org.kymjs.kjframe.utils.KJLoger; - import android.annotation.TargetApi; import android.net.TrafficStats; import android.os.Build; import android.os.Process; +import org.kymjs.kjframe.utils.KJLoger; + +import java.util.concurrent.BlockingQueue; + /** * 网络请求任务的调度器,负责不停的从RequestQueue中取Request并交给NetWork执行, * 执行完成后分发执行结果到UI线程的回调并缓存结果到缓存器 + * + * @author kymjs (http://www.kymjs.com/) . */ public class NetworkDispatcher extends Thread { private final BlockingQueue> mQueue; // 正在发生请求的队列 @@ -37,7 +39,7 @@ public class NetworkDispatcher extends Thread { private volatile boolean mQuit = false; // 标记是否退出本线程 public NetworkDispatcher(BlockingQueue> queue, Network network, - Cache cache, Delivery delivery) { + Cache cache, Delivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; @@ -111,7 +113,7 @@ public void run() { } private void parseAndDeliverNetworkError(Request request, - KJHttpException error) { + KJHttpException error) { error = request.parseNetworkError(error); mDelivery.postError(request, error); } diff --git a/KJFrame/src/org/kymjs/kjframe/http/NetworkResponse.java b/KJFrame/src/org/kymjs/kjframe/http/NetworkResponse.java index 73a6c8dc..48c8f901 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/NetworkResponse.java +++ b/KJFrame/src/org/kymjs/kjframe/http/NetworkResponse.java @@ -19,15 +19,15 @@ import java.util.Collections; import java.util.Map; -import org.apache.http.HttpStatus; - /** * 从NetWork执行器返回的Http响应,包含了本次响应是成功还是失败,请求头,响应内容,HTTP状态码 + * + * @author kymjs (http://www.kymjs.com/) . */ public class NetworkResponse { public NetworkResponse(int statusCode, byte[] data, - Map headers, boolean notModified) { + Map headers, boolean notModified) { this.statusCode = statusCode; this.data = data; this.headers = headers; @@ -35,7 +35,7 @@ public NetworkResponse(int statusCode, byte[] data, } public NetworkResponse(byte[] data) { - this(HttpStatus.SC_OK, data, Collections. emptyMap(), + this(HttpStatus.SC_OK, data, Collections.emptyMap(), false); } diff --git a/KJFrame/src/org/kymjs/kjframe/http/PoolingByteArrayOutputStream.java b/KJFrame/src/org/kymjs/kjframe/http/PoolingByteArrayOutputStream.java index 087344cd..d979f349 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/PoolingByteArrayOutputStream.java +++ b/KJFrame/src/org/kymjs/kjframe/http/PoolingByteArrayOutputStream.java @@ -21,6 +21,8 @@ /** * 缓存的写入流 + * + * @author kymjs (http://www.kymjs.com/) . */ public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { /** @@ -45,10 +47,9 @@ public PoolingByteArrayOutputStream(ByteArrayPool pool) { * Constructs a new {@code ByteArrayOutputStream} with a default size of * {@code size} bytes. If more than {@code size} bytes are written to this * instance, the underlying byte array will expand. - * - * @param size - * initial size for the underlying byte array. The value will be - * pinned to a default minimum size. + * + * @param size initial size for the underlying byte array. The value will be + * pinned to a default minimum size. */ public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) { mPool = pool; diff --git a/KJFrame/src/org/kymjs/kjframe/http/Request.java b/KJFrame/src/org/kymjs/kjframe/http/Request.java index 817e1a17..7cc162e2 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/Request.java +++ b/KJFrame/src/org/kymjs/kjframe/http/Request.java @@ -15,23 +15,24 @@ */ package org.kymjs.kjframe.http; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Collections; -import java.util.Map; +import android.net.TrafficStats; +import android.net.Uri; +import android.os.SystemClock; +import android.text.TextUtils; import org.kymjs.kjframe.KJHttp; import org.kymjs.kjframe.utils.KJLoger; -import android.net.Uri; -import android.os.SystemClock; -import android.text.TextUtils; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.Map; /** * 一个请求基类 - * - * @param - * Http返回类型 + * + * @param Http返回类型 + * @author kymjs (http://www.kymjs.com/) . */ public abstract class Request implements Comparable> { @@ -110,7 +111,7 @@ public Object getTag() { } /** - * @return A tag for use with TrafficStats.setThreadStatsTag(int) + * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)} */ public int getTrafficStatsTag() { return mDefaultTrafficStatsTag; @@ -222,7 +223,7 @@ public byte[] getBody() { * 对中文参数做URL转码 */ private byte[] encodeParameters(Map params, - String paramsEncoding) { + String paramsEncoding) { StringBuilder encodedParams = new StringBuilder(); try { for (Map.Entry entry : params.entrySet()) { @@ -280,9 +281,8 @@ public boolean hasHadResponseDelivered() { /** * 将网络请求执行器(NetWork)返回的NetWork响应转换为Http响应 - * - * @param response - * 网络请求执行器(NetWork)返回的NetWork响应 + * + * @param response 网络请求执行器(NetWork)返回的NetWork响应 * @return 转换后的HttpRespond, or null in the case of an error */ abstract public Response parseNetworkResponse(NetworkResponse response); @@ -296,18 +296,16 @@ protected KJHttpException parseNetworkError(KJHttpException volleyError) { /** * 将Http请求结果分发到主线程 - * - * @param response - * {@link #parseNetworkResponse(NetworkResponse)} + * + * @param response {@link #parseNetworkResponse(NetworkResponse)} */ abstract protected void deliverResponse(Map headers, - T response); + T response); /** * 响应Http请求异常的回调 - * - * @param error - * 原因 + * + * @param error 原因 */ public void deliverError(KJHttpException error) { if (mCallback != null) { @@ -328,6 +326,17 @@ public void deliverError(KJHttpException error) { } } + /** + * Http请求成功后,在异步调用本方法,本方法执行完成才会继续调用onSuccess() + * + * @param t 请求成功后的数据 + */ + protected void onAsyncSuccess(byte[] t) { + if (mCallback != null) { + mCallback.onSuccessInAsync(t); + } + } + /** * Http请求完成(不论成功失败) */ diff --git a/KJFrame/src/org/kymjs/kjframe/http/Response.java b/KJFrame/src/org/kymjs/kjframe/http/Response.java index 5410331f..e9b7ddec 100755 --- a/KJFrame/src/org/kymjs/kjframe/http/Response.java +++ b/KJFrame/src/org/kymjs/kjframe/http/Response.java @@ -20,6 +20,8 @@ /** * Http响应封装类,包含了本次响应的全部信息 + * + * @author kymjs (http://www.kymjs.com/) . */ public class Response { /** @@ -41,7 +43,7 @@ public boolean isSuccess() { } private Response(T result, Map headers, - Cache.Entry cacheEntry) { + Cache.Entry cacheEntry) { this.result = result; this.cacheEntry = cacheEntry; this.error = null; @@ -57,22 +59,19 @@ private Response(KJHttpException error) { /** * 返回一个成功的HttpRespond - * - * @param result - * Http响应的类型 - * @param cacheEntry - * 缓存对象 + * + * @param result Http响应的类型 + * @param cacheEntry 缓存对象 */ public static Response success(T result, - Map headers, Cache.Entry cacheEntry) { + Map headers, Cache.Entry cacheEntry) { return new Response(result, headers, cacheEntry); } /** * 返回一个失败的HttpRespond - * - * @param error - * 失败原因 + * + * @param error 失败原因 */ public static Response error(KJHttpException error) { return new Response(error); diff --git a/KJFrame/src/org/kymjs/kjframe/ui/AnnotateUtil.java b/KJFrame/src/org/kymjs/kjframe/ui/AnnotateUtil.java index cf9dc9ef..7ec55689 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/AnnotateUtil.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/AnnotateUtil.java @@ -15,14 +15,14 @@ */ package org.kymjs.kjframe.ui; -import java.lang.reflect.Field; - import android.app.Activity; import android.app.Fragment; import android.content.Context; import android.view.View; import android.view.View.OnClickListener; +import java.lang.reflect.Field; + /** * 注解工具类
* @@ -67,7 +67,7 @@ public static void initBindView(Object currentClass, View sourceView) { /** * 必须在setContentView之后调用 * - * @param aty + * @param aty Activity对象 */ public static void initBindView(Activity aty) { initBindView(aty, aty.getWindow().getDecorView()); @@ -91,7 +91,7 @@ public static void initBindView(View view) { /** * 必须在setContentView之后调用 * - * @param frag + * @param frag 要初始化的Fragment */ public static void initBindView(Fragment frag) { initBindView(frag, frag.getActivity().getWindow().getDecorView()); diff --git a/KJFrame/src/org/kymjs/kjframe/ui/BindView.java b/KJFrame/src/org/kymjs/kjframe/ui/BindView.java index 37802343..fe281b92 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/BindView.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/BindView.java @@ -22,16 +22,15 @@ /** * 注解式绑定控件
- * * 创建时间 2014-7-11 - * + * * @author kymjs (https://github.com/kymjs) * @version 1.0 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface BindView { - public int id(); + int id(); - public boolean click() default false; + boolean click() default false; } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/ui/FrameActivity.java b/KJFrame/src/org/kymjs/kjframe/ui/FrameActivity.java deleted file mode 100644 index 5ec01645..00000000 --- a/KJFrame/src/org/kymjs/kjframe/ui/FrameActivity.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.kymjs.kjframe.ui; - -import android.app.FragmentTransaction; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.view.View.OnClickListener; - -/** - * Activity's framework,the developer shouldn't extends it
- * - * 创建时间 2014-3-1
- * 最后修改时间 2014-10-17
- * - * @author kymjs (https://github.com/kymjs) - * @version 1.8 - */ -public abstract class FrameActivity extends FragmentActivity implements - OnClickListener, I_BroadcastReg, I_KJActivity, I_SkipActivity { - - public static final int WHICH_MSG = 0X37210; - - protected KJFragment currentKJFragment; - protected SupportFragment currentSupportFragment; - - /** - * 一个私有回调类,线程中初始化数据完成后的回调 - */ - private interface ThreadDataCallBack { - void onSuccess(); - } - - private static ThreadDataCallBack callback; - - // 当线程中初始化的数据初始化完成后,调用回调方法 - private static Handler threadHandle = new Handler() { - @Override - public void handleMessage(android.os.Message msg) { - if (msg.what == WHICH_MSG) { - callback.onSuccess(); - } - }; - }; - - /** - * 如果调用了initDataFromThread(),则当数据初始化完成后将回调该方法。 - */ - protected void threadDataInited() {} - - /** - * 在线程中初始化数据,注意不能在这里执行UI操作 - */ - @Override - public void initDataFromThread() { - callback = new ThreadDataCallBack() { - @Override - public void onSuccess() { - threadDataInited(); - } - }; - } - - @Override - public void initData() {} - - @Override - public void initWidget() {} - - // 仅仅是为了代码整洁点 - private void initializer() { - new Thread(new Runnable() { - @Override - public void run() { - initDataFromThread(); - threadHandle.sendEmptyMessage(WHICH_MSG); - } - }).start(); - initData(); - initWidget(); - } - - /** listened widget's click method */ - @Override - public void widgetClick(View v) {} - - @Override - public void onClick(View v) { - widgetClick(v); - } - - protected T bindView(int id) { - return (T) findViewById(id); - } - - protected T bindView(int id, boolean click) { - T view = (T) findViewById(id); - if (click) { - view.setOnClickListener(this); - } - return view; - } - - @Override - public void registerBroadcast() {} - - @Override - public void unRegisterBroadcast() {} - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setRootView(); // 必须放在annotate之前调用 - AnnotateUtil.initBindView(this); - initializer(); - registerBroadcast(); - } - - @Override - protected void onDestroy() { - unRegisterBroadcast(); - super.onDestroy(); - } - - /** - * 用Fragment替换视图 - * - * @param resView - * 将要被替换掉的视图 - * @param targetFragment - * 用来替换的Fragment - */ - public void changeFragment(int resView, KJFragment targetFragment) { - if (targetFragment.equals(currentKJFragment)) { - return; - } - FragmentTransaction transaction = getFragmentManager() - .beginTransaction(); - if (!targetFragment.isAdded()) { - transaction.add(resView, targetFragment, targetFragment.getClass() - .getName()); - } - if (targetFragment.isHidden()) { - transaction.show(targetFragment); - } - if (currentKJFragment != null && currentKJFragment.isVisible()) { - transaction.hide(currentKJFragment); - } - currentKJFragment = targetFragment; - transaction.commit(); - } - - /** - * 用Fragment替换视图 - * - * @param resView - * 将要被替换掉的视图 - * @param targetFragment - * 用来替换的Fragment - */ - public void changeFragment(int resView, SupportFragment targetFragment) { - if (targetFragment.equals(currentSupportFragment)) { - return; - } - android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager() - .beginTransaction(); - if (!targetFragment.isAdded()) { - transaction.add(resView, targetFragment, targetFragment.getClass() - .getName()); - } - if (targetFragment.isHidden()) { - transaction.show(targetFragment); - } - if (currentSupportFragment != null - && currentSupportFragment.isVisible()) { - transaction.hide(currentSupportFragment); - } - currentSupportFragment = targetFragment; - transaction.commit(); - } -} diff --git a/KJFrame/src/org/kymjs/kjframe/ui/FrameFragment.java b/KJFrame/src/org/kymjs/kjframe/ui/FrameFragment.java deleted file mode 100644 index b227b7be..00000000 --- a/KJFrame/src/org/kymjs/kjframe/ui/FrameFragment.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.kymjs.kjframe.ui; - -import android.app.Fragment; -import android.os.Bundle; -import android.os.Handler; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -/** - * Fragment's framework,the developer shouldn't extends it
- * - * 创建时间 2014-3-1
- * 最后修改时间 2014-5-30
- * - * @author kymjs (https://github.com/kymjs) - * @version 1.6 - */ -public abstract class FrameFragment extends Fragment implements OnClickListener { - - public static final int WHICH_MSG = 0X37211; - - protected View fragmentRootView; - - /** - * 一个私有回调类,线程中初始化数据完成后的回调 - */ - private interface ThreadDataCallBack { - void onSuccess(); - } - - private static ThreadDataCallBack callback; - - // 当线程中初始化的数据初始化完成后,调用回调方法 - private static Handler threadHandle = new Handler() { - @Override - public void handleMessage(android.os.Message msg) { - if (msg.what == WHICH_MSG) { - callback.onSuccess(); - } - }; - }; - - protected abstract View inflaterView(LayoutInflater inflater, - ViewGroup container, Bundle bundle); - - /** - * initialization widget, you should look like parentView.findviewbyid(id); - * call method - * - * @param parentView - */ - protected void initWidget(View parentView) {} - - /** initialization data */ - protected void initData() {} - - /** - * initialization data. And this method run in background thread, so you - * shouldn't change ui
- * on initializated, will call threadDataInited(); - */ - protected void initDataFromThread() { - callback = new ThreadDataCallBack() { - @Override - public void onSuccess() { - threadDataInited(); - } - }; - } - - /** - * 如果调用了initDataFromThread(),则当数据初始化完成后将回调该方法。 - */ - protected void threadDataInited() {} - - /** widget click method */ - protected void widgetClick(View v) {} - - @Override - public void onClick(View v) { - widgetClick(v); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - fragmentRootView = inflaterView(inflater, container, savedInstanceState); - AnnotateUtil.initBindView(this, fragmentRootView); - initData(); - initWidget(fragmentRootView); - new Thread(new Runnable() { - @Override - public void run() { - initDataFromThread(); - threadHandle.sendEmptyMessage(WHICH_MSG); - } - }).start(); - return fragmentRootView; - } - - protected T bindView(int id) { - return (T) fragmentRootView.findViewById(id); - } - - protected T bindView(int id, boolean click) { - T view = (T) fragmentRootView.findViewById(id); - if (click) { - view.setOnClickListener(this); - } - return view; - } -} diff --git a/KJFrame/src/org/kymjs/kjframe/ui/I_BroadcastReg.java b/KJFrame/src/org/kymjs/kjframe/ui/I_BroadcastReg.java index 1f821b74..42851e67 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/I_BroadcastReg.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/I_BroadcastReg.java @@ -19,8 +19,8 @@ * 规范Activity中广播接受者注册的接口协议
* * 创建时间 2014-7-11 - * - * @author kymjs (https://github.com/kymjs) + * + * @author kymjs (http://www.kymjs.com/) . * @version 1.0 */ public interface I_BroadcastReg { diff --git a/KJFrame/src/org/kymjs/kjframe/ui/I_KJActivity.java b/KJFrame/src/org/kymjs/kjframe/ui/I_KJActivity.java index 9a3c71ec..7dcb00d7 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/I_KJActivity.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/I_KJActivity.java @@ -19,26 +19,41 @@ /** * KJFrameActivity接口协议,实现此接口可使用KJActivityManager堆栈
- * * 创建时间 2014-3-1
* 最后修改时间 2014-5-30 - * - * @author kymjs (https://github.com/kymjs) - * @version 1.0 + * + * @author kymjs (http://www.kymjs.com) + * @version 2.25 */ public interface I_KJActivity { - /** 设置root界面 */ + + int DESTROY = 0; + int STOP = 2; + int PAUSE = 1; + int RESUME = 3; + + /** + * 设置root界面 + */ void setRootView(); - /** 初始化数据 */ + /** + * 初始化数据 + */ void initData(); - /** 在线程中初始化数据 */ + /** + * 在线程中初始化数据 + */ void initDataFromThread(); - /** 初始化控件 */ + /** + * 初始化控件 + */ void initWidget(); - /** 点击事件回调方法 */ + /** + * 点击事件回调方法 + */ void widgetClick(View v); } diff --git a/KJFrame/src/org/kymjs/kjframe/ui/KJFragment.java b/KJFrame/src/org/kymjs/kjframe/ui/KJFragment.java index 60bd77c2..eb8237d8 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/KJFragment.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/KJFragment.java @@ -15,52 +15,132 @@ */ package org.kymjs.kjframe.ui; -import org.kymjs.kjframe.utils.KJLoger; +import java.lang.ref.SoftReference; +import android.app.Fragment; import android.os.Bundle; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; /** - * Application's base Fragment,you should inherit it for your Fragment
- * - * 创建时间 2014-5-28 + * Fragment's framework
+ * 创建时间 2014-3-1
+ * 最后修改时间 2014-5-30
* * @author kymjs (https://github.com/kymjs) - * @version 1.0 + * @version 1.6 */ -public abstract class KJFragment extends FrameFragment { +public abstract class KJFragment extends Fragment implements + View.OnClickListener { - /*************************************************************************** - * - * print Fragment callback methods + public static final int WHICH_MSG = 0X37211; + + protected View fragmentRootView; + private ThreadDataCallBack callback; + private final KJFragmentHandle threadHandle = new KJFragmentHandle(this); + + /** + * 一个私有回调类,线程中初始化数据完成后的回调 + */ + private interface ThreadDataCallBack { + void onSuccess(); + } + + private static class KJFragmentHandle extends Handler { + private final SoftReference mOuterInstance; + + KJFragmentHandle(KJFragment outer) { + mOuterInstance = new SoftReference(outer); + } + + // 当线程中初始化的数据初始化完成后,调用回调方法 + @Override + public void handleMessage(android.os.Message msg) { + if (msg.what == WHICH_MSG && mOuterInstance.get() != null) { + mOuterInstance.get().callback.onSuccess(); + } + } + } + + protected abstract View inflaterView(LayoutInflater inflater, + ViewGroup container, Bundle bundle); + + /** + * initialization widget, you should look like parentView.findviewbyid(id); + * call method * - ***************************************************************************/ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - KJLoger.state(this.getClass().getName(), "---------onCreateView "); + * @param parentView + * 根View + */ + protected void initWidget(View parentView) {} + + /** + * initialization data + */ + protected void initData() {} + + /** + * 当通过changeFragment()显示时会被调用(类似于onResume) + */ + public void onChange() {} + + /** + * initialization data. And this method run in background thread, so you + * shouldn't change ui
+ * on initializated, will call threadDataInited(); + */ + protected void initDataFromThread() { + callback = new ThreadDataCallBack() { + @Override + public void onSuccess() { + threadDataInited(); + } + }; } + /** + * 如果调用了initDataFromThread(),则当数据初始化完成后将回调该方法。 + */ + protected void threadDataInited() {} + + /** + * widget click method + */ + protected void widgetClick(View v) {} + @Override - public void onResume() { - KJLoger.state(this.getClass().getName(), "---------onResume "); - super.onResume(); + public void onClick(View v) { + widgetClick(v); } @Override - public void onPause() { - KJLoger.state(this.getClass().getName(), "---------onPause "); - super.onPause(); + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + fragmentRootView = inflaterView(inflater, container, savedInstanceState); + AnnotateUtil.initBindView(this, fragmentRootView); + initData(); + initWidget(fragmentRootView); + new Thread(new Runnable() { + @Override + public void run() { + initDataFromThread(); + threadHandle.sendEmptyMessage(WHICH_MSG); + } + }).start(); + return fragmentRootView; } - @Override - public void onStop() { - KJLoger.state(this.getClass().getName(), "---------onStop "); - super.onStop(); + protected T bindView(int id) { + return (T) fragmentRootView.findViewById(id); } - @Override - public void onDestroyView() { - KJLoger.state(this.getClass().getName(), "---------onDestroy "); - super.onDestroyView(); + protected T bindView(int id, boolean click) { + T view = (T) fragmentRootView.findViewById(id); + if (click) { + view.setOnClickListener(this); + } + return view; } } diff --git a/KJFrame/src/org/kymjs/kjframe/ui/SupportFragment.java b/KJFrame/src/org/kymjs/kjframe/ui/SupportFragment.java index 10c9b55b..4737d567 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/SupportFragment.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/SupportFragment.java @@ -1 +1 @@ -package org.kymjs.kjframe.ui; import org.kymjs.kjframe.utils.KJLoger; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; public abstract class SupportFragment extends Fragment implements OnClickListener { public static final int WHICH_MSG = 0X37211; protected View fragmentRootView; /** * 一个私有回调类,线程中初始化数据完成后的回调 */ private interface ThreadDataCallBack { void onSuccess(); } private static ThreadDataCallBack callback; // 当线程中初始化的数据初始化完成后,调用回调方法 private static Handler threadHandle = new Handler() { @Override public void handleMessage(android.os.Message msg) { if (msg.what == WHICH_MSG) { callback.onSuccess(); } }; }; protected abstract View inflaterView(LayoutInflater inflater, ViewGroup container, Bundle bundle); /** * initialization widget, you should look like parentView.findviewbyid(id); * call method * * @param parentView */ protected void initWidget(View parentView) {} /** initialization data */ protected void initData() {} /** * initialization data. And this method run in background thread, so you * shouldn't change ui
* on initializated, will call threadDataInited(); */ protected void initDataFromThread() { callback = new ThreadDataCallBack() { @Override public void onSuccess() { threadDataInited(); } }; } /** * 如果调用了initDataFromThread(),则当数据初始化完成后将回调该方法。 */ protected void threadDataInited() {} /** widget click method */ protected void widgetClick(View v) {} @Override public void onClick(View v) { widgetClick(v); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { fragmentRootView = inflaterView(inflater, container, savedInstanceState); AnnotateUtil.initBindView(this, fragmentRootView); initData(); initWidget(fragmentRootView); new Thread(new Runnable() { @Override public void run() { initDataFromThread(); threadHandle.sendEmptyMessage(WHICH_MSG); } }).start(); return fragmentRootView; } protected T bindView(int id) { return (T) fragmentRootView.findViewById(id); } protected T bindView(int id, boolean click) { T view = (T) fragmentRootView.findViewById(id); if (click) { view.setOnClickListener(this); } return view; } /*************************************************************************** * * print Fragment callback methods * ***************************************************************************/ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); KJLoger.state(this.getClass().getName(), "---------onCreateView "); } @Override public void onResume() { KJLoger.state(this.getClass().getName(), "---------onResume "); super.onResume(); } @Override public void onPause() { KJLoger.state(this.getClass().getName(), "---------onPause "); super.onPause(); } @Override public void onStop() { KJLoger.state(this.getClass().getName(), "---------onStop "); super.onStop(); } @Override public void onDestroyView() { KJLoger.state(this.getClass().getName(), "---------onDestroy "); super.onDestroyView(); } } \ No newline at end of file +/* * Copyright (c) 2015, 张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.ui; import java.lang.ref.SoftReference; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; /** * 兼容v4包的Fragment * * @author kymjs (http://www.kymjs.com/) . */ public abstract class SupportFragment extends Fragment implements OnClickListener { public static final int WHICH_MSG = 0X37211; protected View fragmentRootView; private ThreadDataCallBack callback; private final KJFragmentHandle threadHandle = new KJFragmentHandle(this); /** * 一个私有回调类,线程中初始化数据完成后的回调 */ private interface ThreadDataCallBack { void onSuccess(); } private static class KJFragmentHandle extends Handler { private final SoftReference mOuterInstance; KJFragmentHandle(SupportFragment outer) { mOuterInstance = new SoftReference(outer); } // 当线程中初始化的数据初始化完成后,调用回调方法 @Override public void handleMessage(android.os.Message msg) { if (msg.what == WHICH_MSG && mOuterInstance.get() != null) { mOuterInstance.get().callback.onSuccess(); } } } protected abstract View inflaterView(LayoutInflater inflater, ViewGroup container, Bundle bundle); /** * initialization widget, you should look like parentView.findviewbyid(id); * call method * * @param parentView */ protected void initWidget(View parentView) {} /** * initialization data */ protected void initData() {} /** * initialization data. And this method run in background thread, so you * shouldn't change ui
* on initializated, will call threadDataInited(); */ protected void initDataFromThread() { callback = new ThreadDataCallBack() { @Override public void onSuccess() { threadDataInited(); } }; } /** * 如果调用了initDataFromThread(),则当数据初始化完成后将回调该方法。 */ protected void threadDataInited() {} /** * 当通过changeFragment()显示时会被调用(类似于onResume) */ public void onChange() {} /** * widget click method */ protected void widgetClick(View v) {} @Override public void onClick(View v) { widgetClick(v); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { fragmentRootView = inflaterView(inflater, container, savedInstanceState); AnnotateUtil.initBindView(this, fragmentRootView); initData(); initWidget(fragmentRootView); new Thread(new Runnable() { @Override public void run() { initDataFromThread(); threadHandle.sendEmptyMessage(WHICH_MSG); } }).start(); return fragmentRootView; } protected T bindView(int id) { return (T) fragmentRootView.findViewById(id); } protected T bindView(int id, boolean click) { T view = (T) fragmentRootView.findViewById(id); if (click) { view.setOnClickListener(this); } return view; } } \ No newline at end of file diff --git a/KJFrame/src/org/kymjs/kjframe/ui/ViewInject.java b/KJFrame/src/org/kymjs/kjframe/ui/ViewInject.java index 0676ffed..a4a33f78 100644 --- a/KJFrame/src/org/kymjs/kjframe/ui/ViewInject.java +++ b/KJFrame/src/org/kymjs/kjframe/ui/ViewInject.java @@ -15,10 +15,6 @@ */ package org.kymjs.kjframe.ui; -import org.kymjs.kjframe.utils.DensityUtils; -import org.kymjs.kjframe.utils.StringUtils; -import org.kymjs.kjframe.utils.SystemTool; - import android.app.Activity; import android.app.AlertDialog; import android.app.DatePickerDialog; @@ -32,14 +28,19 @@ import android.widget.TextView; import android.widget.Toast; +import org.kymjs.kjframe.utils.DensityUtils; +import org.kymjs.kjframe.utils.StringUtils; +import org.kymjs.kjframe.utils.SystemTool; + /** * 侵入式View的调用工具类 - * - * @author kymjs (https://github.com/kymjs) + * + * @author kymjs (http://www.kymjs.com/). */ public class ViewInject { - private ViewInject() {} + private ViewInject() { + } private static class ClassHolder { private static final ViewInject instance = new ViewInject(); @@ -47,7 +48,7 @@ private static class ClassHolder { /** * 类对象创建方法 - * + * * @return 本类的对象 */ public static ViewInject create() { @@ -56,7 +57,7 @@ public static ViewInject create() { /** * 显示一个toast - * + * * @param msg */ public static void toast(String msg) { @@ -68,7 +69,7 @@ public static void toast(String msg) { /** * 长时间显示一个toast - * + * * @param msg */ public static void longToast(String msg) { @@ -80,7 +81,7 @@ public static void longToast(String msg) { /** * 长时间显示一个toast - * + * * @param msg */ public static void longToast(Context context, String msg) { @@ -89,7 +90,7 @@ public static void longToast(Context context, String msg) { /** * 显示一个toast - * + * * @param msg */ public static void toast(Context context, String msg) { @@ -100,7 +101,7 @@ public static void toast(Context context, String msg) { * 返回一个退出确认对话框 */ public void getExitDialog(final Context context, String title, - OnClickListener l) { + OnClickListener l) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setMessage(title); builder.setCancelable(false); @@ -150,7 +151,7 @@ public void getDateDialog(String title, final TextView textView) { new OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, - int monthOfYear, int dayOfMonth) { + int monthOfYear, int dayOfMonth) { textView.setText(year + "-" + (monthOfYear + 1) + "-" + dayOfMonth); } @@ -161,16 +162,13 @@ public void onDateSet(DatePicker view, int year, /** * 返回一个等待信息弹窗 - * - * @param aty - * 要显示弹出窗的Activity - * @param msg - * 弹出窗上要显示的文字 - * @param cancel - * dialog是否可以被取消 + * + * @param aty 要显示弹出窗的Activity + * @param msg 弹出窗上要显示的文字 + * @param cancel dialog是否可以被取消 */ public static ProgressDialog getprogress(Activity aty, String msg, - boolean cancel) { + boolean cancel) { // 实例化一个ProgressBarDialog ProgressDialog progressDialog = new ProgressDialog(aty); progressDialog.setMessage(msg); diff --git a/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java b/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java index 475eaf1a..fddfbfe2 100644 --- a/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java +++ b/KJFrame/src/org/kymjs/kjframe/utils/ImageUtils.java @@ -1 +1,127 @@ -/* * Copyright (c) 2015,KJFrameForAndroid Open Source Project,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kymjs.kjframe.utils; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Random; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; /** * 图片工具类 Created by kymjs on 15/9/8. */ public class ImageUtils { /** * 压缩图片 * * @param filePath * 源图片地址 * @param width * 想要的宽度 * @param height * 想要的高度 * @param isAdjust * 是否自动调整尺寸, true图片就不会拉伸,false严格按照你的尺寸压缩 * @return Bitmap */ public static File getSmallImageFile(Context cxt, String filePath, int width, int height, boolean isAdjust) { Bitmap bitmap = reduce(BitmapFactory.decodeFile(filePath), width, height, isAdjust); File file = new File(getRandomFileName(cxt.getCacheDir().getPath())); BufferedOutputStream outputStream = null; try { outputStream = new BufferedOutputStream(new FileOutputStream(file)); bitmap.compress(Bitmap.CompressFormat.JPEG, 60, outputStream); outputStream.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return file; } /*** * 获取一个随机图片文件名,含路径 * * @param filePath * @return */ public static String getRandomFileName(String filePath) { SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault()); Date date = new Date(); String key = format.format(date); Random r = new Random(); key = key + r.nextInt(); key = key.substring(0, 15); return filePath + "/" + key + ".jpeg"; } /** * 压缩图片 * * @param bitmap * 源图片 * @param width * 想要的宽度 * @param height * 想要的高度 * @param isAdjust * 是否自动调整尺寸, true图片就不会拉伸,false严格按照你的尺寸压缩 * @return Bitmap */ public static Bitmap reduce(Bitmap bitmap, int width, int height, boolean isAdjust) { // 如果想要的宽度和高度都比源图片小,就不压缩了,直接返回原图 if (bitmap.getWidth() < width && bitmap.getHeight() < height) { return bitmap; } if (width == 0 && height == 0) { width = bitmap.getWidth(); height = bitmap.getHeight(); } // 根据想要的尺寸精确计算压缩比例, 方法详解:public BigDecimal divide(BigDecimal divisor, // int scale, int // roundingMode); // scale表示要保留的小数位, roundingMode表示如何处理多余的小数位,BigDecimal.ROUND_DOWN表示自动舍弃 float sx = new BigDecimal(width).divide( new BigDecimal(bitmap.getWidth()), 4, BigDecimal.ROUND_DOWN) .floatValue(); float sy = new BigDecimal(height).divide( new BigDecimal(bitmap.getHeight()), 4, BigDecimal.ROUND_DOWN) .floatValue(); if (isAdjust) {// 如果想自动调整比例,不至于图片会拉伸 sx = (sx < sy ? sx : sy); sy = sx;// 哪个比例小一点,就用哪个比例 } Matrix matrix = new Matrix(); matrix.postScale(sx, sy);// 调用api中的方法进行压缩,就大功告成了 return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } } \ No newline at end of file +/* + * Copyright (c) 2015,KJFrameForAndroid Open Source Project,张涛. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kymjs.kjframe.utils; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Random; + +/** + * 图片工具类 + * Created by kymjs on 15/9/8. + */ +public class ImageUtils { + + /** + * 压缩图片 + * + * @param filePath 源图片地址 + * @param width 想要的宽度 + * @param height 想要的高度 + * @param isAdjust 是否自动调整尺寸, true图片就不会拉伸,false严格按照你的尺寸压缩 + * @return Bitmap + */ + public static File getSmallImageFile(Context cxt, String filePath, int width, int height, + boolean isAdjust) { + + Bitmap bitmap = reduce(BitmapFactory.decodeFile(filePath), width, height, isAdjust); + + File file = new File(getRandomFileName(cxt.getCacheDir().getPath())); + + BufferedOutputStream outputStream = null; + try { + outputStream = new BufferedOutputStream(new FileOutputStream(file)); + bitmap.compress(Bitmap.CompressFormat.JPEG, 60, outputStream); + outputStream.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (outputStream != null) { + outputStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return file; + } + + /*** + * 获取一个随机图片文件名,含路径 + * + * @param filePath + * @return + */ + public static String getRandomFileName(String filePath) { + SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", + Locale.getDefault()); + Date date = new Date(); + String key = format.format(date); + + Random r = new Random(); + key = key + r.nextInt(); + key = key.substring(0, 15); + return filePath + "/" + key + ".jpeg"; + } + + /** + * 压缩图片 + * + * @param bitmap 源图片 + * @param width 想要的宽度 + * @param height 想要的高度 + * @param isAdjust 是否自动调整尺寸, true图片就不会拉伸,false严格按照你的尺寸压缩 + * @return Bitmap + */ + public static Bitmap reduce(Bitmap bitmap, int width, int height, boolean isAdjust) { + // 如果想要的宽度和高度都比源图片小,就不压缩了,直接返回原图 + if (bitmap.getWidth() < width && bitmap.getHeight() < height) { + return bitmap; + } + if (width == 0 && height == 0) { + width = bitmap.getWidth(); + height = bitmap.getHeight(); + } + + // 根据想要的尺寸精确计算压缩比例, 方法详解:public BigDecimal divide(BigDecimal divisor, int scale, int + // roundingMode); + // scale表示要保留的小数位, roundingMode表示如何处理多余的小数位,BigDecimal.ROUND_DOWN表示自动舍弃 + float sx = new BigDecimal(width).divide(new BigDecimal(bitmap.getWidth()), 4, BigDecimal + .ROUND_DOWN).floatValue(); + float sy = new BigDecimal(height).divide(new BigDecimal(bitmap.getHeight()), 4, + BigDecimal.ROUND_DOWN).floatValue(); + if (isAdjust) {// 如果想自动调整比例,不至于图片会拉伸 + sx = (sx < sy ? sx : sy); + sy = sx;// 哪个比例小一点,就用哪个比例 + } + Matrix matrix = new Matrix(); + matrix.postScale(sx, sy);// 调用api中的方法进行压缩,就大功告成了 + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, + true); + } +} diff --git a/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java b/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java index 0ae9ace0..77a42af3 100644 --- a/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java +++ b/KJFrame/src/org/kymjs/kjframe/utils/KJConfig.java @@ -16,20 +16,25 @@ package org.kymjs.kjframe.utils; /** - * * @author kymjs (https://github.com/kymjs) */ public final class KJConfig { - public static final double VERSION = 2.2451; + public static final double VERSION = 2.2512; - /** 错误处理广播 */ + /** + * 错误处理广播 + */ public static final String RECEIVER_ERROR = KJConfig.class.getName() + "org.kymjs.android.frame.error"; - /** 无网络警告广播 */ + /** + * 无网络警告广播 + */ public static final String RECEIVER_NOT_NET_WARN = KJConfig.class.getName() + "org.kymjs.android.frame.notnet"; - /** preference键值对 */ + /** + * preference键值对 + */ public static final String SETTING_FILE = "kjframe_preference"; public static final String ONLY_WIFI = "only_wifi"; } diff --git a/KJFrame/src/org/kymjs/kjframe/widget/AdapterHolder.java b/KJFrame/src/org/kymjs/kjframe/widget/AdapterHolder.java index dd9ca1ba..3a57207f 100644 --- a/KJFrame/src/org/kymjs/kjframe/widget/AdapterHolder.java +++ b/KJFrame/src/org/kymjs/kjframe/widget/AdapterHolder.java @@ -20,8 +20,6 @@ * * @author kymjs(http://www.kymjs.com/) */ -import org.kymjs.kjframe.KJBitmap; - import android.graphics.Bitmap; import android.util.SparseArray; import android.view.LayoutInflater; @@ -30,6 +28,8 @@ import android.widget.ImageView; import android.widget.TextView; +import org.kymjs.kjframe.KJBitmap; + public class AdapterHolder { private final SparseArray mViews; private final int mPosition; @@ -98,7 +98,7 @@ public T getView(int viewId) { * @param text * @return */ - public AdapterHolder setText(int viewId, String text) { + public AdapterHolder setText(int viewId, CharSequence text) { TextView view = getView(viewId); view.setText(text); return this; diff --git a/KJFrame/src/org/kymjs/kjframe/widget/KJAdapter.java b/KJFrame/src/org/kymjs/kjframe/widget/KJAdapter.java index cf488b02..25d02b04 100644 --- a/KJFrame/src/org/kymjs/kjframe/widget/KJAdapter.java +++ b/KJFrame/src/org/kymjs/kjframe/widget/KJAdapter.java @@ -15,11 +15,6 @@ */ package org.kymjs.kjframe.widget; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; - import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -27,12 +22,16 @@ import android.widget.AbsListView; import android.widget.BaseAdapter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + /** * 对ViewHolder的封装,以及更方便的控制ListView滑动过程中不加载图片 - * - * @author kymjs (https://www.kymjs.com/) - * + * * @param + * @author kymjs (https://www.kymjs.com/) */ public abstract class KJAdapter extends BaseAdapter implements AbsListView.OnScrollListener { @@ -101,15 +100,16 @@ public View getView(int position, View convertView, ViewGroup parent) { } private AdapterHolder getViewHolder(int position, View convertView, - ViewGroup parent) { + ViewGroup parent) { return AdapterHolder.get(convertView, parent, mItemLayoutId, position); } - public abstract void convert(AdapterHolder helper, T item, - boolean isScrolling); + public void convert(AdapterHolder helper, T item, + boolean isScrolling) { + } public void convert(AdapterHolder helper, T item, boolean isScrolling, - int position) { + int position) { convert(helper, getItem(position), isScrolling); } @@ -129,7 +129,7 @@ public void onScrollStateChanged(AbsListView view, int scrollState) { @Override public void onScroll(AbsListView view, int firstVisibleItem, - int visibleItemCount, int totalItemCount) { + int visibleItemCount, int totalItemCount) { if (listener != null) { listener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); diff --git a/KJFrame/src/org/kymjs/kjframe/widget/KJScrollView.java b/KJFrame/src/org/kymjs/kjframe/widget/KJScrollView.java index 9ea638e5..48c47844 100644 --- a/KJFrame/src/org/kymjs/kjframe/widget/KJScrollView.java +++ b/KJFrame/src/org/kymjs/kjframe/widget/KJScrollView.java @@ -60,6 +60,7 @@ public KJScrollView(Context context, AttributeSet attrs) { @Override protected void onFinishInflate() { + super.onFinishInflate(); if (getChildCount() > 0) { contentView = getChildAt(0); } diff --git a/KJFrame/src/org/kymjs/kjframe/widget/ScaleImageView.java b/KJFrame/src/org/kymjs/kjframe/widget/ScaleImageView.java deleted file mode 100644 index 757f2986..00000000 --- a/KJFrame/src/org/kymjs/kjframe/widget/ScaleImageView.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.kymjs.kjframe.widget; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.ImageView; - -/** - * 来自网络:图片缩放控件(支持缩放、拖动、双击、旋转)
- * - * @author kymjs (https://github.com/kymjs) - */ -public class ScaleImageView extends ImageView { - - public enum ImageState { - NONE, // 初始状态 - DRAG, // 拖动 - ZOOM, // 缩放 - ROTATE, // 旋转 - ZOOM_OR_ROTATE // 缩放或旋转 - } - - private ImageState mode = ImageState.NONE; - - static final float MAX_SCALE = 2.5f; - - private float imageW; // 图片宽度 - private float imageH; // 图片高度 - private float rotatedImageW; - private float rotatedImageH; - private float viewW; - private float viewH; - - private final Matrix matrix = new Matrix(); - private final Matrix savedMatrix = new Matrix(); - - private final PointF pA = new PointF(); - private final PointF pB = new PointF(); - private final PointF mid = new PointF(); - private final PointF lastClickPos = new PointF(); - - private long lastClickTime = 0; // 上次点击时间(用于计算双击) - private double rotation = 0.0; - private float dist = 1f; // 两点间距 - - private boolean canRotate = true; // 是否开启旋转图片功能 - private boolean canDoubleClick = true; // 是否开启双击缩放功能 - - public void setCanDoubleClick(boolean canDoubleClick) { - this.canDoubleClick = canDoubleClick; - } - - public boolean canDoubleClick() { - return canDoubleClick; - } - - public void setCanRotate(boolean canRotate) { - this.canRotate = canRotate; - } - - public boolean canRotate() { - return canRotate; - } - - public ScaleImageView(Context context) { - super(context); - initView(); - } - - public ScaleImageView(Context context, AttributeSet attrs) { - super(context, attrs); - initView(); - } - - public ScaleImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initView(); - } - - /** - * 初始化控件 - */ - private void initView() { - setScaleType(ImageView.ScaleType.MATRIX); - } - - @Override - public void setImageBitmap(Bitmap bm) { - super.setImageBitmap(bm); - setImageWidthHeight(); - } - - @Override - public void setImageDrawable(Drawable drawable) { - super.setImageDrawable(drawable); - setImageWidthHeight(); - } - - @Override - public void setImageResource(int resId) { - super.setImageResource(resId); - setImageWidthHeight(); - } - - private void setImageWidthHeight() { - Drawable d = getDrawable(); - if (d == null) { - return; - } - imageW = rotatedImageW = d.getIntrinsicWidth(); - imageH = rotatedImageH = d.getIntrinsicHeight(); - initImage(); - } - - /** - * 图片大小改变时回调 - */ - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - viewW = w; - viewH = h; - if (oldw == 0) { // 如果图片还没有计算大小 - initImage(); - } else { - fixScale(); // 固定大小 - fixTranslation(); - setImageMatrix(matrix); - } - } - - /** - * 首次加载,初始化图片控件 - */ - private void initImage() { - if (viewW <= 0 || viewH <= 0 || imageW <= 0 || imageH <= 0) { - return; - } - mode = ImageState.NONE; - matrix.setScale(0, 0); - fixScale(); - fixTranslation(); - setImageMatrix(matrix); - } - - /** - * 设置matrix,记录当前图片大小 - */ - private void fixScale() { - float p[] = new float[9]; - matrix.getValues(p); - float curScale = Math.abs(p[0]) + Math.abs(p[1]); - float minScale = Math.min(viewW / rotatedImageW, viewH / rotatedImageH); - if (curScale < minScale) { - if (curScale > 0) { - double scale = minScale / curScale; - p[0] = (float) (p[0] * scale); - p[1] = (float) (p[1] * scale); - p[3] = (float) (p[3] * scale); - p[4] = (float) (p[4] * scale); - matrix.setValues(p); - } else { - matrix.setScale(minScale, minScale); - } - } - } - - /** - * 最大缩放值 - */ - private float maxPostScale() { - float p[] = new float[9]; - matrix.getValues(p); - float curScale = Math.abs(p[0]) + Math.abs(p[1]); - - float minScale = Math.min(viewW / rotatedImageW, viewH / rotatedImageH); - float maxScale = Math.max(minScale, MAX_SCALE); - return maxScale / curScale; - } - - /** - * 移动matrix - */ - private void fixTranslation() { - RectF rect = new RectF(0, 0, imageW, imageH); - matrix.mapRect(rect); - - float height = rect.height(); - float width = rect.width(); - - float deltaX = 0, deltaY = 0; - - if (width < viewW) { - deltaX = (viewW - width) / 2 - rect.left; - } else if (rect.left > 0) { - deltaX = -rect.left; - } else if (rect.right < viewW) { - deltaX = viewW - rect.right; - } - - if (height < viewH) { - deltaY = (viewH - height) / 2 - rect.top; - } else if (rect.top > 0) { - deltaY = -rect.top; - } else if (rect.bottom < viewH) { - deltaY = viewH - rect.bottom; - } - matrix.postTranslate(deltaX, deltaY); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction() & MotionEvent.ACTION_MASK) { - // 主点按下 :记录当前matrix,以及初始化按下坐标、图片状态 - case MotionEvent.ACTION_DOWN: - savedMatrix.set(matrix); - pA.set(event.getX(), event.getY()); - pB.set(event.getX(), event.getY()); - mode = ImageState.DRAG; - break; - // 副点按下 :如果间距大于10,记录此时matrix,以及两点的坐标、图片状态 - case MotionEvent.ACTION_POINTER_DOWN: - if (event.getActionIndex() > 1) // 超过两个点返回 - break; - dist = spacing(event.getX(0), event.getY(0), event.getX(1), - event.getY(1)); - if (dist > 10f) { // 如果连续两点距离大于10,则判定为多点模式 - savedMatrix.set(matrix); - pA.set(event.getX(0), event.getY(0)); - pB.set(event.getX(1), event.getY(1)); - mid.set((event.getX(0) + event.getX(1)) / 2, - (event.getY(0) + event.getY(1)) / 2); - mode = ImageState.ZOOM_OR_ROTATE; - } - break; - - // 主点或负点抬起 - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - if (mode == ImageState.DRAG) { - if (spacing(pA.x, pA.y, pB.x, pB.y) < 50) { - long now = System.currentTimeMillis(); - if (now - lastClickTime < 500 - && spacing(pA.x, pA.y, lastClickPos.x, - lastClickPos.y) < 50) { - doubleClick(pA.x, pA.y); - now = 0; - } - lastClickPos.set(pA); - lastClickTime = now; - } - } else if (mode == ImageState.ROTATE) { - int level = (int) Math.floor((rotation + Math.PI / 4) - / (Math.PI / 2)); - if (level == 4) - level = 0; - matrix.set(savedMatrix); - matrix.postRotate(90 * level, mid.x, mid.y); - if (level == 1 || level == 3) { - float tmp = rotatedImageW; - rotatedImageW = rotatedImageH; - rotatedImageH = tmp; - fixScale(); - } - fixTranslation(); - setImageMatrix(matrix); - } - mode = ImageState.NONE; - break; - - // 拖动时 - case MotionEvent.ACTION_MOVE: - if (mode == ImageState.ZOOM_OR_ROTATE) { - PointF pC = new PointF(event.getX(1) - event.getX(0) + pA.x, - event.getY(1) - event.getY(0) + pA.y); - double a = spacing(pB.x, pB.y, pC.x, pC.y); - double b = spacing(pA.x, pA.y, pC.x, pC.y); - double c = spacing(pA.x, pA.y, pB.x, pB.y); - if (a >= 10) { - double cosB = (a * a + c * c - b * b) / (2 * a * c); - double angleB = Math.acos(cosB); - double PID4 = Math.PI / 4; - if (angleB > PID4 && angleB < 3 * PID4) { - mode = ImageState.ROTATE; - rotation = 0; - } else { - mode = ImageState.ZOOM; - } - } - } - - if (mode == ImageState.DRAG) { - matrix.set(savedMatrix); - pB.set(event.getX(), event.getY()); - matrix.postTranslate(event.getX() - pA.x, event.getY() - pA.y); - fixTranslation(); - setImageMatrix(matrix); - } else if (mode == ImageState.ZOOM) { - float newDist = spacing(event.getX(0), event.getY(0), - event.getX(1), event.getY(1)); - if (newDist > 10f) { - matrix.set(savedMatrix); - float tScale = Math.min(newDist / dist, maxPostScale()); - matrix.postScale(tScale, tScale, mid.x, mid.y); - fixScale(); - fixTranslation(); - setImageMatrix(matrix); - } - } else if (mode == ImageState.ROTATE) { - if (canRotate) { - PointF pC = new PointF( - event.getX(1) - event.getX(0) + pA.x, event.getY(1) - - event.getY(0) + pA.y); - double a = spacing(pB.x, pB.y, pC.x, pC.y); - double b = spacing(pA.x, pA.y, pC.x, pC.y); - double c = spacing(pA.x, pA.y, pB.x, pB.y); - if (b > 10) { - double cosA = (b * b + c * c - a * a) / (2 * b * c); - double angleA = Math.acos(cosA); - double ta = pB.y - pA.y; - double tb = pA.x - pB.x; - double tc = pB.x * pA.y - pA.x * pB.y; - double td = ta * pC.x + tb * pC.y + tc; - if (td > 0) { - angleA = 2 * Math.PI - angleA; - } - rotation = angleA; - matrix.set(savedMatrix); - matrix.postRotate((float) (rotation * 180 / Math.PI), - mid.x, mid.y); - setImageMatrix(matrix); - } - } - } - break; - } - return true; - } - - /** - * 两点的距离 - */ - private float spacing(float x1, float y1, float x2, float y2) { - float x = x1 - x2; - float y = y1 - y2; - return (float) Math.sqrt(x * x + y * y); - } - - /** - * 双击时调用 - */ - private void doubleClick(float x, float y) { - if (canDoubleClick) { - float p[] = new float[9]; - matrix.getValues(p); - float curScale = Math.abs(p[0]) + Math.abs(p[1]); - - float minScale = Math.min(viewW / rotatedImageW, viewH - / rotatedImageH); - if (curScale <= minScale + 0.01) { // 放大 - float toScale = Math.max(minScale, MAX_SCALE) / curScale; - matrix.postScale(toScale, toScale, x, y); - } else { // 缩小 - float toScale = minScale / curScale; - matrix.postScale(toScale, toScale, x, y); - fixTranslation(); - } - setImageMatrix(matrix); - } - } -} \ No newline at end of file diff --git a/KJLibraryExample/res/layout/aty_kjlistview.xml b/KJLibraryExample/res/layout/aty_kjlistview.xml index 91d11354..d79ba31c 100644 --- a/KJLibraryExample/res/layout/aty_kjlistview.xml +++ b/KJLibraryExample/res/layout/aty_kjlistview.xml @@ -1,12 +1,12 @@ - - - - - - + + + + + + diff --git a/KJLibraryExample/res/layout/aty_main.xml b/KJLibraryExample/res/layout/aty_main.xml index 06680342..36cee91a 100644 --- a/KJLibraryExample/res/layout/aty_main.xml +++ b/KJLibraryExample/res/layout/aty_main.xml @@ -1,32 +1,32 @@ - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/KJLibraryExample/res/layout/aty_new_listview.xml b/KJLibraryExample/res/layout/aty_new_listview.xml index 6087f90c..10b10e73 100644 --- a/KJLibraryExample/res/layout/aty_new_listview.xml +++ b/KJLibraryExample/res/layout/aty_new_listview.xml @@ -1,28 +1,28 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/KJLibraryExample/res/layout/example_db.xml b/KJLibraryExample/res/layout/example_db.xml index ece8bacf..680d58b5 100644 --- a/KJLibraryExample/res/layout/example_db.xml +++ b/KJLibraryExample/res/layout/example_db.xml @@ -1,54 +1,54 @@ - - - - - -