2013-02-03 31 views
6

Tôi có ViewPager mà tôi sử dụng để hiển thị hình ảnh có thể thu phóng (sử dụng ImageViewTouch). Tôi cần tải các bitmap lớn từ Internet (http). Lớn, tôi có nghĩa là 2000x1000. Các hình ảnh cần phải quá lớn, vì chúng có thể phóng to và cần hiển thị chi tiết. Hình ảnh trên máy chủ là định dạng .jpg, nhưng nó không phải là vấn đề - tôi có thể thay đổi nó.Tải bitmap lớn vào ImageView trong ViewPager - hết bộ nhớ

Làm cách nào tôi có thể quản lý để tải những hình ảnh quá lớn đến ImageViewTouch (ImageView) mà không nhận được sự cố với bộ nhớ?

Bởi bây giờ tôi đang sử dụng đơn giản này (AsyncTask):

ImageView currentView; //ImageView where to place image loaded from Internet 
    String ImageUrl; //URL of image to load 

    protected Bitmap doInBackground(ArrayList... params) { 

      Bitmap bitmap; 
      InputStream in = null; 
      imageUrl = (String)params[0].get(1); 

      try{ 
       HttpClient httpclient = new DefaultHttpClient(); 
       HttpResponse response = httpclient.execute(new HttpGet(imageUrl)); 
       in = response.getEntity().getContent(); 
      } catch(Exception e){ 
       e.printStackTrace(); 
      } 
      try { 
       bitmapa = BitmapFactory.decodeStream(in); 
       in.close(); 
      } catch (IOException e1) { 
       e1.printStackTrace(); 
      } 

      return bitmap; 
     } 


     @Override 
     protected void onPostExecute(Bitmap bitmap) { 

      currentView.setImageBitmap(bitmap); 
     } 

Và nó gây ra nhiều vấn đề với bộ nhớ:

E/dalvikvm-heap(369): 69560740-byte external allocation too large for this process. 
E/GraphicsJNI(369): VM won't let us allocate 69560740 bytes 

hoặc

E/AndroidRuntime(369): java.lang.RuntimeException: An error occured while executing doInBackground() 
E/AndroidRuntime(369): at android.os.AsyncTask$3.done(AsyncTask.java:200) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask.setException(FutureTask.java:125) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask.run(FutureTask.java:138) 
E/AndroidRuntime(369): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) 
E/AndroidRuntime(369): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) 
E/AndroidRuntime(369): at java.lang.Thread.run(Thread.java:1019) 
E/AndroidRuntime(369): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
E/AndroidRuntime(369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
E/AndroidRuntime(369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470) 
E/AndroidRuntime(369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:525) 
E/AndroidRuntime(369): at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:78) 
E/AndroidRuntime(369): at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:1) 
E/AndroidRuntime(369): at android.os.AsyncTask$2.call(AsyncTask.java:185) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306) 
E/AndroidRuntime(369): ... 4 more 
+0

http: // stackoverflow. com/a/24135283/294884 – Fattie

Trả lời

2

Phiên bản nào của Hệ điều hành để bạn kiểm tra này trên? Trên các phiên bản trước, không gian heap có sẵn thấp hơn nhiều so với các phiên bản sau.

Tôi nghiêm túc xem xét việc giảm kích thước bitmap của bạn để tránh điều này. Nếu bạn đang tải xuống 2000x1000 Bitmap cho điện thoại mdpi thì đây có thể là một ý tưởng tồi.

Tôi cũng khuyên bạn nên đọc this article để biết thêm thông tin về cách tải hình ảnh thích hợp chính xác cho một số cụ thể ImageView, dựa trên kích thước của nó.

Cuối cùng, bạn nên luôn luôn đóng một InputStream trong một khối finally:

try { 
     bitmap = BitmapFactory.decodeStream(in); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     if (in != null) { in.close(); } 
    } 

Ngoài ra còn có android:largeHeap="true" mà bạn có thể cân nhắc, đặc biệt là làm cho các ứng dụng mà đối phó với bitmap lớn, chẳng hạn như các ứng dụng chỉnh sửa ảnh.

+0

Cảm ơn bạn đã trả lời. Phiên bản SDK mục tiêu của tôi là 15 và tối thiểu là 4. Tôi đã đọc bài viết đó nhưng nó không giúp tôi - vấn đề ở đây là tôi không thể thay đổi kích thước hoặc chia tỷ lệ hình ảnh của mình vì nó phải ở chế độ thu phóng và hiển thị chi tiết khi thu nhỏ. – michalsol

+0

Tôi sẽ không nói dối bạn, bạn sẽ không tìm thấy câu trả lời có thể để phù hợp với hình ảnh 2000x1000 trong bộ nhớ. Hãy suy nghĩ về nó, không có vấn đề gì bạn làm bạn sẽ vượt quá heapspace có sẵn.Có lẽ bạn có thể tìm thấy một chiến lược khác tương tự như cách trình duyệt web Chrome hoạt động, chỉ hiển thị một phần của hình ảnh trong bộ nhớ, khi người dùng xung quanh bạn tải lại nhanh cửa sổ mới dựa trên chế độ xem hiện tại. – dnkoutso

+0

Tôi nghĩ về điều đó - tải lại phần thu phóng của hình ảnh - nhưng có vẻ khá khó thực hiện. Bạn có hướng dẫn nào không? Cảm ơn! – michalsol

2

Hình ảnh pixel 2000x1000 lớn, nhưng không lớn.

Tôi thấy, mặc dù quá trình này đang cố gắng phân bổ 69560740 byte trước khi nó chết, đó là khoảng 66MByte!

Trường hợp xấu nhất của bạn là 2000x1000, khoảng 2000x1000x4 = 8MByte, cách nhỏ hơn 66MByte. Có một cái gì đó đang xảy ra.

Ngoài ra, tôi đã tìm thấy sự cố khi sử dụng Bitmap làm giá trị trả về/kết quả trong AsyncTasks. AsyncTasks đã hoàn thành vẫn giữ kết quả của họ cho đến khi chúng được thu thập rác. Vì bạn không kiểm soát bộ sưu tập khổng lồ của các cá thể AsyncTask, không sử dụng Bitmap làm giá trị trả về/kết quả. Thay vì đặt Bitmap trong một lĩnh vực và gán nó khi doInBackground sắp kết thúc, và sử dụng trường này để có được những Bitmap trong onPostExecute và thiết lập lĩnh vực này để null (!):

Bitmap bitmap; 
protected Void doInBackground(ArrayList... params) { 

     InputStream in = null; 
     imageUrl = (String)params[0].get(1); 

     try{ 
      HttpClient httpclient = new DefaultHttpClient(); 
      HttpResponse response = httpclient.execute(new HttpGet(imageUrl)); 
      in = response.getEntity().getContent(); 
     } catch(Exception e){ 
      e.printStackTrace(); 
     } 
     try { 
      bitmap = BitmapFactory.decodeStream(in); 
      in.close(); 
     } catch (IOException e1) { 
      e1.printStackTrace(); 
     } 

     return null; 
    } 


    @Override 
    protected void onPostExecute(Void unused) { 
     if (bitmap != null) { 
      currentView.setImageBitmap(bitmap); 
     } 

     // And don't forget to null the bitmap field! 
     bitmap = null; 
    } 
+0

Là một nhận xét bổ sung cho câu trả lời trước của tôi. Tôi nghĩ rằng máy chủ của bạn trả về các hình ảnh rất lớn, vì VM cố gắng phân bổ 69.500.000 byte. Đây sẽ là hình ảnh ARGB_8888 gần 3000x3000 pixel. –

+0

Giải pháp này thực sự cải thiện việc sử dụng bộ nhớ của tôi. Tôi chỉ làm những gì bạn nói, bắt đầu với "Ngoài ra, tôi đã tìm thấy sự cố khi sử dụng Bitmap làm giá trị trả lại/kết quả trong AsyncTasks ......." Tôi đã sửa đổi mã của mình như bạn đã nói và bây giờ tôi có thể hiển thị gấp x2 lần hình ảnh mà không gặp sự cố. Cảm ơn bạn –

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