亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

【Android Developers Training】 58. 緩存位圖

系統(tǒng) 2160 0

注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術(shù)一般,由于喜愛(ài)安卓而產(chǎn)生了翻譯的念頭,純屬個(gè)人興趣愛(ài)好。

原文鏈接: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html


向你的應(yīng)用中加載一個(gè)單一的位圖是很直接的行為,然而當(dāng)你需要一次性加載一組圖像的大集合時(shí),事情會(huì)變得更加復(fù)雜。在很多情況下(比如對(duì)于 ListView GridView 或者 ViewPager ),屏幕上顯示的圖片以及會(huì)因加載動(dòng)作而進(jìn)入屏幕的圖片,這兩者的總數(shù)加起來(lái)是無(wú)法限制的。

通過(guò)對(duì)移除屏幕區(qū)域的子View進(jìn)行回收,可以讓這類組件內(nèi)存使用降低下來(lái)。垃圾回收器也會(huì)對(duì)那些假定你將不再需要的引用對(duì)象進(jìn)行回收和釋放。這些措施都很好,但是為了保持流暢地和快速地加載UI,你會(huì)希望避免多次連續(xù)地處理這些圖片,當(dāng)它們回到屏幕區(qū)域中來(lái)時(shí)。一個(gè)存儲(chǔ)或磁盤緩存可以在這方面提供幫助,它可以讓組件迅速的重新加載處理過(guò)的圖片。

這節(jié)課將會(huì)教你使用一個(gè)存儲(chǔ)和磁盤緩存,來(lái)提升你的UI加載多個(gè)圖片時(shí)的響應(yīng)和流暢性。


一). 使用一個(gè)內(nèi)存緩存

一個(gè)內(nèi)存緩存提供了快速訪問(wèn)位圖的方法,但它的代價(jià)是需要消耗掉珍貴的應(yīng)用內(nèi)存。 LruCache 類(在 Support Library 也有,可以支持到API Level 4及以上的平臺(tái))對(duì)于緩存圖片來(lái)說(shuō)尤其適合,它能將最近引用的對(duì)象存儲(chǔ)在一個(gè)基于強(qiáng)引用的 LinkedHashMap 中,并且在緩存超出它的特定大小后,將最近最遲被引用的對(duì)象去除。

Note:

在過(guò)去,一個(gè)流行的內(nèi)存緩存實(shí)現(xiàn)是 SoftReference 或者 WeakReference 的位圖緩存,然而,這并不是推薦的實(shí)現(xiàn)方法。從Android 2.3(API Level 9)開(kāi)始,垃圾回收器對(duì)于軟引用和弱引用的回收變得更加地激進(jìn),從而使得它們的效用正在下降。從Android 3.0(API Level 11)開(kāi)始,存儲(chǔ)于本機(jī)內(nèi)存的位圖數(shù)據(jù)并不是以一個(gè)可預(yù)測(cè)的形式釋放的,這就有潛在的可能性導(dǎo)致一個(gè)應(yīng)用超出它的內(nèi)存限制進(jìn)而崩潰。

為了為一個(gè) LruCache 選擇合適的大小,一些因素需要考量,例如:

  • 你的activity或應(yīng)用剩余的存儲(chǔ)壓力是如何的?
  • 同一時(shí)間有多少應(yīng)用顯示在屏幕上?有多少需要準(zhǔn)備就緒顯示到屏幕上?
  • 設(shè)備的屏幕的尺寸和密度的大小是多少?一個(gè)極高密度的屏幕(xhdpi)的設(shè)備(比如 Galaxy Nexus )可能相對(duì)于其他比如hdpi的設(shè)備(比如 Nexus S )需要更大的緩存來(lái)容納同樣數(shù)量的照片。
  • 位圖文件的尺寸和屬性是怎樣的,需要消耗多少大的內(nèi)存空間?
  • 圖片被訪問(wèn)的頻率高不高?有沒(méi)有一些圖片被訪問(wèn)你的頻率比其它的要高?如果有,也許你會(huì)期望讓這些項(xiàng)目一直保留在內(nèi)存或者為不同被訪問(wèn)頻率的圖片設(shè)置多組 LruCache 對(duì)象。
  • 能否做到數(shù)量和質(zhì)量間的平衡?有些時(shí)候存儲(chǔ)大量低質(zhì)量的圖片時(shí)很有用的,而將更高質(zhì)量的圖片加載任務(wù)放在后臺(tái)執(zhí)行。

沒(méi)有什么特定的大小或者公式能夠適合所有的應(yīng)用,你應(yīng)該自己分析并決定你的用法和解決方案。一個(gè)過(guò)小的緩存會(huì)導(dǎo)致大量無(wú)益處的執(zhí)行操作,而太大的緩存會(huì)導(dǎo)致 java.lang.OutOfMemory 異常,或者讓你剩下的應(yīng)用只有有限的存儲(chǔ)來(lái)工作。

下面是一個(gè) LruCache 配置的樣例代碼:

      
        private
      
       LruCache<String, Bitmap>
      
         mMemoryCache;



@Override


      
      
        protected
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

    ...

    
      
      
        //
      
      
         Get max available VM memory, exceeding this amount will throw an

    
      
      
        //
      
      
         OutOfMemory exception. Stored in kilobytes as LruCache takes an

    
      
      
        //
      
      
         int in its constructor.
      
      
        final
      
      
        int
      
       maxMemory = (
      
        int
      
      ) (Runtime.getRuntime().maxMemory() / 1024
      
        );



    
      
      
        //
      
      
         Use 1/8th of the available memory for this memory cache.
      
      
        final
      
      
        int
      
       cacheSize = maxMemory / 8
      
        ;



    mMemoryCache 
      
      = 
      
        new
      
       LruCache<String, Bitmap>
      
        (cacheSize) {

        @Override

        
      
      
        protected
      
      
        int
      
      
         sizeOf(String key, Bitmap bitmap) {

            
      
      
        //
      
      
         The cache size will be measured in kilobytes rather than

            
      
      
        //
      
      
         number of items.
      
      
        return
      
       bitmap.getByteCount() / 1024
      
        ;

        }

    };

    ...

}




      
      
        public
      
      
        void
      
      
         addBitmapToMemoryCache(String key, Bitmap bitmap) {

    
      
      
        if
      
       (getBitmapFromMemCache(key) == 
      
        null
      
      
        ) {

        mMemoryCache.put(key, bitmap);

    }

}




      
      
        public
      
      
         Bitmap getBitmapFromMemCache(String key) {

    
      
      
        return
      
      
         mMemoryCache.get(key);

}
      
    

Note:

在這個(gè)例子中,八分之一的應(yīng)用內(nèi)存被分配給了我們的緩存。在一個(gè)標(biāo)準(zhǔn)或hdpi的設(shè)備上,這大約為4MB左右(32/8)。一個(gè)全屏的 GridView ,在一個(gè)分辨率為800x480的設(shè)備上,充滿圖片之后,會(huì)使用掉大約1.5MB( 800*480*4字節(jié) ),所以這個(gè)緩存至少大約能放下2.5個(gè)頁(yè)面數(shù)量的圖片在內(nèi)存中。

當(dāng)把一個(gè)圖片加載到 ImageView 時(shí), LruCache 會(huì)先進(jìn)行檢查。如果找到了一個(gè)對(duì)應(yīng)的條目,那么它將會(huì)立即用來(lái)更新 ImageView ,否則的話一個(gè)后臺(tái)線程會(huì)啟動(dòng)并處理該圖像:

      
        public
      
      
        void
      
       loadBitmap(
      
        int
      
      
         resId, ImageView imageView) {

    
      
      
        final
      
       String imageKey =
      
         String.valueOf(resId);



    
      
      
        final
      
       Bitmap bitmap =
      
         getBitmapFromMemCache(imageKey);

    
      
      
        if
      
       (bitmap != 
      
        null
      
      
        ) {

        mImageView.setImageBitmap(bitmap);

    } 
      
      
        else
      
      
         {

        mImageView.setImageResource(R.drawable.image_placeholder);

        BitmapWorkerTask task 
      
      = 
      
        new
      
      
         BitmapWorkerTask(mImageView);

        task.execute(resId);

    }

}
      
    

BitmapWorkerTask 也需要更新,并將相應(yīng)字段添加到內(nèi)存緩存中:

      
        class
      
       BitmapWorkerTask 
      
        extends
      
       AsyncTask<Integer, Void, Bitmap>
      
         {

    ...

    
      
      
        //
      
      
         Decode image in background.
      
      
            @Override

    
      
      
        protected
      
      
         Bitmap doInBackground(Integer... params) {

        
      
      
        final
      
       Bitmap bitmap =
      
         decodeSampledBitmapFromResource(

                getResources(), params[
      
      0], 100, 100
      
        ));

        addBitmapToMemoryCache(String.valueOf(params[
      
      0
      
        ]), bitmap);

        
      
      
        return
      
      
         bitmap;

    }

    ...

}
      
    

二). 使用磁盤緩存

一個(gè)內(nèi)存緩存對(duì)于加速訪問(wèn)最近查看的位圖是很有效果的,然而你不能依賴于它,因?yàn)闊o(wú)法做到所有圖片都放置在該緩存中。如 GridView 這樣的組件其較大的數(shù)據(jù)集可以迅速填充內(nèi)存緩存。同時(shí),你的應(yīng)用可能會(huì)被另一個(gè)事務(wù)打斷,如一個(gè)來(lái)電,此時(shí)在后臺(tái)中,它可能會(huì)被殺掉,這樣的話內(nèi)存緩存就被銷毀了。一旦這個(gè)用戶恢復(fù)了,你的應(yīng)用不得不重新處理這些圖片。

一個(gè)磁盤緩存可以在這種情況下發(fā)揮效用,它能保持處理過(guò)的位圖文件,并減少在內(nèi)存緩存中不再可以獲得的加載時(shí)間。當(dāng)然,從磁盤獲取圖片比從內(nèi)存獲取圖片要慢,由于磁盤讀寫的速度有很多不確定性,故應(yīng)該在后臺(tái)線程中執(zhí)行。

Note:

一個(gè) ContentProvider 是一個(gè)比較合適的存儲(chǔ)緩存圖片的地方,對(duì)于那些訪問(wèn)頻率較高的圖片來(lái)說(shuō),例如在圖庫(kù)的應(yīng)用中。

下面的代碼使用了 DiskLruCache 的實(shí)現(xiàn),它來(lái)自于 Android source 。并且添加到內(nèi)存緩存的代碼中,更新其功能:

      
        private
      
      
         DiskLruCache mDiskLruCache;


      
      
        private
      
      
        final
      
       Object mDiskCacheLock = 
      
        new
      
      
         Object();


      
      
        private
      
      
        boolean
      
       mDiskCacheStarting = 
      
        true
      
      
        ;


      
      
        private
      
      
        static
      
      
        final
      
      
        int
      
       DISK_CACHE_SIZE = 1024 * 1024 * 10; 
      
        //
      
      
         10MB
      
      
        private
      
      
        static
      
      
        final
      
       String DISK_CACHE_SUBDIR = "thumbnails"
      
        ;



@Override


      
      
        protected
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

    ...

    
      
      
        //
      
      
         Initialize memory cache
      
      
            ...

    
      
      
        //
      
      
         Initialize disk cache on background thread
      
      

    File cacheDir = getDiskCacheDir(
      
        this
      
      
        , DISK_CACHE_SUBDIR);

    
      
      
        new
      
      
         InitDiskCacheTask().execute(cacheDir);

    ...

}




      
      
        class
      
       InitDiskCacheTask 
      
        extends
      
       AsyncTask<File, Void, Void>
      
         {

    @Override

    
      
      
        protected
      
      
         Void doInBackground(File... params) {

        
      
      
        synchronized
      
      
         (mDiskCacheLock) {

            File cacheDir 
      
      = params[0
      
        ];

            mDiskLruCache 
      
      =
      
         DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);

            mDiskCacheStarting 
      
      = 
      
        false
      
      ; 
      
        //
      
      
         Finished initialization
      
      

            mDiskCacheLock.notifyAll(); 
      
        //
      
      
         Wake any waiting threads
      
      
                }

        
      
      
        return
      
      
        null
      
      
        ;

    }

}




      
      
        class
      
       BitmapWorkerTask 
      
        extends
      
       AsyncTask<Integer, Void, Bitmap>
      
         {

    ...

    
      
      
        //
      
      
         Decode image in background.
      
      
            @Override

    
      
      
        protected
      
      
         Bitmap doInBackground(Integer... params) {

        
      
      
        final
      
       String imageKey = String.valueOf(params[0
      
        ]);



        
      
      
        //
      
      
         Check disk cache in background thread
      
      

        Bitmap bitmap =
      
         getBitmapFromDiskCache(imageKey);



        
      
      
        if
      
       (bitmap == 
      
        null
      
      ) { 
      
        //
      
      
         Not found in disk cache

            
      
      
        //
      
      
         Process as normal
      
      
        final
      
       Bitmap bitmap =
      
         decodeSampledBitmapFromResource(

                    getResources(), params[
      
      0], 100, 100
      
        ));

        }



        
      
      
        //
      
      
         Add final bitmap to caches
      
      
                addBitmapToCache(imageKey, bitmap);



        
      
      
        return
      
      
         bitmap;

    }

    ...

}




      
      
        public
      
      
        void
      
      
         addBitmapToCache(String key, Bitmap bitmap) {

    
      
      
        //
      
      
         Add to memory cache as before
      
      
        if
      
       (getBitmapFromMemCache(key) == 
      
        null
      
      
        ) {

        mMemoryCache.put(key, bitmap);

    }



    
      
      
        //
      
      
         Also add to disk cache
      
      
        synchronized
      
      
         (mDiskCacheLock) {

        
      
      
        if
      
       (mDiskLruCache != 
      
        null
      
       && mDiskLruCache.get(key) == 
      
        null
      
      
        ) {

            mDiskLruCache.put(key, bitmap);

        }

    }

}




      
      
        public
      
      
         Bitmap getBitmapFromDiskCache(String key) {

    
      
      
        synchronized
      
      
         (mDiskCacheLock) {

        
      
      
        //
      
      
         Wait while disk cache is started from background thread
      
      
        while
      
      
         (mDiskCacheStarting) {

            
      
      
        try
      
      
         {

                mDiskCacheLock.wait();

            } 
      
      
        catch
      
      
         (InterruptedException e) {}

        }

        
      
      
        if
      
       (mDiskLruCache != 
      
        null
      
      
        ) {

            
      
      
        return
      
      
         mDiskLruCache.get(key);

        }

    }

    
      
      
        return
      
      
        null
      
      
        ;

}




      
      
        //
      
      
         Creates a unique subdirectory of the designated app cache directory. Tries to use external


      
      
        //
      
      
         but if not mounted, falls back on internal storage.
      
      
        public
      
      
        static
      
      
         File getDiskCacheDir(Context context, String uniqueName) {

    
      
      
        //
      
      
         Check if media is mounted or storage is built-in, if so, try and use external cache dir

    
      
      
        //
      
      
         otherwise use internal cache dir
      
      
        final
      
       String cachePath =
      
        

            Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 
      
      ||

                    !isExternalStorageRemovable() ?
      
         getExternalCacheDir(context).getPath() :

                            context.getCacheDir().getPath();



    
      
      
        return
      
      
        new
      
       File(cachePath + File.separator +
      
         uniqueName);

}
      
    

Note:

因?yàn)槌跏蓟疟P緩存也需要磁盤操作所以它也不能再主線程中執(zhí)行。然而,這其實(shí)意味著緩存有可能在還未初始化的時(shí)候就被訪問(wèn)了。為了解決這個(gè)問(wèn)題,在上面的代碼實(shí)現(xiàn)中,一個(gè)信號(hào)量(lock)保證了應(yīng)用會(huì)在初始化完成之后才去讀取緩存。

雖然內(nèi)存緩存在UI線程中檢查,磁盤緩存是在后臺(tái)線程中檢查。磁盤操作不應(yīng)該發(fā)生在UI線程中執(zhí)行。當(dāng)圖片處理完成了,最后位圖將會(huì)同時(shí)添加到內(nèi)存和磁盤緩存中,以備將來(lái)使用。


三). 處理配置變更

運(yùn)行時(shí)的配置變更,如屏幕方向變化,會(huì)導(dǎo)致Android銷毀當(dāng)前activity,并以新的配置重啟activity(可以閱讀: Handling Runtime Changes )。你一定希望避免重復(fù)處理圖像,這樣的話用戶就能在配置改變時(shí),擁有平滑快速地使用體驗(yàn)。

幸運(yùn)的是,你在之前的章節(jié)中,已經(jīng)擁有了一個(gè)很出色的圖片內(nèi)存緩存了。這個(gè)緩存可以通過(guò)使用一個(gè) Fragment (該 Fragment 通過(guò)調(diào)用 setRetainInstance(true) 將其自身保留),傳遞給新的activity實(shí)例。在activity重新創(chuàng)建之后,這個(gè)保留的 Fragment 就完成了重新依附( reattach ),同時(shí)你獲得了現(xiàn)有緩存對(duì)象的訪問(wèn),允許圖片快速提取并填充到 ImageView 對(duì)象中。

下面是一個(gè)使用 Fragment ,在配置變更發(fā)生時(shí)保留 LruCache 對(duì)象的例子:

      
        private
      
       LruCache<String, Bitmap>
      
         mMemoryCache;



@Override


      
      
        protected
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

    ...

    RetainFragment retainFragment 
      
      =
      
        

            RetainFragment.findOrCreateRetainFragment(getFragmentManager());

    mMemoryCache 
      
      =
      
         retainFragment.mRetainedCache;

    
      
      
        if
      
       (mMemoryCache == 
      
        null
      
      
        ) {

        mMemoryCache 
      
      = 
      
        new
      
       LruCache<String, Bitmap>
      
        (cacheSize) {

            ... 
      
      
        //
      
      
         Initialize cache here as usual
      
      
                }

        retainFragment.mRetainedCache 
      
      =
      
         mMemoryCache;

    }

    ...

}




      
      
        class
      
       RetainFragment 
      
        extends
      
      
         Fragment {

    
      
      
        private
      
      
        static
      
      
        final
      
       String TAG = "RetainFragment"
      
        ;

    
      
      
        public
      
       LruCache<String, Bitmap>
      
         mRetainedCache;



    
      
      
        public
      
      
         RetainFragment() {}



    
      
      
        public
      
      
        static
      
      
         RetainFragment findOrCreateRetainFragment(FragmentManager fm) {

        RetainFragment fragment 
      
      =
      
         (RetainFragment) fm.findFragmentByTag(TAG);

        
      
      
        if
      
       (fragment == 
      
        null
      
      
        ) {

            fragment 
      
      = 
      
        new
      
      
         RetainFragment();

            fm.beginTransaction().add(fragment, TAG).commit();

        }

        
      
      
        return
      
      
         fragment;

    }



    @Override

    
      
      
        public
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

        
      
      
        super
      
      
        .onCreate(savedInstanceState);

        setRetainInstance(
      
      
        true
      
      
        );

    }

}
      
    

要測(cè)試這段代碼,嘗試分別在保留 Fragment 和不保留 Fragment 的情況下旋轉(zhuǎn)設(shè)備。你應(yīng)該能注意到當(dāng)保留了緩存時(shí),圖片填充到activity時(shí)幾乎沒(méi)有延遲。那些在內(nèi)存緩存中找不到的圖片一般都會(huì)在磁盤緩存中找到,如果找不到,這些圖片就會(huì)像平常一樣處理。

【Android Developers Training】 58. 緩存位圖


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 天天操视频 夜夜 | 欧美成人禁片在线www | 久久综合九色综合国产 | 四虎永久免费网站免费观看 | 成年女人18毛片毛片免费 | 99久久国产综合精品成人影院 | 青青青免费高清视频在线 | 在线观看麻豆精品国产不卡 | 免费在线中文字幕 | 多色视频 | 九色av99久久| 国产青草 | 国产欧美亚洲精品第二区首页 | 成人深夜视频在线观看 | 天天干天天拍天天射天天添天天爱 | 欧美 日韩 国产在线 | 欧美aaa性bbb毛片 | 日日操美女 | 日韩在线欧美 | 野外一级毛片 | 日韩欧美在线观看成人 | 亚洲小说春色综合另类网蜜桃 | 亚洲欧美成人永久第一网站 | 日本aaaaa级毛片 | 亚洲欧美另类在线观看 | 国产高清在线精品 | 久久视屏这里只有精品6国产 | 国产成人精品免费视频 | 欧美日韩国产亚洲一区二区 | 成人亚洲性情网站www在线观看 | 999精品国产 | 精品特级一级毛片免费观看 | 中文字幕在线视频不卡 | 国产亚洲欧美日韩国产片 | 国产精品岛国久久久久 | 在线观看免费黄色小视频 | 国产一区二区精品久 | 亚洲国产一区二区三区最新 | 99视频在线 | 亚洲国产成人久久综合一 | 中文一级毛片 |