XUtils BitmapUtils 改造以加入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虛擬機(臨時不考慮ART技術)垃圾回收常常不及時, 所以圖片處理不當,常常會出現OOM(out of memory), 即內存溢出的情況. 在接觸XUtils等框架之前, 非常多人都是自己通過BitmapFactory.Options 來解決燃眉之急, 網上也有非常多對策, 可是這樣非常不系統, 並且有些方案, 比如使用軟引用或者弱引用, 已經在安卓4之後不再被推薦(事實上com.lidroid.xutils.bitmap.core.AsyncDrawable.java 還是用了弱引用
XUtils 的圖片處理存在緩存, 主要是內存緩存和外存緩存. 可是這不是今天本文的重點, 可是以後會提及. 今天主要說說XUtils不太好的方面, 首先直接上改動過的官方代碼:
// this 是一個 Context
BitmapUtils bitmapUtils = 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選擇項目, 全文搜索 keyword”drawable://”. 結果出來一堆東西, 換個思路, 搜索”assets://”, 竟然找到的是樣例, 再換思路, 搜索”assets:”, ok, 僥幸找到了核心代碼 (事實上他是通過 scheme 匹配傳遞的url的協議的):
// com.nostra13.universalimageloader.core.download.BaseImageDownloader.java
@Override
public InputStream getStream(String imageUri,Objectextra) throws IOException{
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return getStreamFromNetwork(imageUri,extra);
case FILE:
return getStreamFromFile(imageUri,extra);
case CONTENT:
return getStreamFromContent(imageUri,extra);
case ASSETS:
return getStreamFromAssets(imageUri,extra);
case DRAWABLE:
return getStreamFromDrawable(imageUri,extra);
case UNKNOWN:
default:
return getStreamFromOtherSource(imageUri,extra);
}
}
好吧, 把DRAWABLE看看:
protected InputStream getStreamFromDrawable(String imageUri,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());//註意別算錯了
int drawableId =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”等協議的代碼, 本文就不贅述了, 註意源碼的協議, 引用或者改動都要留出處啊.
2015-03-14 PS: 註意不要對 *.9.png 套用, 這非常沒有必要, *.9.png 通常非常小. 此外 對圖片實時性要求高的也不可用上面的方案, 盡管能解決 內存溢出和卡頓, 可是 播放連貫性 和 用戶體驗等會打大折扣的.
Presented by imknown
2015-03-13
XUtils BitmapUtils 改造以加入drawable支持