แคชภาพ Android


คำตอบ:


177

และตอนนี้ punchline: ใช้แคชของระบบ

URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
  Bitmap bitmap = (Bitmap)response;
} 

จัดให้มีทั้งหน่วยความจำและแคชแฟลชรอมที่แชร์กับเบราว์เซอร์

GRR ฉันหวังว่าจะมีคนบอกฉันว่าก่อนที่ฉันจะเขียนโปรแกรมจัดการแคชของตัวเอง


1
ว้าวนี่เป็นวิธีที่ยอดเยี่ยมในการทำเช่นนี้ขอบคุณมาก มันไม่ช้ากว่าตัวจัดการแคชแบบเรียบง่ายของฉันเองและตอนนี้ฉันไม่จำเป็นต้องดูแลทำความสะอาดในโฟลเดอร์การ์ด SD
Kevin อ่าน

11
connection.getContent()ส่งคืน InputStream ให้ฉันเสมอฉันกำลังทำอะไรผิด
Tyler Collier

3
ถ้าฉันจะตอนนี้ยังกำหนดวันหมดอายุในเนื้อหาสำหรับแคชชีวิตของฉันจะเป็นมากขึ้น :)
Janusz

11
@Scienceprodigy ไม่รู้หรอกว่า BitmapLoader คืออะไรแน่นอนว่ามันไม่ได้อยู่ในไลบรารี่ android มาตรฐานใด ๆ ที่ฉันรู้ แต่อย่างน้อยก็ทำให้ฉันไปในทิศทางที่ถูกต้อง Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
Stephen Fuhry

6
ให้แน่ใจว่าได้เห็นคำตอบของโจด้านล่างเกี่ยวกับขั้นตอนพิเศษที่คุณต้องทำเพื่อให้แคชทำงาน
Keith

65

เกี่ยวกับการconnection.setUseCachesแก้ปัญหาที่สง่างามข้างต้น: น่าเศร้าที่มันไม่สามารถทำงานได้หากไม่มีความพยายามเพิ่มเติม คุณจะต้องติดตั้งใช้ResponseCache ResponseCache.setDefaultมิฉะนั้นHttpURLConnectionจะเพิกเฉยต่อsetUseCaches(true)บิตเงียบ ๆ

ดูความคิดเห็นที่ด้านบนของFileResponseCache.javaเพื่อดูรายละเอียด:

http://libs-for-android.googlecode.com/svn/reference/com/google/android/filecache/FileResponseCache.html

(ฉันโพสต์สิ่งนี้ในความคิดเห็น แต่เห็นได้ชัดว่าฉันมีกรรมไม่เพียงพอ)


นี่คือไฟล์
Telémako

2
เมื่อคุณใช้HttpResponseCacheคุณอาจพบHttpResponseCache.getHitCount()0 ที่ส่งคืนฉันไม่แน่ใจ แต่ฉันคิดว่าเป็นเพราะเว็บเซิร์ฟเวอร์ที่คุณร้องขอไม่ได้ใช้ส่วนหัวแคชในกรณีนั้น หากต้องการทำให้การแคชใช้งานconnection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);ได้
Almer

1
ลิงก์ค้นหารหัส Google ไม่ทำงาน (อีกแล้ว?) โปรดอัปเดตลิงก์
เฟลิกซ์ดี

นอกจากนี้ฉันไม่แน่ใจว่าพฤติกรรมนี้ได้รับการแก้ไขแล้วหรือไม่ ด้วยเหตุผลบางอย่างที่ส่งคืน 304 จากเซิร์ฟเวอร์จะแฮงค์ HUC เมื่อใช้.getContent()วิธีการเนื่องจาก 304 การตอบสนองไม่มีเนื้อหาการตอบสนองที่เกี่ยวข้องตามมาตรฐาน RFC
TheRealChx101

27

แปลงเป็น Bitmaps แล้วเก็บไว้ใน Collection (HashMap, List เป็นต้น) หรือคุณสามารถเขียนมันลงใน SDcard

เมื่อจัดเก็บไว้ในพื้นที่แอปพลิเคชันโดยใช้วิธีแรกคุณอาจต้องการล้อมรอบด้วยjava.lang.ref.SoftReferenceโดยเฉพาะถ้าจำนวนของพวกเขามีขนาดใหญ่ (เพื่อให้พวกเขาเก็บขยะในช่วงวิกฤต) นี่อาจเป็นเพราะการโหลดซ้ำ

HashMap<String,SoftReference<Bitmap>> imageCache =
        new HashMap<String,SoftReference<Bitmap>>();

การเขียนลง SDcard นั้นไม่จำเป็นต้องโหลดซ้ำ เพียงแค่สิทธิ์ของผู้ใช้


เราจะเขียนอิมเมจใน sd หรือหน่วยความจำโทรศัพท์ได้อย่างไร?
d-man

ในการบันทึกภาพในการ์ด SD: คุณสามารถส่ง Image Streams ที่อ่านจากเซิร์ฟเวอร์ระยะไกลไปยังหน่วยความจำโดยใช้การดำเนินการตามปกติของไฟล์ I / O หรือหากคุณแปลงรูปภาพของคุณเป็นวัตถุบิตแมปคุณสามารถใช้วิธี Bitmap.compress ()
Samuh

@ d-man ฉันขอแนะนำให้เขียนลงดิสก์ก่อนแล้วจึงได้รับการUriอ้างอิงเส้นทางที่คุณสามารถส่งต่อImageViewและมุมมองที่กำหนดเองอื่น ๆ เพราะทุกครั้งที่คุณcompressคุณจะสูญเสียคุณภาพ แน่นอนว่านี่เป็นความจริงสำหรับอัลกอริทึมที่สูญเสียเท่านั้น วิธีนี้จะช่วยให้คุณสามารถเก็บแฮชของไฟล์และใช้ในครั้งถัดไปที่คุณขอไฟล์จากเซิร์ฟเวอร์ผ่านIf-None-MatchและETagส่วนหัว
TheRealChx101

@ TheRealChx101 คุณช่วยกรุณาเข้าใจในสิ่งที่คุณหมายถึงในครั้งต่อไปที่คุณขอไฟล์จากเซิร์ฟเวอร์ผ่านส่วนหัว If-None-Match และ ETagฉันมักจะมองหาวิธีการแก้ปัญหาที่ภาพควรจะใช้รูปแบบแคชในท้องถิ่นเพื่อกำหนด รอบระยะเวลาหรือหากไม่สามารถทำได้เมื่อใดก็ตามที่เนื้อหาสำหรับ URL ได้รับการเปลี่ยนแปลงมันควรจะสะท้อนให้เห็นในแอปพลิเคชันที่มีล่าสุดและแคชไว้
รหัส

@CoDe เยี่ยมชมลิงค์นี้ตอนนี้android.jlelse.eu/…
TheRealChx101

27

ใช้LruCacheเพื่อแคชรูปภาพอย่างมีประสิทธิภาพ คุณสามารถอ่านเกี่ยวกับLruCacheจากเว็บไซต์นักพัฒนา Android

ฉันใช้วิธีแก้ปัญหาด้านล่างเพื่อดาวน์โหลดรูปภาพและแคชใน Android คุณสามารถทำตามขั้นตอนด้านล่าง:

ขั้นตอนที่ 1:ImagesCacheทำให้ชั้นชื่อ ฉันเคยใช้Singleton object for this class

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

public class ImagesCache 
{
    private  LruCache<String, Bitmap> imagesWarehouse;

    private static ImagesCache cache;

    public static ImagesCache getInstance()
    {
        if(cache == null)
        {
            cache = new ImagesCache();
        }

        return cache;
    }

    public void initializeCache()
    {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);

        final int cacheSize = maxMemory / 8;

        System.out.println("cache size = "+cacheSize);

        imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
                {
                    protected int sizeOf(String key, Bitmap value) 
                    {
                        // The cache size will be measured in kilobytes rather than number of items.

                        int bitmapByteCount = value.getRowBytes() * value.getHeight();

                        return bitmapByteCount / 1024;
                    }
                };
    }

    public void addImageToWarehouse(String key, Bitmap value)
    {       
        if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
        {
            imagesWarehouse.put(key, value);
        }
    }

    public Bitmap getImageFromWarehouse(String key)
    {
        if(key != null)
        {
            return imagesWarehouse.get(key);
        }
        else
        {
            return null;
        }
    }

    public void removeImageFromWarehouse(String key)
    {
        imagesWarehouse.remove(key);
    }

    public void clearCache()
    {
        if(imagesWarehouse != null)
        {
            imagesWarehouse.evictAll();
        }       
    }

}

ขั้นตอนที่ 2:

ทำให้คลาสอื่นชื่อว่า DownloadImageTask ซึ่งใช้ถ้าบิตแมปไม่พร้อมใช้งานในแคชมันจะดาวน์โหลดจากที่นี่:

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{   
    private int inSampleSize = 0;

    private String imageUrl;

    private BaseAdapter adapter;

    private ImagesCache cache;

    private int desiredWidth, desiredHeight;

    private Bitmap image = null;

    private ImageView ivImageView;

    public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight) 
    {
        this.adapter = adapter;

        this.cache = ImagesCache.getInstance();

        this.desiredWidth = desiredWidth;

        this.desiredHeight = desiredHeight;
    }

    public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
    {
        this.cache = cache;

        this.ivImageView = ivImageView;

        this.desiredHeight = desireHeight;

        this.desiredWidth = desireWidth;
    }

    @Override
    protected Bitmap doInBackground(String... params) 
    {
        imageUrl = params[0];

        return getImage(imageUrl);
    }

    @Override
    protected void onPostExecute(Bitmap result) 
    {
        super.onPostExecute(result);

        if(result != null)
        {
            cache.addImageToWarehouse(imageUrl, result);

            if(ivImageView != null)
            {
                ivImageView.setImageBitmap(result);
            }
            else if(adapter != null)
            {
                adapter.notifyDataSetChanged();
            }
        }
    }

    private Bitmap getImage(String imageUrl)
    {   
        if(cache.getImageFromWarehouse(imageUrl) == null)
        {
            BitmapFactory.Options options = new BitmapFactory.Options();

            options.inJustDecodeBounds = true;

            options.inSampleSize = inSampleSize;

            try
            {
                URL url = new URL(imageUrl);

                HttpURLConnection connection = (HttpURLConnection)url.openConnection();

                InputStream stream = connection.getInputStream();

                image = BitmapFactory.decodeStream(stream, null, options);

                int imageWidth = options.outWidth;

                int imageHeight = options.outHeight;

                if(imageWidth > desiredWidth || imageHeight > desiredHeight)
                {   
                    System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);

                    inSampleSize = inSampleSize + 2;

                    getImage(imageUrl);
                }
                else
                {   
                    options.inJustDecodeBounds = false;

                    connection = (HttpURLConnection)url.openConnection();

                    stream = connection.getInputStream();

                    image = BitmapFactory.decodeStream(stream, null, options);

                    return image;
                }
            }

            catch(Exception e)
            {
                Log.e("getImage", e.toString());
            }
        }

        return image;
    }

ขั้นตอนที่ 3:การใช้งานจากของคุณActivityหรือAdapter

หมายเหตุ:หากคุณต้องการโหลดภาพจาก url จากActivityClass ใช้ Constructor ที่สองของDownloadImageTaskแต่ถ้าคุณต้องการแสดงรูปภาพจากการAdapterใช้ Constructor แรกของDownloadImageTask(ตัวอย่างเช่นคุณมีรูปภาพอยู่ListViewและคุณกำลังตั้งค่ารูปภาพจาก 'Adaptor')

การใช้งานจากกิจกรรม:

ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.

  imgTask.execute(img);
}

การใช้งานจากอะแดปเตอร์:

ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.

  imgTask.execute(img);
}

บันทึก:

cache.initializeCache()คุณสามารถใช้คำสั่งนี้ในกิจกรรมแรกของแอปพลิเคชันของคุณ เมื่อคุณเริ่มต้นแคชคุณจะไม่ต้องเริ่มต้นใหม่ทุกครั้งถ้าคุณใช้ImagesCacheอินสแตนซ์

ฉันไม่เคยอธิบายสิ่งต่าง ๆ ได้ดี แต่หวังว่านี่จะช่วยผู้เริ่มต้นใช้งานว่าจะใช้แคชอย่างไรLruCacheและใช้อย่างไร :)

แก้ไข:

วันนี้มีห้องสมุดที่มีชื่อเสียงที่รู้จักกันดีPicassoและGlideสามารถใช้โหลดรูปภาพได้อย่างมีประสิทธิภาพในแอพ android ลองใช้Picassoไลบรารี่ที่เรียบง่ายและมีประโยชน์สำหรับ AndroidและGlide สำหรับ Android คุณไม่จำเป็นต้องกังวลเกี่ยวกับภาพแคช

Picasso ช่วยให้การโหลดภาพที่ไม่ยุ่งยากในแอปพลิเคชั่นของคุณ - บ่อยครั้งในโค้ดหนึ่งบรรทัด!

ร่อนเช่นเดียวกับ Picasso สามารถโหลดและแสดงภาพจากหลาย ๆ แหล่งในขณะที่ยังดูแลแคชและรักษาผลกระทบหน่วยความจำต่ำเมื่อทำการจัดการภาพ แอปอย่างเป็นทางการของ Google ถูกใช้งานแล้ว (เช่นแอพสำหรับ Google I / O 2015) และเป็นที่นิยมเช่นเดียวกับ Picasso ในซีรี่ส์นี้เราจะสำรวจความแตกต่างและข้อดีของ Glide over Picasso

คุณสามารถเยี่ยมชมบล็อกเพื่อดูความแตกต่างระหว่าง Glide และ Picasso


3
คำตอบและคำอธิบายที่โดดเด่น! ฉันคิดว่านี่เป็นทางออกที่ดีที่สุดเพราะใช้งานได้ตอนออฟไลน์และใช้ Android LruCache ฉันพบว่าโซลูชันของ edrowland ไม่ทำงานในโหมดเครื่องบินแม้จะมีการเพิ่มของ Joe ซึ่งต้องการความพยายามมากขึ้นในการรวมเข้าด้วยกัน Btw ดูเหมือนว่า Android หรือเครือข่ายจะมีการแคชจำนวนมากแม้ว่าคุณจะไม่ทำอะไรเป็นพิเศษก็ตาม (นิดหน่อยเล็กน้อยหนึ่ง: สำหรับการใช้งานตัวอย่าง getImageFromWareHouse ตัว 'H' ควรเป็นตัวพิมพ์เล็กเพื่อให้ตรงกัน) ขอบคุณ!
Edwin Evans

1
คำอธิบายที่ดี :)
XtreemDeveloper

คุณช่วยอธิบายวิธี getImage () โดยเฉพาะอย่างยิ่งกับขนาดภาพและวิธีการที่มันเกิดขึ้นได้อย่างไร ฉันไม่เข้าใจเช่นทำไมคุณเรียกฟังก์ชั่นภายในตัวเองอีกครั้งและวิธีการทำงาน
Greyshack

1
โหวตขึ้นสำหรับสิ่งif(cache == null)ที่แก้ปัญหาของฉัน! :)
MR. Garcia

1
ดูคำตอบที่แก้ไขของฉันในตอนท้าย ฉันได้พูดถึงห้องสมุดที่มีชื่อเสียงที่นักพัฒนาซอฟต์แวร์ส่วนใหญ่ใช้อยู่ทุกวัน ลองใช้ Picasso เหล่านั้น: square.github.io/picasso and Glide: futurestud.io/blog/glide-getting-started
Zubair Ahmed

18

ในการดาวน์โหลดภาพและบันทึกลงในการ์ดหน่วยความจำคุณสามารถทำได้เช่นนี้

//First create a new URL object 
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")

//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");

//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());

//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));

อย่าลืมเพิ่มสิทธิ์อินเทอร์เน็ตในรายการของคุณ:

<uses-permission android:name="android.permission.INTERNET" />

10
ทำไมคุณถึงถอดรหัส JPEG แล้วเข้ารหัสอีกครั้ง คุณจะได้รับบริการที่ดีกว่าในการดาวน์โหลด URL ไปยังอาร์เรย์ไบต์จากนั้นใช้อาร์เรย์ไบต์นั้นเพื่อสร้างบิตแมปของคุณและเขียนลงในไฟล์ ทุกครั้งที่คุณถอดรหัสและเข้ารหัส JPEG คุณภาพของภาพจะแย่ลง
CommonsWare

2
จุดที่เป็นธรรมมากขึ้นสำหรับความเร็วแล้วอะไรก็ตาม แม้ว่าหากบันทึกเป็นอาร์เรย์ไบต์และไฟล์ต้นฉบับไม่ใช่ JPEG จะไม่ต้องแปลงไฟล์เลยใช่ไหม "decodeByteArray" จาก SDK ส่งคืน "บิตแมปที่ถอดรหัสหรือโมฆะถ้าข้อมูลรูปภาพไม่สามารถถอดรหัสได้" ดังนั้นสิ่งนี้ทำให้ฉันคิดว่ามันถอดรหัสข้อมูลรูปภาพอยู่เสมอดังนั้นจะไม่ต้องเข้ารหัสอีกครั้งหรือไม่
Ljdawson

การพูดถึงประสิทธิภาพมันจะมีประสิทธิภาพหรือไม่ถ้าแทนที่จะส่ง FileOutputStream ที่เราส่งผ่าน BufferedOutputStream
Samuh

1
ฉันไม่แนะนำให้แคชภาพไปยังการ์ด SD ของคุณ เมื่อถอนการติดตั้งแอปพลิเคชันแล้วรูปภาพจะไม่ถูกลบออกทำให้ SD การ์ดเต็มไปด้วยขยะที่ไร้ประโยชน์ แนะนำให้บันทึกภาพไปยังไดเรกทอรีแคชของแอปพลิเคชัน
james

ด้วยขีด จำกัด APK ที่ 50mb ในขณะนี้การแคชไปยังการ์ด SD อาจเป็นวิธีเดียวสำหรับนักพัฒนา
Ljdawson

13

ฉันจะพิจารณาใช้แคชรูปภาพของ droidfu มันใช้ทั้งแคชรูปภาพในหน่วยความจำและดิสก์ คุณยังได้รับ WebImageView ที่ใช้ประโยชน์จากไลบรารี ImageCache

นี่คือคำอธิบายแบบเต็มของ droidfu และ WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/


เขาปรับโครงสร้างรหัสของเขาใหม่ตั้งแต่ปี 2010 นี่คือลิงค์ราก: github.com/kaeppler/droid-fu
esilver

3
ลิงก์นั้นยังใช้งานไม่ได้ ฉันเขียนห้องสมุดที่คล้ายกันชื่อว่า Android-ImageManager github.com/felipecsl/Android-ImageManager
Felipe Lima

9

ฉันได้ลอง SoftReferences แล้วพวกเขาก็มีการเรียกคืนอุปกรณ์อย่างรุนแรงใน Android ที่ฉันรู้สึกว่าไม่มีประโยชน์ในการใช้


2
เห็นด้วย - SoftReferences ถูกเรียกคืนอย่างรวดเร็วบนอุปกรณ์ที่ฉันทดสอบ
ตอนที่

3
Google มีตัวเองยืนยันว่า Dalvik ของ GC ก้าวร้าวมากในการเก็บรวบรวมSoftReferences พวกเขาแนะนำให้ใช้LruCacheแทน
kaka

9

ตามที่ Thunder Rabbit แนะนำไว้ ImageDownloader นั้นดีที่สุดสำหรับงานนี้ ฉันยังพบความแตกต่างเล็กน้อยของชั้นเรียนที่:

http://theandroidcoder.com/utilities/android-image-download-and-caching/

ข้อแตกต่างที่สำคัญระหว่างทั้งสองคือ ImageDownloader ใช้ระบบแคช Android และอุปกรณ์ที่ถูกดัดแปลงใช้หน่วยความจำภายในและภายนอกเป็นตัวแคชทำให้ภาพที่เก็บไว้ไม่สิ้นสุดหรือจนกว่าผู้ใช้จะลบมันออกด้วยตนเอง ผู้เขียนยังกล่าวถึงความเข้ากันได้กับ Android 2.1


7

นี่เป็นสิ่งที่ Joe จับได้เป็นอย่างดี ตัวอย่างโค้ดด้านบนมีสองปัญหา - ข้อหนึ่ง - วัตถุตอบสนองไม่ใช่อินสแตนซ์ของบิตแมป (เมื่อ URL ของฉันอ้างถึง jpg เช่น http: \ website.com \ image.jpg มันคือ

org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream)

ข้อสองในขณะที่ Joe ชี้ให้เห็นไม่มีการแคชเกิดขึ้นหากไม่มีแคชการตอบสนองที่กำหนด นักพัฒนา Android ถูกปล่อยให้แคชของตัวเอง นี่เป็นตัวอย่างของการทำเช่นนั้น แต่แคชในหน่วยความจำเท่านั้นซึ่งจริงๆแล้วไม่ใช่วิธีการแก้ปัญหาที่สมบูรณ์

http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/

API การแคช URLConnection อธิบายไว้ที่นี่:

http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html

ฉันยังคิดว่านี่เป็นทางออกที่ดีสำหรับเส้นทางนี้ - แต่คุณยังต้องเขียนแคช ฟังดูสนุก แต่ฉันอยากจะเขียนคุณสมบัติ


7

มีรายการพิเศษในส่วนการฝึกอบรมอย่างเป็นทางการของ Android เกี่ยวกับเรื่องนี้: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

ส่วนนี้ค่อนข้างใหม่มันไม่ได้อยู่ตรงนั้นเมื่อถามคำถาม

ทางออกที่แนะนำคือการใช้ LruCache คลาสนั้นได้รับการแนะนำใน Honeycomb แต่มันยังรวมอยู่ในไลบรารีความเข้ากันได้

คุณสามารถเริ่มต้น LruCache โดยการตั้งค่าจำนวนสูงสุดหรือรายการและมันจะจัดเรียงพวกเขาของคุณโดยอัตโนมัติและทำความสะอาดพวกเขาใช้น้อยกว่าเมื่อคุณไปเกินขีด จำกัด นอกเหนือจากนั้นจะใช้เป็นแผนที่ปกติ

ตัวอย่างโค้ดจากหน้าอย่างเป็นทางการ:

private LruCache mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get memory class of this device, exceeding this amount will throw an
    // OutOfMemory exception.
    final int memClass = ((ActivityManager) context.getSystemService(
            Context.ACTIVITY_SERVICE)).getMemoryClass();

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

    mMemoryCache = new LruCache(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in bytes rather than number of items.
            return bitmap.getByteCount();
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}

ก่อนหน้านี้ SoftReferences เป็นทางเลือกที่ดี แต่ไม่ใช่อีกต่อไปอ้างอิงจากหน้าอย่างเป็นทางการ:

หมายเหตุ: ในอดีตการใช้แคชหน่วยความจำยอดนิยมคือแคชบิตแมป SoftReference หรือ WeakReference อย่างไรก็ตามไม่แนะนำให้ทำเช่นนี้ เริ่มต้นจาก Android 2.3 (API ระดับ 9) ตัวเก็บรวบรวมขยะมีความก้าวร้าวมากขึ้นด้วยการรวบรวมการอ้างอิงแบบอ่อน / อ่อนซึ่งทำให้ไม่มีประสิทธิภาพพอสมควร นอกจากนี้ก่อนหน้า Android 3.0 (API ระดับ 11) ข้อมูลสำรองของบิตแมปถูกเก็บไว้ในหน่วยความจำดั้งเดิมซึ่งไม่ได้เผยแพร่ในลักษณะที่สามารถคาดการณ์ได้ซึ่งอาจทำให้แอปพลิเคชันเกินขีด จำกัด หน่วยความจำ


3

พิจารณาการใช้ยูนิเวอร์แซห้องสมุดภาพ LoaderโดยSergey Tarasevich มันมาพร้อมกับ:

  • การโหลดภาพแบบมัลติเธรด มันช่วยให้คุณสามารถกำหนดขนาดพูลเธรด
  • การแคชรูปภาพในหน่วยความจำบนระบบไฟล์ของอุปกรณ์และการ์ด SD
  • ความเป็นไปได้ที่จะฟังความคืบหน้าในการโหลดและการโหลดเหตุการณ์

Universal Image Loader อนุญาตการจัดการแคชอย่างละเอียดสำหรับรูปภาพที่ดาวน์โหลดด้วยการกำหนดค่าแคชต่อไปนี้:

  • UsingFreqLimitedMemoryCache: บิตแมปที่ใช้งานบ่อยที่สุดจะถูกลบเมื่อเกินขีด จำกัด ขนาดแคช
  • LRULimitedMemoryCache: บิตแมปที่ใช้น้อยที่สุดเมื่อเร็ว ๆ นี้จะถูกลบเมื่อเกินขีด จำกัด ขนาดแคช
  • FIFOLimitedMemoryCache: กฎ FIFO ใช้สำหรับการลบเมื่อเกินขีด จำกัด ขนาดแคช
  • LargestLimitedMemoryCache: บิตแมปที่ใหญ่ที่สุดจะถูกลบเมื่อเกินขีด จำกัด ขนาดแคช
  • LimitedAgeMemoryCache: วัตถุที่เก็บไว้จะถูกลบเมื่อมันอายุเกินกว่าค่าที่กำหนดไว้
  • WeakMemoryCache: แคชหน่วยความจำที่มีการอ้างอิงที่อ่อนแอเพียงเล็กน้อยกับบิตแมป

ตัวอย่างการใช้งานง่าย:

ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://site.com/image.png"; 

ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);

UsingFreqLimitedMemoryCacheตัวอย่างนี้ใช้ค่าเริ่มต้น


เมื่อใช้อย่างเข้มงวด Universal Image Loader จะทำให้หน่วยความจำรั่วไหลมาก ฉันสงสัยว่าสิ่งนี้เกิดขึ้นเพราะใช้ซิงเกิลตันในรหัส (ดู 'getInstance ()' ในตัวอย่าง) หลังจากโหลดภาพจำนวนมากแล้วหมุนหน้าจอสองครั้งแอปของฉันก็ขัดข้องตลอดเวลาเพราะ OutOfMemoryErrors ใน UIL มันเป็นห้องสมุดที่ดี แต่มันเป็นความจริงที่รู้กันดีว่าคุณไม่ควรใช้ singletons โดยเฉพาะใน Android ...
Geert Bellemans

1
ใช้ซิงเกิลตันเมื่อคุณรู้วิธี! :)
Renetik

3

สิ่งที่ใช้งานได้จริงสำหรับฉันคือการตั้งค่า ResponseCache ในคลาสหลักของฉัน:

try {
   File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
   long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
   HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { } 

และ

connection.setUseCaches(true);

เมื่อดาวน์โหลดบิตแมป

http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html


เป็นไปได้ไหมที่จะใช้ lrucache ร่วมกับ httpresponsecache
iOSAndroidWindowsMobileAppsDev


1

ฉันเคยต่อสู้กับสิ่งนี้มาระยะหนึ่งแล้ว คำตอบที่ใช้ SoftReferences จะทำให้ข้อมูลสูญหายเร็วเกินไป คำตอบที่แนะนำให้สร้างอินสแตนซ์ของ RequestCache นั้นยุ่งเกินไปแถมฉันไม่สามารถหาตัวอย่างได้เลย

แต่ImageDownloader.javaทำงานได้อย่างยอดเยี่ยมสำหรับฉัน มันใช้ HashMap จนกว่าจะถึงความจุหรือจนกว่าจะหมดเวลาการล้างจึงเกิดสิ่งต่างๆขึ้นมาที่ SoftReference ดังนั้นจึงใช้สิ่งที่ดีที่สุดของทั้งสองโลก



0

แม้ภายหลังคำตอบ แต่ฉันเขียน Android Image Manager ที่จัดการแคชโปร่งใส (หน่วยความจำและดิสก์) รหัสนี้อยู่บน Github https://github.com/felipecsl/Android-ImageManager


1
ฉันเพิ่มสิ่งนี้ลงใน ListView และดูเหมือนว่าจะจัดการไม่ดี มีการใช้งานพิเศษสำหรับ ListViews ไหม?

0

คำตอบปลาย แต่ฉันคิดว่าฉันควรจะเพิ่มการเชื่อมโยงไปยังเว็บไซต์ของฉันเพราะฉันได้เขียนกวดวิชาวิธีการทำแคชรูปภาพสำหรับ Android: http://squarewolf.nl/2010/11/android-image-cache/ ปรับปรุง:หน้าถูกออฟไลน์เนื่องจากที่มาล้าสมัย ฉันเข้าร่วม @elenasys ในคำแนะนำของเธอที่จะใช้ระบบจุดระเบิด

ดังนั้นสำหรับทุกคนที่สะดุดกับคำถามนี้และไม่พบวิธีแก้ปัญหา: หวังว่าคุณจะสนุก! = D


0

คำตอบหลังเวลาที่กำหนด แต่ฉันคิดว่าห้องสมุดนี้จะช่วยให้มากกับแคชภาพ: https://github.com/crypticminds/ColdStorage

เพียงแค่ใส่คำอธิบายประกอบ ImageView ด้วย @LoadCache (R.id.id_of_my_image_view, "URL_to_downlaod_image_from") แล้วมันจะดูแลการดาวน์โหลดรูปภาพและโหลดลงในมุมมองรูปภาพนอกจากนี้คุณยังสามารถระบุรูปภาพตัวแทนและโหลดภาพเคลื่อนไหว

เอกสารรายละเอียดของคำอธิบายประกอบมีอยู่ที่นี่: - https://github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.