內存緩存-LruCache 與 壓縮圖片


來源於郭大神:http://blog.csdn.net/guolin_blog/article/details/9316683

LruCache這個類非常適合用來緩存圖片,它的主要算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,並且把最近最少使用的對象在緩存值達到預設定值之前從內存中移除.與它對應的是DiskLruCache(磁盤緩存)

我們在編寫Android程序的時候經常要用到許多圖片,不同圖片總是會有不同的形狀、不同的大小,但在大多數情況下,這些圖片都會大於我們程序所需要的大小。比如說系統圖片庫里展示的圖片大都是用手機攝像頭拍出來的,這些圖片的分辨率會比我們手機屏幕的分辨率高得多。大家應該知道,我們編寫的應用程序都是有一定內存限制的,程序占用了過高的內存就容易出現OOM(OutOfMemory)異常。我們可以通過下面的代碼看出每個應用程序最高可用內存是多少

int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
System.out.println("Max memory is " + maxMemory + "KB");//模擬器輸出:16384kB

因此在展示高分辨率圖片的時候,最好先將圖片進行壓縮。壓縮后的圖片大小應該和用來展示它的控件大小相近,在一個很小的ImageView上顯示一張超大的圖片不會帶來任何視覺上的好處,但卻會占用我們相當多寶貴的內存,而且在性能上還可能會帶來負面影響。下面我們就來看一看,如何對一張大圖片進行適當的壓縮,讓它能夠以最佳大小顯示的同時,還能防止OOM的出現。

BitmapFactory這個類提供了多個解析方法(decodeByteArray, decodeFile, decodeResource等)用於創建Bitmap對象,我們應該根據圖片的來源選擇合適的方法。比如SD卡中的圖片可以使用decodeFile方法,網絡上的圖片可以使用decodeStream方法,資源文件中的圖片可以使用decodeResource方法。這些方法會嘗試為已經構建的bitmap分配內存,這時就會很容易導致OOM出現。為此每一種解析方法都提供了一個可選的BitmapFactory.Options參數,將這個參數的inJustDecodeBounds屬性設置為true就可以讓解析方法禁止為bitmap分配內存,返回值也不再是一個Bitmap對象,而是null。雖然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。這個技巧讓我們可以在加載圖片之前就獲取到圖片的長寬值和MIME類型,從而根據情況對圖片進行壓縮。如下代碼所示:

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inJustDecodeBounds = true;//解析方法禁止為bitmap分配內存
 BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
 int imageHeight = options.outHeight;
 int imageWidth = options.outWidth;
 String imageType = options.outMimeType;//類型值(不同數字不同的含義)

並沒有一個指定的緩存大小可以滿足所有的應用程序,這是由你決定的。你應該去分析程序內存的使用情況,然后制定出一個合適的解決方案。一個太小的緩存空間,有可能造成圖片頻繁地被釋放和重新加載,這並沒有好處。而一個太大的緩存空間,則有可能還是會引起 java.lang.OutOfMemory 的異常。

解決OOM異常的方法1,壓縮圖片:

/**
內存緩存:程序占用了過高的內存就容易出現OOM(OutOfMemory)異常,解決OOM異常的兩種方法:
 1.處理圖片的大小,進行必要的壓縮,寬高等比例壓縮
 2.使用圖片還催技術
 */
//方法1
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024 /1024);//獲取應用的最大可用內存
        Log.i("TAG", "該應用的最大可用內存為:" + maxMemory + "M");//genymotion 64M
        ImageView img= (ImageView) findViewById(R.id.imageView);
        Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.a, 100, 100);
        img.setImageBitmap(bitmap);
    }

    /**
     * 解碼imageVIew為想要的尺寸,
     * 在一個很小的ImageView上顯示一張超大的圖片不會帶來任何視覺上的好處,
     * 但卻會占用我們相當多寶貴的內存,而且在性能上還可能會帶來負面影響。
     * @param res  資源對象
     * @param resId  圖片的id.這里使用的是資源id,對於流,file的圖片也是可以的
     * @param reqWidth  需要的圖片寬度
     * @param reqHeight 需要的圖片高度
     * @return bitmap
     */
    private Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inJustDecodeBounds=true;//只解碼圖片大小
        BitmapFactory.decodeResource(res, resId, options);//這里得到的bitmap為null
        options.inSampleSize=calculateInSampleSize(options, reqWidth, reqHeight);
        //使用獲取的比例值再次解析圖片,設置為false.返回的bitmap才不會為空
        options.inJustDecodeBounds=false;
        return BitmapFactory.decodeResource(res,resId,options);
    }

    /**
     * 根據圖片的實際尺寸與需要的尺寸進行比較計算
     * @param options 包含實際圖片的寬高
     * @param reqWidth 請求的圖片寬度
     * @param reqHeight 需要的圖片高度
     * @return 長寬等比例壓縮這個值,
     * 比如我們有一張2048*1536像素的圖片,將inSampleSize的值設置為4,就可以把這張圖片壓縮成512*384像素
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        //獲取來源圖片的寬高
        int height=options.outHeight;
        int width=options.outWidth;
        int inSampleSize=1;//默認為1 不壓縮圖片
        if(height>reqHeight || width>reqWidth){
            //計算出實際寬高和目標寬高的比率  round代表四舍五入
            int heightRatio=Math.round((float) height / (float) reqHeight);
            int widthRatio=Math.round((float) width / (float) reqWidth);
            Log.i("tag","實際/目標,高:"+heightRatio+"  寬:"+widthRatio);
            // 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高,一定都會大於等於目標的寬和高
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }
}

說明:這里壓縮的圖片結果是大於或者等於目標的尺寸,圖方便這里使用的圖片為工程里的資源圖片,如果是流,或者的文件,改下個別方法就行

下面是一個使用 LruCache 來緩存圖片的例子:

 主Activity,MainActivity:

/**
內存緩存:程序占用了過高的內存就容易出現OOM(OutOfMemory)異常,解決OOM異常的兩種方法:
 1.處理圖片的大小,進行必要的壓縮,寬高等比例壓縮
 2.使用圖片還催技術
 */
//方法2
public class MainActivity extends AppCompatActivity {
    //圖片地址
    String[] imgs=new String[]{
        "http://a.hiphotos.baidu.com/image/h%3D360/sign=c429bad21dd8bc3ed90800ccb28aa6c8/e7cd7b899e510fb3a78c787fdd33c895d0430c44.jpg",
            "http://f.hiphotos.baidu.com/image/h%3D360/sign=5ec76a78d8c451dae9f60aed86fd52a5/dbb44aed2e738bd4f60e4017a48b87d6277ff9ed.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=51650b06b83eb1355bc7b1bd961fa8cb/7a899e510fb30f2493c8cbedcc95d143ac4b0389.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=2c82459da31ea8d395227202a70b30cf/43a7d933c895d143b233160576f082025aaf074a.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=ea96ce4c0e7b020813c939e752d8f25f/14ce36d3d539b600be63e95eed50352ac75cb7ae.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=e6bd967ff203738dc14a0a248319b073/08f790529822720e534f0b257fcb0a46f31fab68.jpg",
            "http://g.hiphotos.baidu.com/image/h%3D360/sign=a3ab0d059758d109dbe3afb4e159ccd0/b7fd5266d0160924d5e7cc60d10735fae7cd348e.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=78d136c1d5a20cf45990f8d946084b0c/9d82d158ccbf6c81a1599daeb93eb13533fa400d.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=8c7b592db8a1cd111ab674268913c8b0/b219ebc4b74543a9f68147d11c178a82b9011410.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=86a67aee8fb1cb1321693a15ed5456da/1ad5ad6eddc451da33c51deab4fd5266d01632fb.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=e7b5381941a7d933a0a8e2759d4bd194/6f061d950a7b020893f23dec61d9f2d3572cc880.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=02a96a298418367ab28979db1e728b68/0b46f21fbe096b6344dcf9500e338744eaf8aced.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D200/sign=a8af76d39f16fdfac76cc1ee848e8cea/738b4710b912c8fccc8e7b35fe039245d78821cc.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=5067d3011d30e924d0a49a377c096e66/242dd42a2834349b5454a08ccbea15ce36d3be6d.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=aec7700940a7d933a0a8e2759d4ad194/6f061d950a7b0208da8075fc60d9f2d3572cc866.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=11705f58a918972bbc3a06ccd6cc7b9d/267f9e2f07082838de56bf58ba99a9014c08f127.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=0d5bbf79952bd4075dc7d5fb4b889e9c/9f2f070828381f30ed5bacf8ab014c086e06f024.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D200/sign=a45f36fa013b5bb5a1d727fe06d2d523/cf1b9d16fdfaaf517fb5b6ee8e5494eef01f7a08.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D200/sign=f541a648b0de9c82b965fe8f5c8180d2/0824ab18972bd40752e3918c78899e510fb3099e.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=349a818ff21f3a2945c8d3c8a924bce3/fd039245d688d43f3f5ab1a37f1ed21b0ff43beb.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=8dbd4f2003e9390149028b384bed54f9/a9d3fd1f4134970ac12ae72f97cad1c8a7865d51.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=da2b333738f33a87816d061cf65c1018/8d5494eef01f3a2922e765c99b25bc315c607c8d.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=9cd86f8b074f78f09f0b9cf549300a83/63d0f703918fa0ec6fcf95d2229759ee3c6ddbe1.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=71dbf9ca41166d222777139276230945/5882b2b7d0a20cf4158d80de74094b36acaf9995.jpg",
            "http://g.hiphotos.baidu.com/image/h%3D360/sign=58a05fe0b11c8701c9b6b4e0177e9e6e/0d338744ebf81a4c27dc0dcdd52a6059242da6cc.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=bd42bd17cdfcc3ceabc0cf35a245d6b7/cefc1e178a82b901b0c2f206768da9773912ef9c.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=ca781b34b8a1cd111ab674268913c8b0/b219ebc4b74543a9b08205c81c178a82b901141c.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=961d4668f203738dc14a0a24831ab073/08f790529822720e23efdb327fcb0a46f31fabd0.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=69f36e3438f33a87816d061cf65d1018/8d5494eef01f3a29913f38ca9b25bc315c607c3b.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=7be3a2dae2fe9925d40c6f5604a95ee4/d53f8794a4c27d1e93d181db19d5ad6eddc4383d.jpg",
            "http://f.hiphotos.baidu.com/image/h%3D360/sign=aca7546fb119ebc4df78709fb227cf79/279759ee3d6d55fb676d1d2c6f224f4a20a4dd15.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=d8ffa9ab013b5bb5a1d726f806d2d523/a6efce1b9d16fdfa5f9a6eeeb68f8c5494ee7b38.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=6329923a938fa0ec60c7620b1696594a/9213b07eca8065380d2ef41a95dda144ac3482f6.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=2837d4cbf9edab646b724bc6c736af81/8b13632762d0f7031db73fdc0afa513d2697c5ad.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=b99475f3d3a20cf45990f8d946094b0c/9d82d158ccbf6c81601cde9cbf3eb13533fa409f.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=b56e9e3d79f40ad10ae4c1e5672c1151/d439b6003af33a875645a91fc45c10385343b58f.jpg",
            "http://g.hiphotos.baidu.com/image/h%3D360/sign=de93277e97cad1c8cfbbfa214f3f67c4/83025aafa40f4bfb1530a905014f78f0f63618fa.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=3fbffe3dce134954611eee62664f92dd/ac6eddc451da81cb58bbc5715766d01609243128.jpg",
            "http://e.hiphotos.baidu.com/image/h%3D360/sign=e86cf529ceef7609230b9f991edca301/6d81800a19d8bc3e55adf77e878ba61ea9d345d9.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=71344afbb47eca800d053fe1a1229712/5fdf8db1cb134954ca0604bc524e9258d0094aca.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=78b729229f16fdfac76cc0e8848e8cea/cc11728b4710b91217a424f7c1fdfc0392452256.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=1b73d383bc12c8fcabf3f0cbcc0292b4/8326cffc1e178a82d0f9de03f303738da877e848.jpg",
            "http://b.hiphotos.baidu.com/image/h%3D360/sign=0aae89397df0f736c7fe4a073a54b382/f603918fa0ec08faf0f7ace15cee3d6d54fbda85.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=348d3f228501a18befeb1449ae2e0761/8644ebf81a4c510f66a05b146259252dd42aa55c.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=4a8fffc080025aafcc3278cdcbecab8d/f3d3572c11dfa9ec118fef1760d0f703918fc109.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=3bf4d46e9a22720e64cee4fc4bcb0a3a/4a36acaf2edda3cc4edac92c03e93901213f92fb.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=30a0c8e1c1fdfc03fa78e5bee43f87a9/8b82b9014a90f6038bd1a2583b12b31bb051ed48.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=df0f29a3eff81a4c3932eacfe72b6029/2e2eb9389b504fc2065e2bd2e1dde71191ef6de0.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=9f6e24765adf8db1a32e7a623922dddb/0ff41bd5ad6eddc492d491153ddbb6fd52663328.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=e1e4d8e0314e251ffdf7e2fe9784c9c2/d01373f082025aaf04df0411faedab64024f1a7e.jpg",
            "http://f.hiphotos.baidu.com/image/h%3D360/sign=541fcde0ce11728b2f2d8a24f8fcc3b3/eac4b74543a982264ae369568982b9014a90eba5.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=bbcbb65457fbb2fb2b2b5e147f4a2043/a044ad345982b2b7328787cd33adcbef76099b9b.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=742cd8a34fed2e73e3e9802ab701a16d/6a63f6246b600c33c512e9ef1f4c510fd9f9a1a9.jpg",
            "http://g.hiphotos.baidu.com/image/h%3D360/sign=66b0e6e1be3eb1355bc7b1bd961fa8cb/7a899e510fb30f24a41d260aca95d143ad4b0330.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=15e6852602082838770dda128898a964/d62a6059252dd42a1aad2f92063b5bb5c9eab866.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=8936bfe687025aafcc3278cdcbedab8d/f3d3572c11dfa9ecd236af3167d0f703918fc1f8.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=1eb47ece9b25bc31345d079e6edf8de7/8694a4c27d1ed21b3db585e5af6eddc451da3fb5.jpg",
            "http://h.hiphotos.baidu.com/image/h%3D360/sign=76b94c916209c93d18f208f1af3cf8bb/aa64034f78f0f736106d4a9e0f55b319eac41399.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=c3722ee78f1001e9513c1209880f7b06/a71ea8d3fd1f4134f7b46270271f95cad1c85e38.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=1eb47ece9b25bc31345d079e6edf8de7/8694a4c27d1ed21b3db585e5af6eddc451da3fb5.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=52d6ae6990cad1c8cfbbfa214f3f67c4/83025aafa40f4bfb99752012064f78f0f63618ca.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=7067c136f236afc3110c39638318eb85/908fa0ec08fa513d55bfe028386d55fbb2fbd92b.jpg",
            "http://f.hiphotos.baidu.com/image/h%3D360/sign=6035c56d3c01213fd03348da64e636f8/fc1f4134970a304ec83a514ed4c8a786c9175c6e.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=bf06263337fae6cd13b4ad673fb30f9e/29381f30e924b899812cbe3c6c061d950a7bf6a6.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=c15e44bacb177f3e0f34fa0b40ce3bb9/4afbfbedab64034f6866140aadc379310a551d16.jpg",
            "http://c.hiphotos.baidu.com/image/h%3D360/sign=2bf4c65457fbb2fb2b2b5e147f4a2043/a044ad345982b2b7a2b8f7cd33adcbef76099b90.jpg",
            "http://a.hiphotos.baidu.com/image/h%3D360/sign=c1b4f92ab251f819ee25054ceab54a76/d6ca7bcb0a46f21f46af2152f4246b600c33ae1c.jpg",
            "http://d.hiphotos.baidu.com/image/h%3D360/sign=7e192e2a4b540923b5696578a259d1dc/dcc451da81cb39db4555b75fd2160924ab183026.jpg",
            "http://g.hiphotos.baidu.com/image/h%3D360/sign=d7346f2a612762d09f3ea2b990ed0849/5243fbf2b2119313cea62fb967380cd790238dd6.jpg"
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        GridView gridView = (GridView) findViewById(R.id.gridView);
        gridView.setAdapter(new PhotoWallAdapter(MainActivity.this,imgs,gridView));
    }
}

布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ts.mylrucache.MainActivity">

    <GridView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:columnWidth="120dp"
        android:stretchMode="columnWidth"
        android:numColumns="auto_fit"
        android:verticalSpacing="10dp"
        android:gravity="center"
        android:id="@+id/gridView" />
</RelativeLayout>

gridview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@mipmap/ic_launcher"
        android:layout_centerInParent="true"
        />
</LinearLayout>

適配器代碼 PhotoWallAdapter

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.util.LruCache;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

/**
 *照片牆的適配器,測試能緩存24張圖片
 */
public class PhotoWallAdapter extends BaseAdapter implements AbsListView.OnScrollListener{

    private Context context;
    private GridView gridView;

    /**
     * 記錄所有正在下載或等待下載的任務。
     */
    private ArrayList<BitmapWorkerTask> taskCollection;

    /**
     * 裝所有圖片的地址
     */
    private String[] imgsUrl;

    /**
     * 主要算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,
     * 並且把最近最少使用的對象在緩存值達到預設定值之前從內存中移除。
     */
    private LruCache<String,Bitmap> memoryCache;//內存緩存,當做map來看就是

    /**
     * 當前屏幕第一張可見圖片的下標
     */
    private int mFirstVisibleItem;

    /**
     * 一屏有多少張圖片可見
     */
    private int mVisibleItemCount;

    /**
     * 記錄是否剛打開程序,用於解決進入程序不滾動屏幕,不會下載圖片的問題。
     */
    private boolean isFirstEnter = true;

    public PhotoWallAdapter(Context context,String[] imgsUrl,GridView gridView) {
        this.context=context;
        this.imgsUrl = imgsUrl;
        this.gridView=gridView;
        taskCollection=new ArrayList<BitmapWorkerTask>();
        int maxMemory = (int) (Runtime.getRuntime().maxMemory());//獲取應用的最大可用內存
        Log.i("TAG", "該應用的最大可用內存為:" + maxMemory/1024 + "KB");//64M,1Kb=1024個字節
        //使用最大可用內存值的1/8作為緩存的大小
        int cacheSize=maxMemory / 8;
        memoryCache=new LruCache<String,Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //重寫此方法來衡量每張圖片的大小,默認返回的1
                return value.getByteCount();
            }
        };
        gridView.setOnScrollListener(this);//設置滑動監聽
    }
    /**
     * 添加一個位圖到內存中
     * @param key 該位圖在內存中的鍵
     * @param bitmap 需要緩存的位圖
     */
    public void addBitmapToMemoryCache(String key,Bitmap bitmap){
        if(getBitmapFromMemCache(key) == null){
           memoryCache.put(key, bitmap);
            Log.i("tag","緩存的個數:"+memoryCache.hitCount());//如果根據相應的key得到value,就增加一次命中hitCount
            Log.i("tag","沒有命中(緩存)的個數:"+memoryCache.missCount());//否則增加一次missCount
            Log.i("tag","回收的個數:"+memoryCache.evictionCount());
            Log.i("tag","調用create的次數:"+memoryCache.createCount());//createCount表示調用create的次數
            Log.i("tag","總共添加的次數:"+memoryCache.putCount());
            Log.i("tag","當前緩存的總大小:"+memoryCache.size());
        }
    }

    /**
     *根據鍵獲取內存中的bitmap
     */
    private Bitmap getBitmapFromMemCache(String key) {
        return memoryCache.get(key);
    }

    @Override
    public int getCount() {
        return imgsUrl.length;
    }

    @Override
    public Object getItem(int position) {
        return imgsUrl[position];
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if(convertView==null){
            holder=new ViewHolder();
            convertView=View.inflate(context, R.layout.gridview_item, null);
            holder.img= (ImageView) convertView.findViewById(R.id.imageView);
            convertView.setTag(holder);
        }else{
            holder= (ViewHolder) convertView.getTag();
        }
        holder.img.setTag(imgsUrl[position]);
        //先從緩存中獲取
        Bitmap bitmap = getBitmapFromMemCache(imgsUrl[position]);
        if(bitmap!=null){
            holder.img.setImageBitmap(bitmap);
        }else{
            //否則先設置默認圖片,等待停止滑動執行onScrollStateChanged方法時再從網絡上獲取
            holder.img.setImageResource(R.mipmap.ic_launcher);
        }
        return convertView;
    }


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 僅當GridView靜止,手指離開屏幕時才去下載圖片,GridView滑動時取消所有正在下載的任務
        if (scrollState == SCROLL_STATE_IDLE) {
            loadBitmaps(mFirstVisibleItem, mVisibleItemCount);//從網絡上獲取圖片
        } else {
            cancelAllTasks();//取消所有正在下載任務
        }
    }

    /**
     * 首次開啟應用時會執行此方法
     * 下載的任務應該由onScrollStateChanged里調用,但首次進入程序時onScrollStateChanged並不會調用
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleItem=firstVisibleItem;//記錄當前屏幕第一個的下標
        mVisibleItemCount=visibleItemCount;//屏幕顯示的個數
        if (isFirstEnter && visibleItemCount > 0) {
            loadBitmaps(firstVisibleItem, visibleItemCount);//下載圖片
            isFirstEnter = false;
        }
    }

    /**
     * 取消所有正在下載或等待下載的任務。
     */
    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
            taskCollection.removeAll(taskCollection);
        }
    }

    /**
     * 加載Bitmap對象。此方法會在LruCache中檢查所有屏幕中可見的ImageView的Bitmap對象,
     * 如果發現任何一個ImageView的Bitmap對象不在緩存中,就會開啟異步線程去下載圖片。
     *
     * @param firstVisibleItem
     *            第一個可見的ImageView的下標
     * @param visibleItemCount
     *            屏幕中總共可見的元素數
     */
    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
        try {
            for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
                String imageUrl = imgsUrl[i];
                Bitmap bitmap = getBitmapFromMemCache(imageUrl);
                if (bitmap == null) {
                    BitmapWorkerTask task = new BitmapWorkerTask();
                    taskCollection.add(task);
                    task.execute(imageUrl);
                } else {
                    //由於在getView方法中就設置了已經存在的緩存圖片,所以這里沒必要再次獲取
                    /*ImageView imageView = (ImageView) gridView.findViewWithTag(imageUrl);
                    if (imageView != null && bitmap != null) {
                        imageView.setImageBitmap(bitmap);
                    }*/
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class ViewHolder {
        ImageView img;
    }

    /**
     * 異步下載圖片
     */
    class BitmapWorkerTask extends AsyncTask<String,Void,Bitmap>{
        private String imgUrl;
        @Override
        protected Bitmap doInBackground(String... params) {
            imgUrl=params[0];
            Bitmap bitmap = downloadBitmap(imgUrl);
            if (bitmap != null) {
                // 圖片下載完成后緩存到LrcCache中
                addBitmapToMemoryCache(imgUrl, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            // 根據Tag找到相應的ImageView控件,將下載好的圖片顯示出來。
            ImageView imageView = (ImageView) gridView.findViewWithTag(imgUrl);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
           taskCollection.remove(this);//圖片下載完畢,從異步任務集合中刪除
            Log.i("tag","任務集合包含的個數:"+taskCollection.size());
        }

        /**
         *根據網址下載圖片
         */
        private Bitmap downloadBitmap(String imageUrl){
            Bitmap bitmap = null;
            HttpURLConnection con = null;
            try {
                URL url = new URL(imageUrl);
                con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(5 * 1000);
                con.setReadTimeout(10 * 1000);
                //這里可以結合第一種方法,將圖片壓縮到一定的尺寸再存到內存中,使得LruCache能存更多的圖片
                bitmap = BitmapFactory.decodeStream(con.getInputStream());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (con != null) {
                    con.disconnect();//斷開連接
                }
            }
            return bitmap;
        }
    }
}

效果圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM