ฉันจะแคชรูปภาพหลังจากดาวน์โหลดจากเว็บได้อย่างไร
ฉันจะแคชรูปภาพหลังจากดาวน์โหลดจากเว็บได้อย่างไร
คำตอบ:
และตอนนี้ 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 ฉันหวังว่าจะมีคนบอกฉันว่าก่อนที่ฉันจะเขียนโปรแกรมจัดการแคชของตัวเอง
connection.getContent()
ส่งคืน InputStream ให้ฉันเสมอฉันกำลังทำอะไรผิด
Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
เกี่ยวกับการconnection.setUseCaches
แก้ปัญหาที่สง่างามข้างต้น: น่าเศร้าที่มันไม่สามารถทำงานได้หากไม่มีความพยายามเพิ่มเติม คุณจะต้องติดตั้งใช้ResponseCache
ResponseCache.setDefault
มิฉะนั้นHttpURLConnection
จะเพิกเฉยต่อsetUseCaches(true)
บิตเงียบ ๆ
ดูความคิดเห็นที่ด้านบนของFileResponseCache.java
เพื่อดูรายละเอียด:
(ฉันโพสต์สิ่งนี้ในความคิดเห็น แต่เห็นได้ชัดว่าฉันมีกรรมไม่เพียงพอ)
HttpResponseCache
คุณอาจพบHttpResponseCache.getHitCount()
0 ที่ส่งคืนฉันไม่แน่ใจ แต่ฉันคิดว่าเป็นเพราะเว็บเซิร์ฟเวอร์ที่คุณร้องขอไม่ได้ใช้ส่วนหัวแคชในกรณีนั้น หากต้องการทำให้การแคชใช้งานconnection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
ได้
.getContent()
วิธีการเนื่องจาก 304 การตอบสนองไม่มีเนื้อหาการตอบสนองที่เกี่ยวข้องตามมาตรฐาน RFC
แปลงเป็น Bitmaps แล้วเก็บไว้ใน Collection (HashMap, List เป็นต้น) หรือคุณสามารถเขียนมันลงใน SDcard
เมื่อจัดเก็บไว้ในพื้นที่แอปพลิเคชันโดยใช้วิธีแรกคุณอาจต้องการล้อมรอบด้วยjava.lang.ref.SoftReferenceโดยเฉพาะถ้าจำนวนของพวกเขามีขนาดใหญ่ (เพื่อให้พวกเขาเก็บขยะในช่วงวิกฤต) นี่อาจเป็นเพราะการโหลดซ้ำ
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
การเขียนลง SDcard นั้นไม่จำเป็นต้องโหลดซ้ำ เพียงแค่สิทธิ์ของผู้ใช้
Uri
อ้างอิงเส้นทางที่คุณสามารถส่งต่อImageView
และมุมมองที่กำหนดเองอื่น ๆ เพราะทุกครั้งที่คุณcompress
คุณจะสูญเสียคุณภาพ แน่นอนว่านี่เป็นความจริงสำหรับอัลกอริทึมที่สูญเสียเท่านั้น วิธีนี้จะช่วยให้คุณสามารถเก็บแฮชของไฟล์และใช้ในครั้งถัดไปที่คุณขอไฟล์จากเซิร์ฟเวอร์ผ่านIf-None-Match
และETag
ส่วนหัว
ใช้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 จากActivity
Class ใช้ 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
if(cache == null)
ที่แก้ปัญหาของฉัน! :)
ในการดาวน์โหลดภาพและบันทึกลงในการ์ดหน่วยความจำคุณสามารถทำได้เช่นนี้
//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" />
ฉันจะพิจารณาใช้แคชรูปภาพของ droidfu มันใช้ทั้งแคชรูปภาพในหน่วยความจำและดิสก์ คุณยังได้รับ WebImageView ที่ใช้ประโยชน์จากไลบรารี ImageCache
นี่คือคำอธิบายแบบเต็มของ droidfu และ WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
ฉันได้ลอง SoftReferences แล้วพวกเขาก็มีการเรียกคืนอุปกรณ์อย่างรุนแรงใน Android ที่ฉันรู้สึกว่าไม่มีประโยชน์ในการใช้
SoftReference
s พวกเขาแนะนำให้ใช้LruCache
แทน
ตามที่ Thunder Rabbit แนะนำไว้ ImageDownloader นั้นดีที่สุดสำหรับงานนี้ ฉันยังพบความแตกต่างเล็กน้อยของชั้นเรียนที่:
http://theandroidcoder.com/utilities/android-image-download-and-caching/
ข้อแตกต่างที่สำคัญระหว่างทั้งสองคือ ImageDownloader ใช้ระบบแคช Android และอุปกรณ์ที่ถูกดัดแปลงใช้หน่วยความจำภายในและภายนอกเป็นตัวแคชทำให้ภาพที่เก็บไว้ไม่สิ้นสุดหรือจนกว่าผู้ใช้จะลบมันออกด้วยตนเอง ผู้เขียนยังกล่าวถึงความเข้ากันได้กับ Android 2.1
นี่เป็นสิ่งที่ 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
ฉันยังคิดว่านี่เป็นทางออกที่ดีสำหรับเส้นทางนี้ - แต่คุณยังต้องเขียนแคช ฟังดูสนุก แต่ฉันอยากจะเขียนคุณสมบัติ
มีรายการพิเศษในส่วนการฝึกอบรมอย่างเป็นทางการของ 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) ข้อมูลสำรองของบิตแมปถูกเก็บไว้ในหน่วยความจำดั้งเดิมซึ่งไม่ได้เผยแพร่ในลักษณะที่สามารถคาดการณ์ได้ซึ่งอาจทำให้แอปพลิเคชันเกินขีด จำกัด หน่วยความจำ
พิจารณาการใช้ยูนิเวอร์แซห้องสมุดภาพ LoaderโดยSergey Tarasevich มันมาพร้อมกับ:
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
ตัวอย่างนี้ใช้ค่าเริ่มต้น
สิ่งที่ใช้งานได้จริงสำหรับฉันคือการตั้งค่า 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
libs-for-android ของ Google มีห้องสมุดที่ดีสำหรับจัดการรูปภาพและแคชไฟล์
ฉันเคยต่อสู้กับสิ่งนี้มาระยะหนึ่งแล้ว คำตอบที่ใช้ SoftReferences จะทำให้ข้อมูลสูญหายเร็วเกินไป คำตอบที่แนะนำให้สร้างอินสแตนซ์ของ RequestCache นั้นยุ่งเกินไปแถมฉันไม่สามารถหาตัวอย่างได้เลย
แต่ImageDownloader.javaทำงานได้อย่างยอดเยี่ยมสำหรับฉัน มันใช้ HashMap จนกว่าจะถึงความจุหรือจนกว่าจะหมดเวลาการล้างจึงเกิดสิ่งต่างๆขึ้นมาที่ SoftReference ดังนั้นจึงใช้สิ่งที่ดีที่สุดของทั้งสองโลก
ฉันขอแนะนำจุดระเบิดนี้ดีกว่า Droid fu
https://github.com/kaeppler/ignition
https://github.com/kaeppler/ignition/wiki/Sample-applications
แม้ภายหลังคำตอบ แต่ฉันเขียน Android Image Manager ที่จัดการแคชโปร่งใส (หน่วยความจำและดิสก์) รหัสนี้อยู่บน Github https://github.com/felipecsl/Android-ImageManager
คำตอบปลาย แต่ฉันคิดว่าฉันควรจะเพิ่มการเชื่อมโยงไปยังเว็บไซต์ของฉันเพราะฉันได้เขียนกวดวิชาวิธีการทำแคชรูปภาพสำหรับ Android: http://squarewolf.nl/2010/11/android-image-cache/ ปรับปรุง:หน้าถูกออฟไลน์เนื่องจากที่มาล้าสมัย ฉันเข้าร่วม @elenasys ในคำแนะนำของเธอที่จะใช้ระบบจุดระเบิด
ดังนั้นสำหรับทุกคนที่สะดุดกับคำถามนี้และไม่พบวิธีแก้ปัญหา: หวังว่าคุณจะสนุก! = D
คำตอบหลังเวลาที่กำหนด แต่ฉันคิดว่าห้องสมุดนี้จะช่วยให้มากกับแคชภาพ: 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