2010-02-08 48 views
29

Tôi có một loạt URL hình ảnh. Tôi phải tải xuống những hình ảnh này và hiển thị chúng trong ứng dụng của tôi từng cái một. Tôi đang lưu hình ảnh trong Bộ sưu tập bằng cách sử dụng SoftReferences và cũng trên Sdcard để tránh tìm nạp lại và cải thiện trải nghiệm người dùng.Xử lý các ảnh bitmap lớn

Vấn đề là tôi không biết gì về kích thước của bitmap. Và khi nó quay ra, tôi nhận được OutOfMemoryExceptions rải rác, khi tôi đang sử dụng phương pháp BitmapFactory.decodeStream(InputStream). Vì vậy, tôi đã chọn downsample các hình ảnh bằng cách sử dụng BitmapFactory Options (mẫu size = 2). Điều này cho kết quả tốt hơn: không có OOM, nhưng điều này ảnh hưởng đến chất lượng của hình ảnh nhỏ hơn.

Tôi nên xử lý các trường hợp như thế nào? Có cách nào để chọn lọc xuống chỉ các hình ảnh có độ phân giải cao không?

+0

Trang web chính thức của [Đào tạo Android] (http://developer.android.com/training/index.html) hiện có một lớp được gọi là [Hiển thị bitmap hiệu quả] (http://developer.android.com/training/ display-bitmaps/index.html) với một bài học, [Tải các bitmap lớn một cách hiệu quả] (http://developer.android.com/training/displaying-bitmaps/load-bitmap.html) mà đi qua điều này. Bài học [Caching Bitmaps] (http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html) cũng bao gồm một số thông tin về bộ nhớ đệm bằng bộ nhớ cache tham chiếu cứng thay vì WeakReference hoặc SoftReference. – AdamK

Trả lời

55

Có một tùy chọn trong BitmapFactory.Options lớp (một tôi bỏ qua) có tên inJustDecodeBounds, javadoc trong đó lần đọc:

Nếu thiết lập là true, các bộ giải mã sẽ trở lại null (không bitmap), nhưng ra ... các trường vẫn sẽ được đặt, cho phép người gọi truy vấn bitmap mà không phải phân bổ bộ nhớ cho pixel của nó.

Tôi đã sử dụng nó để tìm hiểu kích thước thực tế của Bitmap và sau đó chọn xuống mẫu bằng cách sử dụng tùy chọn inSampleSize. Điều này ít nhất tránh bất kỳ lỗi OOM nào khi giải mã tệp.

tham khảo:
1. Handling larger Bitmaps
2. How do I get Bitmap info before I decode

+9

+1 để trả lời câu hỏi của bạn khi bạn phát hiện ra câu trả lời; nghĩ về những người google và tìm câu hỏi của bạn, nhưng không có câu trả lời. – Will

+0

Nó thực sự hoạt động tốt nếu tôi muốn để có được kích thước, nhưng tôi nhận được lỗi OOM khi tôi muốn giải mã một tập tin để bitmap và thiết lập bitmap đó để ImageView. Vui lòng cho tôi biết cách tối ưu hóa nó hoặc bất kỳ cách nào khác để khắc phục vấn đề này. –

12

Những gì tôi đã làm bản thân mình là:

  • sử dụng inJustDecodeBounds để có được kích thước ban đầu của hình ảnh
  • có một cố định Bề mặt tối đa để tải Bitmap (nói 1Mpixels)
  • kiểm tra xem bề mặt hình ảnh có dưới giới hạn hay không, nếu có, hãy tải trực tiếp
  • nếu không tính chiều rộng và chiều cao lý tưởng mà tại đó bạn nên tải Bitmap để ở bên dưới bề mặt tối đa (có một số phép toán đơn giản để thực hiện tại đây). Điều này cung cấp cho bạn tỷ lệ nổi mà bạn muốn áp dụng khi tải bitmap
  • bây giờ bạn muốn dịch tỷ lệ này thành phù hợp inSampleSize (công suất 2 không làm giảm chất lượng hình ảnh). Tôi sử dụng chức năng này:
 
int k = Integer.highestOneBit((int)Math.floor(ratio)); 
if(k==0) return 1; 
else return k; 
  • sau đó vì Bitmap sẽ được nạp với độ phân giải cao hơn một chút so với bề mặt tối đa (vì bạn phải sử dụng một sức mạnh nhỏ hơn 2), bạn sẽ phải để thay đổi kích thước Bitmap, nhưng nó sẽ nhanh hơn nhiều.
+0

cảm ơn bạn đã thêm thường trình để tính cỡ mẫu. Nó làm tăng luồng với một phần thông tin quan trọng. – Samuh

11

Sau một vài ngày vật lộn để tránh tất cả các lỗi OutOfMemory mà tôi đã nhận được với các thiết bị khác nhau, tôi tạo này:

private Bitmap getDownsampledBitmap(Context ctx, Uri uri, int targetWidth, int targetHeight) { 
    Bitmap bitmap = null; 
    try { 
     BitmapFactory.Options outDimens = getBitmapDimensions(uri); 

     int sampleSize = calculateSampleSize(outDimens.outWidth, outDimens.outHeight, targetWidth, targetHeight); 

     bitmap = downsampleBitmap(uri, sampleSize); 

    } catch (Exception e) { 
     //handle the exception(s) 
    } 

    return bitmap; 
} 

private BitmapFactory.Options getBitmapDimensions(Uri uri) throws FileNotFoundException, IOException { 
    BitmapFactory.Options outDimens = new BitmapFactory.Options(); 
    outDimens.inJustDecodeBounds = true; // the decoder will return null (no bitmap) 

    InputStream is= getContentResolver().openInputStream(uri); 
    // if Options requested only the size will be returned 
    BitmapFactory.decodeStream(is, null, outDimens); 
    is.close(); 

    return outDimens; 
} 

private int calculateSampleSize(int width, int height, int targetWidth, int targetHeight) { 
    int inSampleSize = 1; 

    if (height > targetHeight || width > targetWidth) { 

     // Calculate ratios of height and width to requested height and 
     // width 
     final int heightRatio = Math.round((float) height 
       /(float) targetHeight); 
     final int widthRatio = Math.round((float) width/(float) targetWidth); 

     // Choose the smallest ratio as inSampleSize value, this will 
     // guarantee 
     // a final image with both dimensions larger than or equal to the 
     // requested height and width. 
     inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 
    } 
    return inSampleSize; 
} 

private Bitmap downsampleBitmap(Uri uri, int sampleSize) throws FileNotFoundException, IOException { 
    Bitmap resizedBitmap; 
    BitmapFactory.Options outBitmap = new BitmapFactory.Options(); 
    outBitmap.inJustDecodeBounds = false; // the decoder will return a bitmap 
    outBitmap.inSampleSize = sampleSize; 

    InputStream is = getContentResolver().openInputStream(uri); 
    resizedBitmap = BitmapFactory.decodeStream(is, null, outBitmap); 
    is.close(); 

    return resizedBitmap; 
} 

Phương pháp này làm việc với tất cả các thiết bị tôi kiểm tra, nhưng tôi nghĩ rằng chất lượng có thể tốt hơn bằng cách sử dụng quá trình khác mà tôi không biết.

Tôi hy vọng mã của mình có thể giúp các nhà phát triển khác trong cùng một tình huống. Tôi cũng đánh giá cao nếu một nhà phát triển cao cấp có thể giúp đỡ, đưa ra một gợi ý về quá trình khác để tránh mất (ít hơn) chất lượng trong quá trình này.

+1

Nội dung hay. Nhưng calculateSampleSize đã sai. Tôi hiện đã cập nhật bài đăng để khắc phục. – Zammbi

+1

Một tác phẩm rất hay. Cảm ơn bạn đã chia sẻ! – MacD

Các vấn đề liên quan