Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 詳解Android_性能優化之ViewPager加載成百上千高清大圖oom解決方案

詳解Android_性能優化之ViewPager加載成百上千高清大圖oom解決方案

編輯:關於Android編程

一、背景

最近做項目需要用到選擇圖片上傳,類似於微信、微博那樣的圖片選擇器,ContentResolver讀取本地圖片資源並用RecyclerView+Glide加載圖片顯示就搞定列表的顯示,這個沒什麼大問題,重點是,點擊圖片進入大圖浏覽,比如你相冊有幾百張圖片,也就意味著在ViewPager中需要加載幾百個view,況且手機拍出來的圖片都是1-2千萬左右像素的高清大圖(筆者手機2千萬像素 也就是拍照出來的照片3888*5152),大小也有5-7個兆,ViewPager滑動不了十幾張就oom了,即是對圖片做了壓縮處理,把圖片分辨率降低至1366*960,大小壓縮至150k以下,並且在ViewPager的destroyItem方法做了bitmap資源的回收,雖然效果好了點,這也抵擋不了oom的降臨(網上查找的方案都是壓縮、使用第三方控件、回收,其實這都沒用,可能他們沒有真正體驗過ViewPager加載幾百上千張大圖的感受),浏覽到了第50-70張的時候就oom了 內存一直暴漲,根本回收不了的,不信你們試試,壓縮和回收根本不能根治問題,那麼怎麼解決呢?研究了微信和微博,他們怎麼也不會oom,最後我想到了一種解決方案。

二、方案實施

1、以往的普通做法

部分代碼:

List<SubsamplingScaleImageView> mViews = new ArrayList<>(); 
     
    int size = mDatas.size(); 
    for (int i = 0; i < size; i++) { 
      SubsamplingScaleImageView view = new SubsamplingScaleImageView(this); 
      mViews.add(view); 
    } 
 
    mBinding.viewpager.setAdapter(new MyAdapter()); 
class MyAdapter extends PagerAdapter { 
 
    @Override 
    public int getCount() { 
      return mDatas.size(); 
    } 
 
    @Override 
    public boolean isViewFromObject(View view, Object object) { 
      return view == object; 
    } 
 
    @Override 
    public Object instantiateItem(ViewGroup container, final int position) { 
 
      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( 
          ViewPager.LayoutParams.MATCH_PARENT,ViewPager.LayoutParams.MATCH_PARENT); 
      final SubsamplingScaleImageView imageView = mViews.get(position); 
      imageView.setLayoutParams(params); 
 
      final String url = mDatas.get(position); 
      String cacheExists = cacheExists(url); 
      if(TextUtils.isEmpty(cacheExists)) {//沒緩存 需要壓縮(壓縮耗時 異步) 
        new AsyncTask<Void, Void, String>() { 
          @Override 
          protected String doInBackground(Void... voids) { 
            String cacheNoExistsPath = getCacheNoExistsPath(url); 
            BitmapCompressUtils.compressBitmap(url, cacheNoExistsPath); 
            File file = new File(cacheNoExistsPath); 
            if (file.exists()) {//存在表示成功 
              return cacheNoExistsPath; 
            } else { 
              return url; 
            } 
          } 
 
          @Override 
          protected void onPostExecute(String s) { 
            imageView.setImage(ImageSource.uri(s)); 
          } 
 
        }.execute(); 
 
 
      } else {//有緩存 直接顯示 
        imageView.setImage(ImageSource.uri(cacheExists)); 
      } 
 
      container.addView(imageView); 
      return imageView; 
 
    } 
 
    @Override 
    public void destroyItem(ViewGroup container, int position, Object object) { 
 
      SubsamplingScaleImageView imageView = mViews.get(position); 
      if(imageView != null) { 
        imageView.recycle(); 
      } 
 
      container.removeView(imageView); 
 
    } 
  } 

/** 
   * 判斷當前圖片url對應的壓縮過的緩存是否存在 ""表示不存在 
   * 
   * @param url 圖片路徑 
   * @return 
   */ 
  private String cacheExists(String url) { 
    try { 
      File fileDir = new File(mCacheRootPath); 
      if(!fileDir.exists()) { 
        fileDir.mkdirs(); 
      } 
 
      File file = new File(mCacheRootPath,new StringBuffer().append(MD5EncryptorUtils.md5Encryption(url)).toString()); 
      if(file.exists()) { 
        return file.getAbsolutePath(); 
      } 
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
 
    return ""; 
  } 
 
  public String getCacheNoExistsPath(String url) { 
    File fileDir = new File(mCacheRootPath); 
    if(!fileDir.exists()) { 
      fileDir.mkdirs(); 
    } 
 
 
    return new StringBuffer().append(mCacheRootPath) 
        .append(MD5EncryptorUtils.md5Encryption(url)).toString(); 
  } 

可以看到,這裡筆者通過自己的壓縮算法(上一篇文章Android_NDK圖片壓縮之Libjpeg庫使用 )做了圖片壓縮,並緩存,細心的朋友應該有發現mViews集合添加的view個數是mDatas的size大小個數,這樣就會導致一個問題ViewPager一直向下滑動的時候,內存一直是增加的,即是做了資源回收,也是不能解決問題(況且筆者這裡展示圖片的控件是SubsamplingScaleImageView 很不錯的大圖局部加載控件 能有效防止oom),大家可以試試,大量圖片的時候還是會oom,這得歸根於viewpager加載的圖片數量問題。

2、解決方案:

圖片壓縮也做了,資源回收也做了,但是ViewPager加載越來越多圖片的時候就會oom 你避免不了,不信你試試;

這裡就要用到ViewPager的view的重用機制(自己理解的),也就是mViews我們固定給定個數量,如4,這樣ViewPager的i實際所需要的item也就只有4個。

修改後的部分代碼:

for (int i = 0; i < 4; i++) { 
      SubsamplingScaleImageView view = new SubsamplingScaleImageView(this); 
      mViews.add(view); 
    } 
 
    mBinding.viewpager.setAdapter(new MyAdapter()); 
class MyAdapter extends PagerAdapter { 
 
    @Override 
    public int getCount() { 
      return mDatas.size(); 
    } 
 
    @Override 
    public boolean isViewFromObject(View view, Object object) { 
      return view == object; 
    } 
 
    @Override 
    public Object instantiateItem(ViewGroup container, final int position) { 
 
      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( 
          ViewPager.LayoutParams.MATCH_PARENT,ViewPager.LayoutParams.MATCH_PARENT); 
 
      int i = position % 4; 
      final SubsamplingScaleImageView imageView = mViews.get(i); 
      imageView.setLayoutParams(params); 
 
      final String url = mDatas.get(position); 
      String cacheExists = cacheExists(url); 
      if(TextUtils.isEmpty(cacheExists)) {//沒緩存 需要壓縮(壓縮耗時 異步) 
        new AsyncTask<Void, Void, String>() { 
          @Override 
          protected String doInBackground(Void... voids) { 
            String cacheNoExistsPath = getCacheNoExistsPath(url); 
            BitmapCompressUtils.compressBitmap(url, cacheNoExistsPath); 
            File file = new File(cacheNoExistsPath); 
            if (file.exists()) {//存在表示成功 
              return cacheNoExistsPath; 
            } else { 
              return url; 
            } 
          } 
 
          @Override 
          protected void onPostExecute(String s) { 
            imageView.setImage(ImageSource.uri(s)); 
          } 
 
        }.execute(); 
 
 
      } else {//有緩存 直接顯示 
        imageView.setImage(ImageSource.uri(cacheExists)); 
      } 
 
      container.addView(imageView); 
      return imageView; 
 
    } 
 
    @Override 
    public void destroyItem(ViewGroup container, int position, Object object) { 
      int i = position % 4; 
      SubsamplingScaleImageView imageView = mViews.get(i); 
      if(imageView != null) { 
        imageView.recycle(); 
      } 
 
      container.removeView(imageView); 
 
    } 

很簡單的修改 就能有效防止oom  利用position%4拿到第幾個控件從mViews取值,保證了viewpager加載的mViews存儲的圖片為4個

看一直向下滑動的內存走勢圖

內存基本維持穩定

三、demo演示

因為要讀取相冊就沒再模擬器運行錄制gif ,直接截圖


 

demo下載:demo

四、總結

這個只是簡單的演示,實際項目中的相冊比這個復雜多了,簡單說就是要壓縮,要回收,View重用。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved