07.Android之多媒體問題

目錄介紹

  • 7.0.0.1 加載bitmap圖片的時候須要注意什麼?爲什麼bitmap容易形成OOM?如何計算Bitmap佔用內存?
  • 7.0.0.2 如何理解recycle釋放內存問題?圖片加載到內存其實有兩部分數據,這是爲什麼?
  • 7.0.0.3 如何在不壓縮圖片的狀況下加載高清大圖?加載圖的機制是什麼,爲什麼不會內存泄漏?
  • 7.0.0.7 LRU算法的原理?核心思想是什麼?若是緩存滿了的話,什麼方法來管理移除最近最少使用的item和添加新的item?

好消息

  • 博客筆記大彙總【15年10月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong2...
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!全部的筆記將會更新到GitHub上,同時保持更新,歡迎同行提出或者push不一樣的見解或者筆記!

7.0.0.1 加載bitmap圖片的時候須要注意什麼?爲什麼bitmap容易形成OOM?如何計算Bitmap佔用內存?

  • 注意問題php

    • 直接加載大容量的高清Bitmap很容易出現顯示不完整、內存溢出OOM的問題,因此最好按必定的採樣率將圖片縮小後再加載進來
    • 爲減小流量消耗,可對圖片採用內存緩存策略,又爲了不圖片佔用過多內存致使內存溢出,最好以軟引用方式持有圖片
    • 若是還須要網上下載圖片,注意要開子線程去作下載的耗時操做技術博客大總結
  • 爲什麼bitmap容易形成OOM
  • 如何計算Bitmap佔用內存java

    • 1.1 如何計算佔用內存android

      • 若是圖片要顯示下Android設備上,ImageView最終是要加載Bitmap對象的,就要考慮單個Bitmap對象的內存佔用了,如何計算一張圖片的加載到內存的佔用呢?其實就是全部像素的內存佔用總和:
      • bitmap內存大小 = 圖片長度 x 圖片寬度 x 單位像素佔用的字節數
      • 起決定因素就是最後那個參數了,Bitmap'常見有2種編碼方式:ARGB_8888和RGB_565,ARGB_8888每一個像素點4個byte,RGB_565是2個byte,通常都採用ARGB_8888這種。那麼常見的1080*1920的圖片內存佔用就是:1920 x 1080 x 4 = 7.9M
    • 1.2 上面方法計算內存對嗎c++

      • 我看到好多博客都是這樣計算的,可是這樣算對嗎?有沒有哥們試驗過這種方法正確性?我以爲看博客要對博主表示懷疑,論證別人寫的是否正確。更多詳細能夠看個人GitHub:https://github.com/yangchong211git

        • 說出個人結論:上面1.1這種說法也對,可是不全對,沒有說明場景,同時也忽略了一個影響項:Density。接下來看看源代碼。
        • inDensity默認爲圖片所在文件夾對應的密度;inTargetDensity爲當前系統密度。
        • 加載一張本地資源圖片,那麼它佔用的內存 = width height nTargetDensity/inDensity nTargetDensity/inDensity 一個像素所佔的內存。
        @Nullable
        public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
                @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
            validate(opts);
            if (opts == null) {
                opts = new Options();
            }
        
            if (opts.inDensity == 0 && value != null) {
                final int density = value.density;
                if (density == TypedValue.DENSITY_DEFAULT) {
                    opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
                } else if (density != TypedValue.DENSITY_NONE) {
                    opts.inDensity = density;
                }
            }
            
            if (opts.inTargetDensity == 0 && res != null) {
                opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
            }
            
            return decodeStream(is, pad, opts);
        }
      • 正確說法,這個注意呢?計算公式以下所示github

        • 對資源文件:width height nTargetDensity/inDensity nTargetDensity/inDensity 一個像素所佔的內存;
        • 別的:width height 一個像素所佔的內存;
    • 1.3 一個像素佔用多大內存技術博客大總結面試

      • Bitmap.Config用來描述圖片的像素是怎麼被存儲的?算法

        • ARGB_8888: 每一個像素4字節. 共32位,默認設置。
        • Alpha_8: 只保存透明度,共8位,1字節。
        • ARGB_4444: 共16位,2字節。
        • RGB_565:共16位,2字節,只存儲RGB值。

7.0.0.2 如何理解recycle釋放內存問題?圖片加載到內存其實有兩部分數據,這是爲什麼?

  • 如何理解recycle釋放內存問題?segmentfault

    • 在Android2.3.3(API 10)及以前的版本中,Bitmap對象與其像素數據是分開存儲的,Bitmap對象存儲在Dalvik heap中,而Bitmap對象的像素數據則存儲在Native Memory(本地內存)中或者說Derict Memory(直接內存)中,這使得存儲在Native Memory中的像素數據的釋放是不可預知的,咱們能夠調用recycle()方法來對Native Memory中的像素數據進行釋放,前提是你能夠清楚的肯定Bitmap已再也不使用了,若是你調用了Bitmap對象recycle()以後再將Bitmap繪製出來,就會出現」Canvas: trying to use a recycled bitmap」錯誤,而在Android3.0(API 11)以後,Bitmap的像素數據和Bitmap對象一塊兒存儲在Dalvik heap中。
  • 圖片加載到內存其實有兩部分數據,這是爲什麼?技術博客大總結數組

    • 一個圖片加載到內存裏,實際上是有兩部分數據組成,一部分是圖片的相關描述信息,另外一部分就是最重要的像素信息(這部分是有byte數組組成的),android系統爲了提升對圖片的處理效率,對於圖片的處理都是調用了底層的功能(由C語言實現的),也就是說一個圖片加載到內存裏後是使用兩部分的內存區域,簡單的說:一部分是java可用的內存區,一部分是c可用的內存區,這兩個內存區域是不能相互直接使用的,這個bitmap對象是由java分配的,固然不用的時候系統會自動回收了,但是那個對應的C可用的內存區域jvm是不能直接回收的,這個只能調用底層的功能釋放。因此你要調用recycle方法來釋放那一部份內存。
  • 查看源碼以下所示

    • image
    • 翻譯這段解釋:釋放bitmap內存的時候,它會釋放和這個bitmap有關的native內存,同時它會清理有關數據對象的引用,可是這裏處理數據對象的引用,並非當即清理數據(他並非調用玩recycle()方法,就直接清理這個內存,他只是給垃圾回收機制發送一個指令,讓它在bitmap沒有對象引用的時候,來進行垃圾回收)。當調用recycle()方法以後,這個bitmap就會被代表爲「死亡狀態」。這個時候你在調用bitmap其餘相關的方法,例若是get像素()或set像素()就會拋出一個異常。同時這個操做是不可逆的,因此必定百分之百肯定這個bitmap在之後的場景下,不會被你的程序在使用到,再去調用recycle()方法。因此谷歌源碼中建議咱們,能夠不用去主動調用recycle()方法,由於在沒有引用的狀況下,咱們的垃圾回收機制會主動的清理內存。
    • 經過看源碼,咱們會發現,這個方法首先將這個Bitmap的引用置爲null,而後調用了nativeRecycle(mNativeBitMap)方法,這個方法很明顯是個JNI調用,會調用底層的c或者c++代碼就能夠作到對該內存的當即回收,而不須要等待那不肯定啥時候會執行的GC來回收了。

7.0.0.3 如何在不壓縮圖片的狀況下加載高清大圖?加載圖的機制是什麼,爲什麼不會內存泄漏?

  • 如何在不壓縮圖片的狀況下加載高清大圖?

    • 使用BitmapRegionDecoder,主要用於顯示圖片的某一塊矩形區域,若是你須要顯示某個圖片的指定區域,那麼這個類很是合適。
  • 加載圖的機制是什麼,爲什麼不會內存泄漏?
  • 自定義可拖動的顯示高清大圖的View技術博客大總結

    • 提供一個設置圖片的入口,setInputStream裏面去得到圖片的真實的寬度和高度,以及初始化咱們的mDecoder
    • 重寫onTouchEvent,在裏面根據用戶移動的手勢,去更新顯示區域的參數。在onMeasure裏面爲咱們的顯示區域的rect賦值,大小爲view的尺寸
    • 每次更新區域參數後,調用invalidate,onDraw裏面去regionDecoder.decodeRegion拿到bitmap,而後draw。

7.0.0.7 LRU算法的原理?核心思想是什麼,談談你的思路?

  • 爲減小流量消耗,可採用緩存策略。經常使用的緩存算法是LRU(Least Recently Used):

    • 核心思想:當緩存滿時, 會優先淘汰那些近期最少使用的緩存對象。主要是兩種方式:

      • LruCache(內存緩存):LruCache類是一個線程安全的泛型類:內部採用一個LinkedHashMap以強引用的方式存儲外界的緩存對象,並提供get和put方法來完成緩存的獲取和添加操做,當緩存滿時會移除較早使用的緩存對象,再添加新的緩存對象。
      • DiskLruCache(磁盤緩存): 經過將緩存對象寫入文件系統從而實現緩存效果
  • 大概過程以下?技術博客大總結

    • LruCache是android提供的一個緩存工具類,其算法是最近最少使用算法。它把最近使用的對象用「強引用」存儲在LinkedHashMap中,而且把最近最少使用的對象在緩存值達到預設定值以前就從內存中移除。
  • 若是緩存滿了的話,什麼方法來管理移除最近最少使用的item和添加新的item?

    • trimToSize()方法,刪除最年長的條目,直到剩餘條目的總數達到或低於請求的大小

      public void trimToSize(int maxSize) {
          while(true) {
              Object key;
              Object value;
              synchronized(this) {
                  if (this.size < 0 || this.map.isEmpty() && this.size != 0) {
                      throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
                  }
      
                  if (this.size <= maxSize || this.map.isEmpty()) {
                      return;
                  }
      
                  Entry<K, V> toEvict = (Entry)this.map.entrySet().iterator().next();
                  key = toEvict.getKey();
                  value = toEvict.getValue();
                  this.map.remove(key);
                  //計算如今緩存的大小,而後減掉多餘的,內部調用的是sizeOf()方法
                  this.size -= this.safeSizeOf(key, value);
                  ++this.evictionCount;
              }
      
              //若是你想在在咱們的緩存中實現二級緩存,能夠實現此方法,源碼中是空方法。
              this.entryRemoved(true, key, value, (Object)null);
          }
      }

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索