一. 問題
使用的Fresco版本為: 0.14.1
最近QA提了一個bug, 說項目中的一個圖片選擇頁面的一張圖片加載失敗 (只有那一張圖片失敗, 其他的OK !). 瞬間蒙逼了......
經過各種調試, 發現那張圖片的名稱非常詭異, 是這樣的EOX}6W$Q~5U$5O3U%~R94]P.jpg
. 我懷疑這張圖片的命名有問題, 于是把名稱改成a.jpg, 發現加載成功了!! 我加載圖片的代碼如下:
SimpleDraweeView draweeView;
//本地圖片文件的路徑, 如: `/storage/emulated/0/DCIM/Camera/EOX}6W$Q~5U$5O3U%~R94]P - 復制 (4).jpg`
String filepath;
//省略無關代碼 ......
Uri uri = Uri.parse("file://" + filepath); //Uri.fromFile(new File(filepath));
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(new ResizeOptions(draweeView.getWidth(), draweeView.getHeight()))
.build();
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setOldController(targetView.getController())
.setImageRequest(request)
.setCallerContext(uri)
.build();
draweeView.setController(controller);
URI中的特殊字符必須要經過轉義, 否則會出問題. 更多討論, 請看此issue
Uri.parse()
方法的定義如下:
/**
* Creates a Uri which parses the given encoded URI string.
*
* @param uriString an RFC 2396-compliant, encoded URI
* @throws NullPointerException if uriString is null
* @return Uri for this given uri string
*/
public static Uri parse(String uriString) {
return new StringUri(uriString);
}
從方法注釋中可以看到, 此方法接收的參數是經過編碼的uri字符串. 直接把文件的路徑拼接一下傳進去當然是不行的. 那么對文件路徑拼接成的uri字符串進行編碼, 然后傳遞給此方法不就可以了?! 然并卵!! 修改代碼, 運行APP后發現所有圖片都加載不出來了!! 上面的issue中有說到, 文件路徑拼接成的uri字符串, 就算編碼過再傳給Uri.parse方法也是不行的 !!
issue中的說明如下(后面一個方法名說錯啦, 是Uri.fromFile(File file)
, 而不是Uri.parseFromFile()
):
二. 解決方案
對于獲取本地文件的uri對象, android.net.Uri類提供了一個靜態方法: fromFile(File file)
. 其定義如下:
/**
* Creates a Uri from a file. The URI has the form
* "file://<absolute path>". Encodes path characters with the exception of
* '/'.
*
* <p>Example: "file:///tmp/android.txt"
*
* @throws NullPointerException if file is null
* @return a Uri for the given file
*/
public static Uri fromFile(File file) {
if (file == null) {
throw new NullPointerException("file");
}
PathPart path = PathPart.fromDecoded(file.getAbsolutePath());
return new HierarchicalUri(
"file", Part.EMPTY, path, Part.NULL, Part.NULL);
}
此方法接受一個文件對象, 然后返回此文件的Uri對象.
對于獲取文件的Uri對象, 推薦使用此方法 !
最終解決方案: 使用android.net.Uri.fromFile(File file)方法獲取本地文件的Uri對象.
代碼如下:
Uri uri = Uri.fromFile(new File(filepath));
.
.
.
更多圖片框架使用問題可以參考:
圖片框架使用問題之一: UIL導致的OOM
圖片框架使用問題之二: Fresco框架導致的OOM (com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:4055))
References:
Fresco官方中文文檔
Fresco: issue#1088, 加載本地文件之特殊文件名