仿微信相冊選擇圖片,查看大圖,寫的不太好,但願評論指出不足,諒解,先介紹一下個人基本思路。
轉載請註明出處:blog.csdn.net/self_study/…
對技術感興趣的同鞋加羣 544645972 一塊兒交流。javascript
第一步獲取手機上的全部圖片路徑:java
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = getContentResolver();
//獲取jpeg和png格式的文件,而且按照時間進行倒序
Cursor cursor = contentResolver.query(uri, null, MediaStore.Images.Media.MIME_TYPE + "=\"image/jpeg\" or " +
MediaStore.Images.Media.MIME_TYPE + "=\"image/png\"", null, MediaStore.Images.Media.DATE_MODIFIED+" desc");
if (cursor != null){
while (cursor.moveToNext()){
//do something
}
handler.sendEmptyMessage(0);
}複製代碼
而後定義一個存儲圖片的數據格式:git
/** 按時間排序的全部圖片list */
private ArrayList<SingleImageModel> allImages;
/** 按目錄排序的全部圖片list */
private ArrayList<SingleImageDirectories> imageDirectories;
/** * 一個文件夾中的圖片數據實體 */
private class SingleImageDirectories{
/** 父目錄的路徑 */
public String directoryPath;
/** 目錄下的全部圖片實體 */
public ImageDirectoryModel images;
}複製代碼
一個是所有圖片的存儲順序,第二個是按照目錄的圖片存儲順序。github
獲取到圖片以後,放入到 Gridview 中進行顯示,可是 BitmapFactory.decodeFile() 函數會很是耗時,因此爲了使得很是流暢的顯示圖片,建立一個類 AlbumBitmapCacheHelper
,用來異步加載圖片,該類使用 LruCache<String,Bitmap>
來緩存 Bitmap,使得存儲圖片不會形成 OOM,我這裏設置 LruCache 的初始大小爲 1/4 的運行時內存而後使用 ThreadPoolExecutor
線程池來處理圖片的顯示,線程池大小應該設置適中(能夠根據系統的處理器線程數來設置,系統也提供一個相關的線程池,感興趣的也能夠去了解),作完這兩件事情以後就能夠用來加載圖片了,方法 getBitmap 用來返回圖片:數據庫
Bitmap bitmap = getBitmapFromCache(path, width, height);
//若是可以從緩存中獲取符合要求的圖片,則直接回調
if (bitmap != null) {
} else {
//新建線程放入線程池去處理該圖片的顯示
}
return bitmap;複製代碼
若是 cache 中找不到該圖片,則調用 BitmapFactory.decodeFile() 去加載圖片,加載圖片不可以直接加載原圖,會形成 OOM,因此要去計算壓縮比:緩存
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = computeScale(options, width, height);
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(path, options);
//獲取以後,放入緩存,以便下次繼續使用
if (bitmap != null && cache!=null) {
cache.put(path, bitmap);
}複製代碼
方法 computeScale() 主要是計算圖片最小的壓縮比,這樣在 Gridview 中的 getview 方法中去調用 AlbumBitmapCacheHelper
的 getBitmap() 方法便可。性能優化
通過上面的處理以後,在實際顯示的時候會出現一些問題,這裏也彙總和分析一下:微信
第一個問題就是圖片顯示會閃爍,這主要是因爲 getview 方法的 convertView 的複用致使一個 Imageview 被設置屢次 background,解決方法就是使用 setTag 方法:異步
holder.iv_content.setTag(path);複製代碼
將要顯示的 Imageview 的 tag 設置爲須要顯示的圖片路徑,這樣在回調的時候使用方法 gridView.findViewWithTag(path),找到這個 Imageview 進行顯示,閃爍的問題就解決了。函數
第二個問題就是加載速度很慢,拉的速度很快的狀況下,圖片要好久纔會加載出來,特別是很大的圖片,好比拍照和截圖的照片,這個問題能夠有兩個步驟去優化:第一個優化方案就是在 AlbumBitmapCacheHelper
類中維護一個 ArrayList
//優化顯示效果
if(holder.iv_content.getTag() != null) {
String remove = (String) holder.iv_content.getTag();
AlbumBitmapCacheHelper.getInstance().removePathFromShowlist(remove);
}
AlbumBitmapCacheHelper.getInstance().addPathToShowlist(path);複製代碼
這樣在線程池中的處理方式就是先查看須要顯示的 path 是否在 ArrayList 中,若是沒有在 ArrayList 中,則該線程直接關閉,若是在 ArrayList 中,則顯示該圖片:
if (!currentShowString.contains(path)||cache==null) {
return;
}複製代碼
第二個優化方案是若是顯示的圖片很大,特別是拍照和截圖的圖片,decode 有時會耗時幾秒鐘,微信顯示效果很是好,我本身參考微信的表現想出來的處理的方式是:
if (!new File(CommonUtil.getDataPath()).exists())
new File(CommonUtil.getDataPath()).mkdirs();
//臨時文件的文件名
String tempPath = CommonUtil.getDataPath() + hash + ".temp";
//若是該文件存在
if (new File(tempPath).exists())
bitmap = BitmapFactory.decodeFile(tempPath);
......
//第三步,若是縮放比例大於4,該圖的加載會很是慢,因此將該圖保存到臨時目錄下以便下次的快速加載
if (options.inSampleSize >= 4) {
try {
File file = new File(tempPath);
if (!file.exists())
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}複製代碼
問題到這裏就差很少解決了;
第四步大圖的查看,大圖主要是使用網上開源的 ZoomImageView+Viewpagger 的組合,可是使用這個出現的問題就是很容易 OOM,沒辦法,個人處理方式就是在點進去大圖的時候:
public void releaseHalfSizeCache() {
cache.resize((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));
}複製代碼
直接將 LruCache 的大小變成原來的一半,由於查看大圖頁加載一張大圖佔用的內存自己就很大,因此這樣去特殊處理,顯示效果頁還湊合,有別的方法,必定要留言告訴我。
還有一點須要注意的是大圖的查看因爲須要經過 intent 傳遞數據,可是 intent 傳遞的數據大小不能太大,若是手機上有幾千張圖片,則數據量大小可能會超過 intent 所能傳遞的最大量,因此能夠寫入一個公共的地方,共享內存、數據庫或者文件均可以,具體的緣由能夠參考個人另外一篇博客:Android TransactionTooLargeException 解析,思考與監控方案。
圖片選擇完成以後,完成善後工做,將 AlbumBitmapCacheHelper
類中 LruCache 清空,差很少就這樣了,還有不少的功能小點,好比圖片時間的顯示,這裏就不詳細一一去介紹了,具體你們看源碼。
最新發現的問題:3.0 之前 GC 操做須要很長時間,常常大於 100ms,在執行 GC 時(關於 JVM 和 ART 的 GC 能夠看看個人這篇博客 Android 性能優化以內存泄漏檢測以及內存優化(上)),程序就會出現卡的現象,3.0 之後 GC 修改成同步,執行的時間一般在 5ms 之內,在 3.0 之前的版本中,加載圖片時,系統把 Bitmap 加載到 Native 緩存中,並不受 GC 管理,須要手機本身釋放,否則會遇到莫名奇妙的內存問題。3.0 之後 Bitmap 直接放到內存中在執行 GC 時,會及時清理無用的 Bitmap 所佔的內存,在初始化圖片時把圖片放到內存中,當加載完後,系統會把圖片從內存轉移到顯存中,當你用內存測試工具時,會發如今加載圖片時,內存佔用率很高,當加載完成後,內存使用量忽然下來,加載大量圖片時會發現這種狀況。
總而言之就是 2.x 版本的時候,就算你使用的是 LruCache,Bitmap 仍是不會被 GC 主動回收,必需要手動釋放,因此若是須要適配 2.x 版本,該 demo 須要加入手動釋放 Bitmap 的操做。
源碼下載地址github.com/zhaozepeng/…。