Làm cách nào tôi có thể lưu trữ hình ảnh sau khi chúng được tải xuống từ web?
Làm cách nào tôi có thể lưu trữ hình ảnh sau khi chúng được tải xuống từ web?
Câu trả lời:
Và bây giờ là punchline: sử dụng bộ đệm hệ thống.
URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
Bitmap bitmap = (Bitmap)response;
}
Cung cấp cả bộ nhớ và bộ nhớ cache flash-rom, được chia sẻ với trình duyệt.
grr. Tôi ước ai đó đã nói với tôi rằng trước khi tôi viết trình quản lý bộ đệm của riêng mình.
connection.getContent()
luôn trả về InputStream cho tôi, tôi đang làm gì sai?
Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
Về connection.setUseCaches
giải pháp tao nhã ở trên: thật đáng buồn, nó sẽ không hoạt động nếu không có thêm nỗ lực. Bạn sẽ cần phải cài đặt ResponseCache
bằng cách sử dụng ResponseCache.setDefault
. Nếu không, HttpURLConnection
sẽ âm thầm bỏ quasetUseCaches(true)
bit.
Xem các bình luận ở đầu FileResponseCache.java
để biết chi tiết:
(Tôi sẽ đăng bài này trong một bình luận, nhưng rõ ràng tôi không có đủ nghiệp lực.)
HttpResponseCache
, bạn có thể tìm thấy HttpResponseCache.getHitCount()
trả về 0. Tôi không chắc nhưng tôi nghĩ đó là vì máy chủ web bạn yêu cầu không sử dụng các tiêu đề bộ đệm trong trường hợp đó. Để làm cho bộ nhớ đệm hoạt động nào, sử dụng connection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
.
.getContent()
phương thức vì các phản hồi 304 không có phần phản hồi liên quan theo tiêu chuẩn RFC.
Chuyển đổi chúng thành Bitmap và sau đó lưu trữ chúng trong Bộ sưu tập (HashMap, Danh sách, v.v.) hoặc bạn có thể viết chúng trên SDcard.
Khi lưu trữ chúng trong không gian ứng dụng bằng cách sử dụng phương pháp đầu tiên, bạn có thể muốn bọc chúng xung quanh java.lang.ref.SoftReference nếu số lượng của chúng lớn (để chúng được thu gom rác trong khủng hoảng). Điều này có thể xảy ra sau đó Tải lại.
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
viết chúng trên SDcard sẽ không yêu cầu Tải lại; chỉ cần sự cho phép của người dùng
Uri
tham chiếu đường dẫn mà bạn có thể chuyển đến ImageView
và các chế độ xem tùy chỉnh khác. Bởi vì mỗi lần bạn compress
, bạn sẽ mất chất lượng. Tất nhiên điều này chỉ đúng với các thuật toán mất mát. Phương pháp này cũng sẽ cho phép bạn thậm chí lưu trữ một hàm băm của tệp và sử dụng nó vào lần tới khi bạn yêu cầu tệp từ máy chủ thông qua If-None-Match
và ETag
các tiêu đề.
Sử dụng LruCache
để lưu trữ hình ảnh hiệu quả. Bạn có thể đọc về LruCache
từ trang web Nhà phát triển Android
Tôi đã sử dụng giải pháp dưới đây để tải xuống hình ảnh và bộ nhớ đệm trong Android. Bạn có thể làm theo các bước dưới đây:
BƯỚC 1:
làm cho lớp được đặt tên ImagesCache
. Tôi đã sử dụngSingleton 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();
}
}
}
BƯỚC 2:
tạo một lớp khác có tên DownloadImageTask được sử dụng nếu bitmap không có sẵn trong bộ đệm, nó sẽ tải xuống từ đây:
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;
}
BƯỚC 3: Sử dụng từ Activity
hoặcAdapter
Lưu ý: Nếu bạn muốn tải hình ảnh từ url từ Activity
Class. Sử dụng Trình xây dựng thứ hai của DownloadImageTask
, nhưng nếu bạn muốn hiển thị hình ảnh từ Adapter
sử dụng Trình xây dựng thứ nhất của DownloadImageTask
(ví dụ: bạn có một hình ảnh trong ListView
và bạn đang đặt hình ảnh từ 'Bộ điều hợp')
SỬ DỤNG TỪ HOẠT ĐỘNG:
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);
}
SỬ DỤNG TỪ QUẢNG CÁO:
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);
}
Ghi chú:
cache.initializeCache()
bạn có thể sử dụng câu lệnh này trong Hoạt động đầu tiên của ứng dụng của bạn. Khi bạn đã khởi tạo bộ đệm, bạn sẽ không bao giờ cần phải khởi tạo nó mỗi lần nếu bạn đang sử dụng ImagesCache
thể hiện.
Tôi không bao giờ giỏi trong việc giải thích mọi thứ nhưng hy vọng điều này sẽ giúp những người mới bắt đầu biết cách sử dụng bộ đệm LruCache
và cách sử dụng :)
BIÊN TẬP:
Ngày nay, có những thư viện rất nổi tiếng được biết đến Picasso
và Glide
có thể được sử dụng để tải hình ảnh rất hiệu quả trong ứng dụng Android. Hãy thử thư viện Picasso rất đơn giản và hữu ích này cho Android và Glide For Android . Bạn không cần phải lo lắng về hình ảnh bộ nhớ cache.
Picasso cho phép tải hình ảnh không rắc rối trong ứng dụng của bạn thường xuyên trong một dòng mã!
Glide, giống như Picasso, có thể tải và hiển thị hình ảnh từ nhiều nguồn, đồng thời chăm sóc bộ nhớ đệm và giữ tác động bộ nhớ thấp khi thực hiện các thao tác hình ảnh. Nó đã được sử dụng bởi các ứng dụng chính thức của Google (như ứng dụng cho Google I / O 2015) và cũng phổ biến như Picasso. Trong loạt bài này, chúng ta sẽ khám phá sự khác biệt và lợi thế của Glide so với Picasso.
Bạn cũng có thể truy cập blog để biết sự khác biệt giữa Glide và Picasso
if(cache == null)
đó đã giải quyết vấn đề của tôi! :)
Để tải xuống một hình ảnh và lưu vào thẻ nhớ, bạn có thể làm như thế này.
//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));
Đừng quên thêm quyền Internet vào bảng kê khai của bạn:
<uses-permission android:name="android.permission.INTERNET" />
Tôi sẽ xem xét sử dụng bộ nhớ cache hình ảnh của droidfu. Nó thực hiện cả bộ nhớ cache hình ảnh trong bộ nhớ và trên đĩa. Bạn cũng nhận được một WebImageView tận dụng thư viện ImageCache.
Dưới đây là mô tả đầy đủ về droidfu và WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryad CHƯƠNG /
Tôi đã dùng thử SoftReferences, chúng được khai thác quá mạnh trong Android mà tôi cảm thấy không có điểm nào sử dụng chúng
SoftReference
s. Họ khuyên bạn nên sử dụng LruCache
thay thế.
Như Thunder Rabbit đã đề xuất, ImageDoader là ứng dụng tốt nhất cho công việc. Tôi cũng tìm thấy một biến thể nhỏ của lớp tại:
http://theandroidcoder.com/utilities/android-image-doad-and-caching/
Sự khác biệt chính giữa hai cái đó là ImageDoader sử dụng hệ thống bộ nhớ đệm Android và cái được sửa đổi sử dụng bộ nhớ trong và ngoài như bộ nhớ đệm, giữ hình ảnh được lưu trong bộ nhớ cache vô thời hạn hoặc cho đến khi người dùng xóa nó bằng tay. Tác giả cũng đề cập đến khả năng tương thích Android 2.1.
Đây là một bắt tốt của Joe. Ví dụ mã ở trên có hai vấn đề - một - đối tượng phản hồi không phải là một phiên bản của Bitmap (khi URL của tôi tham chiếu một jpg, như http: \ website.com \ image.jpg, đó là một
org.apache.harmony.luni.iternal.net.www.protatio.http.HttpURLConnectionImpl $ LimitedInputStream).
Thứ hai, như Joe chỉ ra, không có bộ đệm nào xảy ra mà không có bộ đệm phản hồi được cấu hình. Các nhà phát triển Android còn lại để cuộn bộ nhớ cache của riêng họ. Đây là một ví dụ để làm như vậy, nhưng nó chỉ lưu trữ trong bộ nhớ, đây thực sự không phải là giải pháp đầy đủ.
http://codebycoffee.com/2010/06/29/USE-responsecache-in-an-android-app/
API bộ đệm URLConnection được mô tả ở đây:
http://doad.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
Tôi vẫn nghĩ rằng đây là một giải pháp OK để đi theo con đường này - nhưng bạn vẫn phải viết một bộ đệm. Nghe có vẻ vui, nhưng tôi thích viết các tính năng hơn.
Có một mục đặc biệt trên phần đào tạo chính thức của Android về điều này: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Phần này khá mới, nó không có ở đó khi câu hỏi được hỏi.
Giải pháp được đề xuất là sử dụng LruCache. Lớp đó đã được giới thiệu trên Honeycomb, nhưng nó cũng được bao gồm trong thư viện tương thích.
Bạn có thể khởi tạo LruCache bằng cách đặt số lượng tối đa hoặc các mục nhập và nó sẽ tự động sắp xếp chúng theo bạn và làm sạch chúng ít sử dụng hơn khi bạn vượt quá giới hạn. Ngoài ra, nó được sử dụng như một Bản đồ bình thường.
Mã mẫu từ trang chính thức:
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 trước đây là một lựa chọn tốt, nhưng không còn nữa, trích dẫn từ trang chính thức:
Lưu ý: Trước đây, việc triển khai bộ đệm bộ nhớ phổ biến là bộ đệm bitmap SoftReference hoặc WeakReference, tuy nhiên điều này không được khuyến khích. Bắt đầu từ Android 2.3 (API cấp 9), trình thu gom rác tích cực hơn với việc thu thập các tham chiếu mềm / yếu khiến chúng khá kém hiệu quả. Ngoài ra, trước Android 3.0 (API cấp 11), dữ liệu sao lưu của bitmap được lưu trữ trong bộ nhớ riêng không được phát hành theo cách có thể dự đoán được, có khả năng khiến ứng dụng vượt quá giới hạn bộ nhớ và sự cố.
Cân nhắc sử dụng thư viện Universal Image Loader của Sergey Tarasevich . Nó đi kèm với:
Universal Image Loader cho phép quản lý bộ đệm chi tiết cho các hình ảnh được tải xuống, với các cấu hình bộ đệm sau:
UsingFreqLimitedMemoryCache
: Bitmap ít được sử dụng nhất sẽ bị xóa khi vượt quá giới hạn kích thước bộ đệm.LRULimitedMemoryCache
: Bitmap ít được sử dụng gần đây nhất sẽ bị xóa khi vượt quá giới hạn kích thước bộ đệm.FIFOLimitedMemoryCache
: Quy tắc FIFO được sử dụng để xóa khi vượt quá giới hạn kích thước bộ đệm.LargestLimitedMemoryCache
: Bitmap lớn nhất bị xóa khi vượt quá giới hạn kích thước bộ đệm.LimitedAgeMemoryCache
: Đối tượng được lưu bị xóa khi tuổi của nó vượt quá giá trị được xác định .WeakMemoryCache
: Bộ nhớ cache chỉ có các tham chiếu yếu đến bitmap.Một ví dụ sử dụng đơn giản:
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);
Ví dụ này sử dụng mặc định UsingFreqLimitedMemoryCache
.
Điều thực sự hiệu quả với tôi là thiết lập FeedbackCache trên lớp Main của tôi:
try {
File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { }
và
connection.setUseCaches(true);
khi tải bitmap.
http://prrealdroid.blogspot.com/2013/01/utilizing-http-response-cache.html
Libs-for-android của Google có một thư viện đẹp để quản lý bộ đệm hình ảnh và tệp.
Tôi đã vật lộn với điều này một thời gian; các câu trả lời sử dụng SoftReferences sẽ mất dữ liệu của họ quá nhanh. Các câu trả lời gợi ý việc tạo một RequestCache quá lộn xộn, cộng với tôi không bao giờ có thể tìm thấy một ví dụ đầy đủ.
Nhưng ImageDoader.java hoạt động tuyệt vời đối với tôi. Nó sử dụng HashMap cho đến khi đạt được dung lượng hoặc cho đến khi hết thời gian thanh lọc, sau đó mọi thứ được chuyển sang SoftReference, do đó sử dụng tốt nhất cả hai thế giới.
Tôi đề nghị IGNITION điều này thậm chí còn tốt hơn Droid fu
Ngay cả câu trả lời sau đó, nhưng tôi đã viết Trình quản lý hình ảnh Android xử lý bộ nhớ đệm trong suốt (bộ nhớ và đĩa). Mã này có trên Github https://github.com/felipecsl/Android-ImageManager
Cuối câu trả lời, nhưng tôi figured tôi nên thêm một liên kết đến trang web của tôi bởi vì tôi đã viết một hướng dẫn làm thế nào để thực hiện một bộ nhớ cache hình ảnh cho android: http://squarewolf.nl/2010/11/android-image-cache/ Cập nhật: các trang đã được ngoại tuyến vì nguồn đã lỗi thời. Tôi tham gia @elenasys trong lời khuyên của cô ấy để sử dụng Ignition .
Vì vậy, với tất cả những người vấp phải câu hỏi này và chưa tìm ra giải pháp: hy vọng bạn thích! = D
Trả lời muộn nhưng tôi nghĩ thư viện này sẽ giúp ích rất nhiều cho hình ảnh lưu trữ: https://github.com/crypticminds/ColdStorage .
Chỉ cần chú thích ImageView bằng @LoadCache (R.id.id_of_my_image_view, "URL_to_downlaod_image_from) và bạn sẽ phải tải xuống hình ảnh và tải hình ảnh vào chế độ xem hình ảnh. Bạn cũng có thể chỉ định hình ảnh giữ chỗ và tải hình ảnh.
Tài liệu chi tiết của chú thích có mặt tại đây: - https://github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation