最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android優(yōu)化查詢(xún)加載大數(shù)量的本地相冊(cè)圖片

 更新時(shí)間:2016年10月25日 14:48:36   作者:丿天下丶第一  
本文介紹了Android優(yōu)化查詢(xún)加載大數(shù)量的本地相冊(cè)圖片,可以方便的照片的查詢(xún),,感興趣的小伙伴們可以參考一下。

一、概述

講解優(yōu)化查詢(xún)相冊(cè)圖片之前,我們先來(lái)看下PM提出的需求,PM的需求很簡(jiǎn)單,就是要做一個(gè)類(lèi)似微信的本地相冊(cè)圖片查詢(xún)控件,主要包含兩個(gè)兩部分:

  • 進(jìn)入圖片選擇頁(yè)面就要顯示出手機(jī)中所有的照片,包括系統(tǒng)相冊(cè)圖片和其他目錄下的所有圖片,并按照時(shí)間倒敘排列
  • 切換相冊(cè)功能,切換相冊(cè)頁(yè)面列出手機(jī)中所有的圖片目錄列表,并且顯示出每個(gè)目錄下所有的圖片個(gè)數(shù)以及封面圖片

這兩個(gè)需求看似簡(jiǎn)單,實(shí)則隱藏著一系列的性能優(yōu)化問(wèn)題。在做優(yōu)化之前,我們調(diào)研了一些其他比較出名的app在加載大數(shù)量圖片的性能表現(xiàn)(gif錄制的不夠清晰,但展示問(wèn)題已經(jīng)夠了):

下面測(cè)試了幾個(gè)常用軟件

微信:
微信的圖片查詢(xún)速度還是非??斓?,基本上進(jìn)入圖片選擇頁(yè)面,相冊(cè)數(shù)據(jù)就已經(jīng)查出來(lái)了,包括各個(gè)圖片目錄下圖片的個(gè)數(shù)和封面圖片的url,這個(gè)體驗(yàn)還是比較好的。

新浪微博:
相比較微信來(lái)說(shuō),新浪微博做的體驗(yàn)就比較差了,進(jìn)入圖片選擇頁(yè)面后,先是黑屏然后是白屏,連個(gè)進(jìn)度條都沒(méi)有,讓用戶(hù)以為app死掉了,等過(guò)一段時(shí)間才顯示出來(lái),這個(gè)體驗(yàn)較差

QQ:
QQ一上來(lái)是加載的最近100張照片,這個(gè)速度非常快,但是進(jìn)入Camera相冊(cè)(有5000多張)后,有一個(gè)進(jìn)度條等待,我體驗(yàn)了下,等待的時(shí)間還是比較長(zhǎng)的,這個(gè)體驗(yàn)比新浪微博稍微好點(diǎn),比微信差

閑魚(yú):
閑魚(yú)是做的最爛的一個(gè),一上來(lái)是卡死四五秒,然后是黑屏兩三秒,最后才顯示出來(lái)

二、綜合對(duì)比

經(jīng)過(guò)綜合對(duì)比后,就微信做的還比較好,基本上進(jìn)入相冊(cè)頁(yè)面就能展示出所有照片,相冊(cè)目錄也非??斓恼故境鰜?lái)?。?!

經(jīng)過(guò)我們的調(diào)研,發(fā)現(xiàn)微信是采用循環(huán)分頁(yè)加載策略,我們優(yōu)化的思路也是采用這種策略,先看優(yōu)化后的效果圖:

進(jìn)入圖片選擇頁(yè)面,圖片能夠非??斓娘@示出來(lái),進(jìn)入更換相冊(cè)頁(yè)面,圖片目錄也能非??斓娘@示出來(lái),這里沒(méi)有像微信一樣做圖片目錄的緩存:一是因?yàn)椴樵?xún)速度非???,基本上不到2秒就加載出來(lái)了,二是能夠?qū)崟r(shí)刷新出相冊(cè)的最新數(shù)據(jù)

頻繁的切換各個(gè)相冊(cè)目錄,圖片都能非??焖俚牟樵?xún)出來(lái),體驗(yàn)還是不錯(cuò)的?。。?/p>

三、優(yōu)化實(shí)現(xiàn)

優(yōu)化查詢(xún)相冊(cè)目錄

因?yàn)橐信e出所有的相冊(cè)目錄列表,這里沒(méi)有其他好的辦法,直接請(qǐng)求ContentResolver的query方法來(lái)查詢(xún),這里為了加速查詢(xún),去掉了while循環(huán)中一些耗時(shí)的判斷,將一些檢測(cè)圖片是否判斷的邏輯移到外面去,具體用的時(shí)候再去判斷

查詢(xún)圖片的URI

MediaStore.Images.Media.EXTERNAL_CONTENT_URI

因?yàn)槲覀冎徊樵?xún)圖片url和圖片所在的目錄

String[] projection = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME};

PM要求相冊(cè)按照?qǐng)D片的時(shí)間倒敘排列,圖片的創(chuàng)建、修改會(huì)影響其所在目錄的排序,排序按時(shí)間倒敘排列

String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC ";

根據(jù)這些查詢(xún)條件,經(jīng)過(guò)query之后得到一個(gè)Cursor,這個(gè)cursor里面就包含我們所需要的所有圖片的信息,然后我們while循環(huán)遍歷這個(gè)cursor,在while循環(huán)中一定不能有耗時(shí)操作

//一個(gè)輔助集合,防止同一目錄被掃描多次
HashSet<String> dirPaths = new HashSet<String>();

while (cursor.moveToNext()) {
  // 獲取圖片的路徑
  String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
  String bucketName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME));

  if (TextUtils.isEmpty(allFolderItem.coverImagePath)) {
    allFolderItem.coverImagePath = path;
  }

  File parentFile = new File(path).getParentFile();
  if (parentFile == null) continue;

  String dirPath = parentFile.getAbsolutePath();

  PicFolderItem folderItem = null;
  // 利用一個(gè)HashSet防止多次掃描同一個(gè)文件夾(不加這個(gè)判斷,圖片多起來(lái)還是相當(dāng)恐怖的~~)
  if (dirPaths.contains(dirPath)) {
    continue;
  } else {
    dirPaths.add(dirPath);

    boolean isNew = true;
    //判斷一下是否dirPath不同,但是bucketName相同
    for (PicFolderItem item : picList) {
      if (item.name.equals(bucketName)) {
        folderItem = item;
        item.addParentPath(dirPath);
        isNew = false;
        break;
      }
    }

    if (isNew) {
      folderItem = new PicFolderItem();
      folderItem.coverImagePath = path;
      folderItem.name = bucketName;
      folderItem.addParentPath(dirPath);
    }
  }

  String[] array = parentFile.list(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String filename) {
      if (filename.endsWith(".jpg")
          || filename.endsWith(".png")
          || filename.endsWith(".jpeg"))
        return true;
      return false;
    }
  });

  int arrayCount = array == null ? 0 : array.length;
  folderItem.count += arrayCount;
  if (!picList.contains(folderItem) && arrayCount > 0) {
    picList.add(folderItem);
  }
}

這樣就能非??焖俚牟樵?xún)出手機(jī)中所有的圖片目錄、目錄的圖片張數(shù)以及封面圖url。這里主要優(yōu)化了三點(diǎn):

while循環(huán)中去除耗時(shí)判斷

之前的代碼中存在判斷文件圖片是否存在的代碼:

 public static boolean isFileExist(String path) {
  File file = new File(path);
  if (file == null || !file.exists()) {
    return false;
  }
  return true;
  }

這段代碼放到while循環(huán)中是很恐怖的,我測(cè)試了下,5000多張圖片都要檢測(cè)的話總時(shí)間會(huì)增加三四秒。這個(gè)判斷可以放到外面去,具體操作哪一個(gè)圖片的時(shí)候再做具體的業(yè)務(wù)判斷!

防止一個(gè)圖片文件夾被掃描多次

這里添加了一個(gè)變量來(lái)存儲(chǔ)已經(jīng)掃描過(guò)的圖片目錄,已經(jīng)掃描的就不在處理了:

//一個(gè)輔助集合,防止統(tǒng)一目錄查詢(xún)多次
HashSet<String> dirPaths = new HashSet<String>();

這塊優(yōu)化了之后效果還是很明顯的,相同的目錄不會(huì)掃描多次!

獲取圖片目錄下圖片個(gè)數(shù)

String[] array = parentFile.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String filename) {
              if (filename.endsWith(".jpg")
                  || filename.endsWith(".png")
                  || filename.endsWith(".jpeg"))
                return true;
              return false;
            }
          });

這個(gè)file.list()方法內(nèi)部是一個(gè)native方法,查詢(xún)效率非??欤。?!

當(dāng)然獲取某個(gè)目錄下的圖片有多少?gòu)堃部梢酝ㄟ^(guò)cursor查詢(xún)的方式來(lái)獲取??!!

查詢(xún)某個(gè)相冊(cè)目錄下的所有照片

在介紹查詢(xún)目錄下的照片之前,我們先介紹下我們查詢(xún)圖片的兩種策略,一種是針對(duì)目錄下圖片比較多的,動(dòng)不動(dòng)就上千上萬(wàn)張的那種;另一種是那種目錄下圖片比較少的,就幾百?gòu)垐D片

一次加載策略

當(dāng)目錄下圖片數(shù)量小于1000張時(shí)采用file.list這個(gè)native方法來(lái)一次加載所有圖片,這個(gè)native查詢(xún)效率非??欤锨垐D片都是秒級(jí)查詢(xún)出來(lái)

循環(huán)分頁(yè)加載策略

當(dāng)圖片數(shù)量大于等于1000張時(shí)采用循環(huán)分頁(yè)加載策略,這種策略專(zhuān)門(mén)針對(duì)圖片數(shù)量特別多的情況,通過(guò)分頁(yè)的方式先把第一頁(yè)的圖片加載出來(lái),讓用戶(hù)能第一眼看到最新的圖片,然后后臺(tái)異步循環(huán)的查詢(xún)下一頁(yè)圖片,直到所有圖片都查詢(xún)完成,這也是微信的查詢(xún)相冊(cè)策略。

一次加載策略實(shí)現(xiàn)

我們這里看下一次加載完策略實(shí)現(xiàn)代碼,首先通過(guò)File的list方法將后綴為圖片格式的文件過(guò)濾出來(lái),返回一個(gè)圖片路徑數(shù)組

File dirFile = new File(dir);
String[] list = dirFile.list(new FilenameFilter() {
  @Override
  public boolean accept(File dir, String filename) {
    if (filename.endsWith(".jpg") || filename.endsWith(".png")
        || filename.endsWith(".jpeg"))
      return true;
    return false;
  }
});

因?yàn)槲覀円氖前磿r(shí)間倒敘進(jìn)行排列的數(shù)組,所以要對(duì)上面查詢(xún)出來(lái)的數(shù)組進(jìn)行排序,這里用到了File文件lastModified方法

Collections.sort(strings, new Comparator<String>() {
        @Override
        public int compare(String lhs, String rhs) {
          Long time1 = new File(lhs).lastModified();
          Long time2 = new File(rhs).lastModified();
          return time2.compareTo(time1);
        }
        });

循環(huán)分頁(yè)加載策略實(shí)現(xiàn)

這個(gè)策略借鑒了微信,通過(guò)分頁(yè)的方式來(lái)一頁(yè)一頁(yè)的加載圖片,直到所有的圖片都加載完成。

這里的核心就是查詢(xún)條件,將你要查詢(xún)的某個(gè)目錄添加到查詢(xún)參數(shù)中

String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = '" + 目錄名稱(chēng) + "' ";

這個(gè)selection一定不能寫(xiě)錯(cuò),不然查詢(xún)不出來(lái)

因?yàn)橐猪?yè),sortOrder不是簡(jiǎn)單的按照時(shí)間倒敘來(lái)排了

String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC limit " + PAGE_SIZE + " offset " + pageIndex * PAGE_SIZE;

最后對(duì)Cursor進(jìn)行循環(huán)遍歷拿到我們要的圖片路徑

PAGE_SIZE是個(gè)常量,表示我們要一次查詢(xún)多少條,我們這里定的是200,一次查詢(xún)200條數(shù)據(jù),pageIndex是查詢(xún)第幾頁(yè),從0開(kāi)始

一開(kāi)始的時(shí)候查詢(xún)第一頁(yè)的數(shù)據(jù),當(dāng)查詢(xún)的數(shù)據(jù)列表大小大于等于我們要查詢(xún)的PageSize大小時(shí),我們就認(rèn)為有下一頁(yè),pageIndex加1循環(huán)查詢(xún)下一頁(yè),直到查詢(xún)的列表大小小于PageSize。

經(jīng)過(guò)上面幾步優(yōu)化后,加載本地相冊(cè)圖片基本上就沒(méi)有什么問(wèn)題了。我們經(jīng)過(guò)真機(jī)測(cè)試,圖片5549張,都能夠非??焖俚牟樵?xún)出來(lái),堪比微信和圖庫(kù)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • android RecyclerView添加footerview詳解

    android RecyclerView添加footerview詳解

    大家好,本篇文章主要講的是android RecyclerView添加footerview詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • android使用flutter的ListView實(shí)現(xiàn)滾動(dòng)列表的示例代碼

    android使用flutter的ListView實(shí)現(xiàn)滾動(dòng)列表的示例代碼

    現(xiàn)如今打開(kāi)一個(gè) App,比如頭條、微博,都會(huì)有長(zhǎng)列表,那么android使用flutter的ListView滾動(dòng)列表如何實(shí)現(xiàn),本文就來(lái)詳細(xì)的介紹一下,感興趣的同學(xué)可以來(lái)了解一下
    2018-12-12
  • Android?進(jìn)入Activity時(shí)如何禁止彈出軟鍵盤(pán)輸入法

    Android?進(jìn)入Activity時(shí)如何禁止彈出軟鍵盤(pán)輸入法

    這篇文章主要介紹了Android?進(jìn)入Activity時(shí)如何禁止彈出軟鍵盤(pán)輸入法,文章圍繞主題展開(kāi)具體內(nèi)容,需要的小伙伴可以參考一下
    2022-05-05
  • Flutter 路由插件fluro的使用

    Flutter 路由插件fluro的使用

    使用原生的路由基本上能夠滿足大部分需求,但如果想要對(duì)頁(yè)面做類(lèi)似瀏覽器 url 那樣的路由,或者控制頁(yè)面跳轉(zhuǎn)的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),那么原生的路由需要做不少的改造。在 pub 上,有優(yōu)秀的路由插件 fluro 解決這類(lèi)問(wèn)題。本文介紹該插件的使用方法
    2021-06-06
  • Android ImageView隨手勢(shì)變化動(dòng)態(tài)縮放圖片

    Android ImageView隨手勢(shì)變化動(dòng)態(tài)縮放圖片

    這篇文章主要為大家詳細(xì)介紹了Android ImageView隨手勢(shì)變化動(dòng)態(tài)縮放圖片的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Android selinux策略文件的編譯與加載

    Android selinux策略文件的編譯與加載

    這篇文章主要為大家介紹了Android selinux策略文件的編譯與加載用法解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Android  隱式Intent的實(shí)例詳解

    Android 隱式Intent的實(shí)例詳解

    這篇文章主要介紹了Android 隱式Intent的實(shí)例詳解的相關(guān)資料,隱式意圖就是在不明確設(shè)置激活對(duì)象的前提下尋找最匹配的組件,需要的朋友可以參考下
    2017-08-08
  • Android優(yōu)雅的方式解決軟鍵盤(pán)遮擋按鈕問(wèn)題

    Android優(yōu)雅的方式解決軟鍵盤(pán)遮擋按鈕問(wèn)題

    這篇文章主要介紹了Android優(yōu)雅的方式解決軟鍵盤(pán)遮擋按鈕問(wèn)題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-01-01
  • Android動(dòng)態(tài)加載Activity原理詳解

    Android動(dòng)態(tài)加載Activity原理詳解

    這篇文章主要介紹了Android動(dòng)態(tài)加載Activity原理詳解的相關(guān)資料,需要的朋友可以參考下
    2016-04-04
  • ExpandListView實(shí)現(xiàn)下拉列表案例

    ExpandListView實(shí)現(xiàn)下拉列表案例

    這篇文章主要為大家詳細(xì)介紹了ExpandListView實(shí)現(xiàn)下拉列表案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08

最新評(píng)論

耒阳市| 凤冈县| 谷城县| 溧阳市| 罗平县| 无棣县| 顺平县| 阿拉善盟| 女性| 蓝山县| 无棣县| 绥阳县| 澄迈县| 兰溪市| 安陆市| 合阳县| 来凤县| 张家界市| 朔州市| 北海市| 沙坪坝区| 南安市| 河池市| 大英县| 伊吾县| 台安县| 玉田县| 台北市| 隆昌县| 庆云县| 乡宁县| 清涧县| 渭南市| 义马市| 海淀区| 商城县| 扎鲁特旗| 黎川县| 盘山县| 兴文县| 板桥市|