XUtils改造以新增drawable支援
=== XUtilsBitmapUtils 改造以新增drawable支援 ===
# XUtils 簡介
XUtils 是一套少有的早期國產安卓框架, 其源於AFinal, 目錄結構也與之相似, 但是程式碼卻進行了大量的重構, 是XUtils更加現代, 解決了AFinal 的OOM等問題.
目前 XUtils 已經支援 API 8(android 2.2) 至 API 21(android 5.0.x).
XUtils 主要內建了DbUtils 模組, ViewUtils 模組, HttpUtils 模組, BitmapUtils 模組.
對於新手來說, 這些功能著實使用而且強大, 為我們省下了不少的功夫 去處理業務.
同類的框架, 國外流行的的有androidannotations, roboguice, androidquery,droidparts等, 當然國內也有不少競爭者,
ThinkAndroid,UltimateAndroid, LoonAndroid, KJFrameForAndroid, SmartAndroid, 都是可以可以用來借鑑的. 本文暫時專注於XUtils的使用.
具體的細化模組可以參考 官方地址(wyouflf/xUtils):
https://github.com/wyouflf/xUtils
做過android的同學一定都知道安卓處理Bitmap可謂一絕, Bitmap絕對是吃記憶體的大戶, 而且Dalvik
XUtils 的圖片處理存在快取, 主要是記憶體快取和外存快取. 但是這不是今天本文的重點, 但是以後會提及. 今天主要說說XUtils不太好的方面, 首先直接上修改過的官方程式碼:
// this 是一個 Context
BitmapUtilsbitmapUtils = new BitmapUtils(this);
// 載入網路圖片
bitmapUtils.display(testImageView,"http://www.52deng.com/logo.png");
bitmapUtils.display(testImageView,"ftp://www.52deng.com/logo.png");
// 載入本地圖片, 路徑以/開頭, 需要填寫絕對路徑
bitmapUtils.display(testImageView,"/sdcard/dengdeng/test.jpg");
// 載入assets中的圖片, 路徑以assets開頭
bitmapUtils.display(testImageView,"assets/dengdeng/wallpaper.jpg");
// 使用ListView等容器展示圖片時, 可通過PauseOnScrollListener在滑動和快速滑動過程中控制暫停載入圖片
listView.setOnScrollListener(newPauseOnScrollListener(bitmapUtils, false, true));
listView.setOnScrollListener(newPauseOnScrollListener(bitmapUtils, false, true, customListener));
註釋已經被我優化, 相信結合程式碼, 語義應該更加明朗了. (← 你夠了, 語文渣)
但是仔細觀察會發現, 其實UXtils還是有不完美的地方: 貌似並不支援從專案中的drawable獲取圖片進行展示, 這樣豈不是遇到大圖片又要回歸BitmapFactory.Options等基礎方案了嗎? 這裡給大家推薦一下另一個安卓專攻圖片處理的框架Android-Universal-Image-Loader, 官方地址如下:
https://github.com/nostra13/Android-Universal-Image-Loader,
看關注度就能看出來, 它在Github上處於壟斷地位, 當然還有其他的專攻網路和圖片非同步的框架(國外的有glide, ion, Picasso,volley等), 都是很厲害和出名的. 那麼我們來看看 他支援的圖片處理方案, 不改了, 直接引用官方的例子:
"http://site.com/image.png"// from Web
"file:///mnt/sdcard/image.png"// from SD card
"file:///mnt/sdcard/video.mp4"// from SD card (video thumbnail)
"content://media/external/images/media/13"// from content provider
"content://media/external/video/media/13"// from content provider (video thumbnail)
"assets://image.png"// from assets
"drawable://"+ R.drawable.img// fromdrawables (non-9patch images)
NOTE:Use drawable://
only if you really need it! Always considerthe native way toload drawables -ImageView.setImageResource(...)
instead
of using of ImageLoader
.
看, 他是 支援多種圖片協議或者儲存路徑的, 也包括drawable, 但是值得注意的是, 他其實並不推薦快取drawable, 根據我的理解, 畢竟有一些drawable很小, 直接使用 ImageView 等空間自帶的放置圖片的方法即可. 但是遇到OOM的話, 該出手時就出手. 由於時間緊迫, 暫時不細研究這款開源專案的設計, 直接扒程式碼. 檢出專案之後, Ctrl+H選擇專案, 全文搜尋 關鍵字”drawable://”. 結果出來一堆東西, 換個思路, 搜尋”assets://”, 居然找到的是例子, 再換思路, 搜尋”assets:”, ok, 僥倖找到了核心程式碼 (其實他是通過 scheme 匹配傳遞的url的協議的):
// com.nostra13.universalimageloader.core.download.BaseImageDownloader.Java
@Override
public InputStream getStream(StringimageUri,Objectextra) throws IOException{
switch (Scheme.ofUri(imageUri)) {
caseHTTP:
caseHTTPS:
return getStreamFromNetwork(imageUri,extra);
caseFILE:
return getStreamFromFile(imageUri,extra);
caseCONTENT:
return getStreamFromContent(imageUri,extra);
caseASSETS:
return getStreamFromAssets(imageUri,extra);
caseDRAWABLE:
return getStreamFromDrawable(imageUri,extra);
caseUNKNOWN:
default:
return getStreamFromOtherSource(imageUri,extra);
}
}
好吧, 把DRAWABLE看看:
protected InputStream getStreamFromDrawable(StringimageUri,Objectextra) {
String drawableIdString = Scheme.DRAWABLE.crop(imageUri);
int drawableId =Integer.parseInt(drawableIdString);
return context.getResources().openRawResource(drawableId);
}
OK了, 那麼來匹配一下 Xutils, 開啟Ctrl+H全文搜尋開啟, 搜尋”assets”, 輕鬆找到了:
// com.lidroid.xutils.bitmap.download.DefaultDownloader.java
if (uri.startsWith("/")) {
FileInputStream fileInputStream =newFileInputStream(uri);
fileLen = fileInputStream.available();
bis = new BufferedInputStream(fileInputStream);
result = system.currentTimeMillis() +this.getDefaultExpiry();
} else if (uri.startsWith("assets/")) {
InputStream inputStream = this.getContext().getAssets().open(uri.substring(7,uri.length()));
fileLen = inputStream.available();
bis = new BufferedInputStream(inputStream);
result = Long.MAX_VALUE;
} else {
final URLurl =newURL(uri);
urlConnection =url.openConnection();
urlConnection.setConnectTimeout(this.getDefaultConnectTimeout());
urlConnection.setReadTimeout(this.getDefaultReadTimeout());
bis = new BufferedInputStream(urlConnection.getInputStream());
result = urlConnection.getExpiration();
result = result < System.currentTimeMillis() ?System.currentTimeMillis() +this.getDefaultExpiry() :result;
fileLen = urlConnection.getContentLength();
}
}
有了前幾步的經驗, 輕鬆改造, 加一個else if, 程式碼 參考之前的Android-Universal-Image-Loader的核心程式碼:
if (uri.startsWith("/")) {
FileInputStream fileInputStream =newFileInputStream(uri);
fileLen = fileInputStream.available();
bis = new BufferedInputStream(fileInputStream);
result = System.currentTimeMillis() +this.getDefaultExpiry();
} else if (uri.startsWith("assets/")) {
InputStream inputStream = this.getContext().getAssets().open(uri.substring(7,uri.length()));
fileLen = inputStream.available();
bis = new BufferedInputStream(inputStream);
result = Long.MAX_VALUE;
} else if (uri.startsWith("drawable://")) {//赤裸裸地抄襲,我也用這個協議
String drawableIdString = uri.substring(11,uri.length());//注意別算錯了
intdrawableId =Integer.parseInt(drawableIdString); //還原原始的 id
InputStream inputStream = this.getContext().getResources().openRawResource(drawableId);
fileLen = inputStream.available();//抄上面的
bis = new BufferedInputStream(inputStream); //抄上面的
result = Long.MAX_VALUE;//抄上面的,先這麼寫,以後討論
} else {
final URLurl =newURL(uri);
urlConnection =url.openConnection();
urlConnection.setConnectTimeout(this.getDefaultConnectTimeout());
urlConnection.setReadTimeout(this.getDefaultReadTimeout());
bis = new BufferedInputStream(urlConnection.getInputStream());
result = urlConnection.getExpiration();
result = result < System.currentTimeMillis() ?System.currentTimeMillis() +this.getDefaultExpiry() :result;
fileLen = urlConnection.getContentLength();
}
}
輕鬆改造, TEST!!
String uri = "drawable://" +R.drawable.super_larger_logo;
BitmapHelp.getBitmapUtils(this).display(largePic_imgV, uri);
OK, 測試通過, 圖片出現, 未出現OOM. 至此, 成功地新增drawable支援. 同理, 我們也可以抄抄”content”等協議的程式碼, 本文就不贅述了, 注意原始碼的協議, 引用或者修改都要留出處啊.