• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            xiaoguozi's Blog
            Pay it forword - 我并不覺的自豪,我所嘗試的事情都失敗了······習慣原本生活的人不容易改變,就算現狀很糟,他們也很難改變,在過程中,他們還是放棄了······他們一放棄,大家就都是輸家······讓愛傳出去,很困難,也無法預料,人們需要更細心的觀察別人,要隨時注意才能保護別人,因為他們未必知道自己要什么·····

            另外一個問題就是加載速度,如果應用中圖片加載速度很慢的話,那么用戶同樣會等到崩潰。

            那么如何處理好圖片資源的獲取和管理呢?

            異步下載

            本地緩存

            異步下載

            大家都知道,在android應用中UI線程5秒沒響應的話就會拋出無響應異常,對于遠程獲取大的資源來說,這種異常還是很容易就會拋出來的,那么怎么避免這種問題的產生。在android中提供兩種方法來做這件事情:

            啟動一個新的線程來獲取資源,完成后通過Handler機制發送消息,并在UI線程中處理消息,從而達到在異步線程中獲取圖片,然后通過Handler Message來更新UI線程的過程。

            使用android中提供的AsyncTask來完成。

            具體的做法這里就不介紹了,查下API就可以了,或者是google、baidu下。這里主要來說本地緩存。

            本地緩存

            對于圖片資源來說,你不可能讓應用每次獲取的時候都重新到遠程去下載(ListView),這樣會浪費資源,但是你又不能讓所有圖片資源都放到內存 中去(雖然這樣加載會比較快),因為圖片資源往往會占用很大的內存空間,容易導致OOM。那么如果下載下來的圖片保存到SDCard中,下次直接從 SDCard上去獲取呢?這也是一種做法,我看了下,還是有不少應用采用這種方式的。采用LRU等一些算法可以保證sdcard被占用的空間只有一小部 分,這樣既保證了圖片的加載、節省了流量、又使SDCard的空間只占用了一小部分。另外一種做法是資源直接保存在內存中,然后設置過期時間和LRU規 則。

            sdcard保存:

            在sdcard上開辟一定的空間,需要先判斷sdcard上剩余空間是否足夠,如果足夠的話就可以開辟一些空間,比如10M

            當需要獲取圖片時,就先從sdcard上的目錄中去找,如果找到的話,使用該圖片,并更新圖片最后被使用的時間。如果找不到,通過URL去download

            去服務器端下載圖片,如果下載成功了,放入到sdcard上,并使用,如果失敗了,應該有重試機制。比如3次。

            下載成功后保存到sdcard上,需要先判斷10M空間是否已經用完,如果沒有用完就保存,如果空間不足就根據LRU規則刪除一些最近沒有被用戶的資源。

            關鍵代碼:

            保存圖片到SD卡上

            1. private void saveBmpToSd(Bitmap bm, Stringurl) {
            2. if (bm == null) {
            3. Log.w(TAG, " trying to savenull bitmap");
            4. return;
            5. }
            6. //判斷sdcard上的空間
            7. if (FREE_SD_SPACE_NEEDED_TO_CACHE >freeSpaceOnSd()) {
            8. Log.w(TAG, "Low free space onsd, do not cache");
            9. return;
            10. }
            11. String filename =convertUrlToFileName(url);
            12. String dir = getDirectory(filename);
            13. File file = new File(dir +"/" + filename);
            14. try {
            15. file.createNewFile();
            16. OutputStream outStream = newFileOutputStream(file);
            17. bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
            18. outStream.flush();
            19. outStream.close();
            20. Log.i(TAG, "Image saved tosd");
            21. } catch (FileNotFoundException e) {
            22. Log.w(TAG,"FileNotFoundException");
            23. } catch (IOException e) {
            24. Log.w(TAG,"IOException");
            25. }
            26. }

            計算sdcard上的空間:

            1. /**
            2. * 計算sdcard上的剩余空間
            3. * @return
            4. */
            5. private int freeSpaceOnSd() {
            6. StatFs stat = newStatFs(Environment.getExternalStorageDirectory() .getPath());
            7. double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
            8. return (int) sdFreeMB;
            9. }

            修改文件的最后修改時間

            1. /**
            2. * 修改文件的最后修改時間
            3. * @param dir
            4. * @param fileName
            5. */
            6. private void updateFileTime(String dir,String fileName) {
            7. File file = new File(dir,fileName);
            8. long newModifiedTime =System.currentTimeMillis();
            9. file.setLastModified(newModifiedTime);
            10. }

            本地緩存優化

            1. /**
            2. *計算存儲目錄下的文件大小,當文件總大小大于規定的CACHE_SIZE或者sdcard剩余空間小于FREE_SD_SPACE_NEEDED_TO_CACHE的規定
            3. * 那么刪除40%最近沒有被使用的文件
            4. * @param dirPath
            5. * @param filename
            6. */
            7. private void removeCache(String dirPath) {
            8. File dir = new File(dirPath);
            9. File[] files = dir.listFiles();
            10. if (files == null) {
            11. return;
            12. }
            13. int dirSize = 0;
            14. for (int i = 0; i < files.length;i++) {
            15. if(files[i].getName().contains(WHOLESALE_CONV)) {
            16. dirSize += files[i].length();
            17. }
            18. }
            19. if (dirSize > CACHE_SIZE * MB ||FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
            20. int removeFactor = (int) ((0.4 *files.length) + 1);
            21. Arrays.sort(files, newFileLastModifSort());
            22. Log.i(TAG, "Clear some expiredcache files ");
            23. for (int i = 0; i <removeFactor; i++) {
            24. if(files[i].getName().contains(WHOLESALE_CONV)) {
            25. files[i].delete();
            26. }
            27. }
            28. }
            29. }
            30. /**
            31. * 刪除過期文件
            32. * @param dirPath
            33. * @param filename
            34. */
            35. private void removeExpiredCache(StringdirPath, String filename) {
            36. File file = new File(dirPath,filename);
            37. if (System.currentTimeMillis() -file.lastModified() > mTimeDiff) {
            38. Log.i(TAG, "Clear some expiredcache files ");
            39. file.delete();
            40. }
            41. }

            文件使用時間排序

            1. /**
            2. * TODO 根據文件的最后修改時間進行排序 *
            3. */
            4. classFileLastModifSort implements Comparator<File>{
            5. public int compare(File arg0, File arg1) {
            6. if (arg0.lastModified() >arg1.lastModified()) {
            7. return 1;
            8. } else if (arg0.lastModified() ==arg1.lastModified()) {
            9. return 0;
            10. } else {
            11. return -1;
            12. }
            13. }
            14. }

            內存保存:

            在內存中保存的話,只能保存一定的量,而不能一直往里面放,需要設置數據的過期時間、LRU等算法。這里有一個方法是把常用的數據放到一個緩存中 (A),不常用的放到另外一個緩存中(B)。當要獲取數據時先從A中去獲取,如果A中不存在那么再去B中獲取。B中的數據主要是A中LRU出來的數據,這 里的內存回收主要針對B內存,從而保持A中的數據可以有效的被命中。

            先定義A緩存:

            1. private final HashMap<String, Bitmap>mHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY/ 2, 0.75f, true) {
            2. @Override
            3. protected booleanremoveEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
            4. if (size() >HARD_CACHE_CAPACITY) {
            5. //當map的size大于30時,把最近不常用的key放到mSoftBitmapCache中,從而保證mHardBitmapCache的效率
            6. mSoftBitmapCache.put(eldest.getKey(), newSoftReference<Bitmap>(eldest.getValue()));
            7. return true;
            8. } else
            9. return false;
            10. }
            11. };

            再定于B緩存:

            1. /**
            2. *當mHardBitmapCache的key大于30的時候,會根據LRU算法把最近沒有被使用的key放入到這個緩存中。
            3. *Bitmap使用了SoftReference,當內存空間不足時,此cache中的bitmap會被垃圾回收掉
            4. */
            5. private final staticConcurrentHashMap<String, SoftReference<Bitmap>> mSoftBitmapCache =new ConcurrentHashMap<String,SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

            從緩存中獲取數據:

            1. /**
            2. * 從緩存中獲取圖片
            3. */
            4. private Bitmap getBitmapFromCache(Stringurl) {
            5. // 先從mHardBitmapCache緩存中獲取
            6. synchronized (mHardBitmapCache) {
            7. final Bitmap bitmap =mHardBitmapCache.get(url);
            8. if (bitmap != null) {
            9. //如果找到的話,把元素移到linkedhashmap的最前面,從而保證在LRU算法中是最后被刪除
            10. mHardBitmapCache.remove(url);
            11. mHardBitmapCache.put(url,bitmap);
            12. return bitmap;
            13. }
            14. }
            15. //如果mHardBitmapCache中找不到,到mSoftBitmapCache中找
            16. SoftReference<Bitmap>bitmapReference = mSoftBitmapCache.get(url);
            17. if (bitmapReference != null) {
            18. final Bitmap bitmap =bitmapReference.get();
            19. if (bitmap != null) {
            20. return bitmap;
            21. } else {
            22. mSoftBitmapCache.remove(url);
            23. }
            24. }
            25. return null;
            26. }

            如果緩存中不存在,那么就只能去服務器端去下載:

            1. /**
            2. * 異步下載圖片
            3. */
            4. class ImageDownloaderTask extendsAsyncTask<String, Void, Bitmap> {
            5. private static final int IO_BUFFER_SIZE= 4 * 1024;
            6. private String url;
            7. private finalWeakReference<ImageView> imageViewReference;
            8. public ImageDownloaderTask(ImageViewimageView) {
            9. imageViewReference = newWeakReference<ImageView>(imageView);
            10. }
            11. @Override
            12. protected BitmapdoInBackground(String... params) {
            13. final AndroidHttpClient client =AndroidHttpClient.newInstance("Android");
            14. url = params[0];
            15. final HttpGet getRequest = newHttpGet(url);
            16. try {
            17. HttpResponse response =client.execute(getRequest);
            18. final int statusCode =response.getStatusLine().getStatusCode();
            19. if (statusCode !=HttpStatus.SC_OK) {
            20. Log.w(TAG, "從" +url + "中下載圖片時出錯!,錯誤碼:" + statusCode);
            21. return null;
            22. }
            23. final HttpEntity entity =response.getEntity();
            24. if (entity != null) {
            25. InputStream inputStream =null;
            26. OutputStream outputStream =null;
            27. try {
            28. inputStream =entity.getContent();
            29. finalByteArrayOutputStream dataStream = new ByteArrayOutputStream();
            30. outputStream = newBufferedOutputStream(dataStream, IO_BUFFER_SIZE);
            31. copy(inputStream,outputStream);
            32. outputStream.flush();
            33. final byte[] data =dataStream.toByteArray();
            34. final Bitmap bitmap =BitmapFactory.decodeByteArray(data, 0, data.length);
            35. return bitmap;
            36. } finally {
            37. if (inputStream !=null) {
            38. inputStream.close();
            39. }
            40. if (outputStream !=null) {
            41. outputStream.close();
            42. }
            43. entity.consumeContent();
            44. }
            45. }
            46. } catch (IOException e) {
            47. getRequest.abort();
            48. Log.w(TAG, "I/O errorwhile retrieving bitmap from " + url, e);
            49. } catch (IllegalStateException e) {
            50. getRequest.abort();
            51. Log.w(TAG, "Incorrect URL:" + url);
            52. } catch (Exception e) {
            53. getRequest.abort();
            54. Log.w(TAG, "Error whileretrieving bitmap from " + url, e);
            55. } finally {
            56. if (client != null) {
            57. client.close();
            58. }
            59. }
            60. return null;
            61. }

            這是兩種做法,還有一些應用在下載的時候使用了線程池和消息隊列MQ,對于圖片下載的效率要更好一些。有興趣的同學可以看下。

            總結

            對于遠程圖片等相對比較大的資源一定要在異步線程中去獲取本地做緩存

            posted on 2012-04-16 11:43 小果子 閱讀(1291) 評論(2)  編輯 收藏 引用 所屬分類: Android & Ios

            FeedBack:
            # re: Android遠程圖片獲取和本地緩存
            2012-05-14 17:01 | RKO
            考慮的真是太周全了?。。№敚?nbsp; 回復  更多評論
              
            # re: Android遠程圖片獲取和本地緩存
            2012-11-27 11:36 | cx
            有可運行的demo嗎?  回復  更多評論
              
            久久精品国产99久久久| 精品久久久久久亚洲精品| 国内精品伊人久久久久av一坑| 久久精品国产一区二区| 久久美女人爽女人爽| 久久91综合国产91久久精品| 无码AV波多野结衣久久| 亚洲美日韩Av中文字幕无码久久久妻妇| 中文字幕亚洲综合久久2| 国产99精品久久| 99精品国产在热久久| 91精品国产综合久久久久久| 国产美女久久久| 国产69精品久久久久99| 久久99精品国产99久久6| 国产福利电影一区二区三区久久老子无码午夜伦不 | 久久99国产精品99久久| 99久久99这里只有免费费精品| MM131亚洲国产美女久久| 久久r热这里有精品视频| 日本三级久久网| 久久久这里有精品中文字幕| 日本欧美国产精品第一页久久| 中文字幕精品久久久久人妻| 国产精品99久久久久久宅男小说| 久久久亚洲AV波多野结衣| 欧美喷潮久久久XXXXx| 国产精品久久网| 久久久久亚洲爆乳少妇无 | 国产成人精品综合久久久| 一本久久a久久精品vr综合| 久久这里只有精品18| 人人狠狠综合久久亚洲88| 亚洲国产精品无码久久久久久曰 | 久久影院综合精品| 欧美一区二区精品久久| 一级做a爰片久久毛片看看| 久久九九精品99国产精品| 国产AV影片久久久久久| 亚洲乱码日产精品a级毛片久久 | 国产激情久久久久影院老熟女|