1. 程式人生 > >XUtils BitmapUtils 改造以加入drawable支持

XUtils BitmapUtils 改造以加入drawable支持

awr assets 美的 not lin conn sch vol ati

=== 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 還是用了弱引用

), 仍然可能會出現OOM. 所以一款流行的, 穩定的, 現代的代碼框架是不可缺少的. XUtils 恰恰滿足了這一點.

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支持