Android: Bitmap recycle () hoạt động như thế nào?


89

Giả sử tôi đã tải một hình ảnh trong một đối tượng bitmap như

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Bây giờ, điều gì sẽ xảy ra nếu tôi tải một bitmap khác như

myBitmap = BitmapFactory.decodeFile(myFile2);

Điều gì xảy ra với myBitmap đầu tiên? Nó có được Thu thập rác hay tôi phải thu gom rác theo cách thủ công trước khi tải một bitmap khác, ví dụ:. myBitmap.recycle()?

Ngoài ra, có cách nào tốt hơn để tải các hình ảnh lớn và hiển thị lần lượt trong khi tái chế trên đường không?

Câu trả lời:


79

Bitmap đầu tiên không được thu thập rác khi bạn giải mã cái thứ hai. Garbage Collector sẽ làm điều đó sau bất cứ khi nào nó quyết định. Nếu bạn muốn giải phóng bộ nhớ càng sớm càng tốt, bạn nên gọi recycle()ngay trước khi giải mã bitmap thứ hai.

Nếu bạn muốn tải hình ảnh thực sự lớn, bạn nên lấy mẫu lại. Đây là một ví dụ: Sự cố hết bộ nhớ kỳ lạ khi tải hình ảnh vào đối tượng Bitmap .


23

Tôi nghĩ vấn đề là thế này: Trên các phiên bản Android trước Honeycomb, dữ liệu bitmap thô thực tế không được lưu trữ trong bộ nhớ máy ảo mà thay vào đó trong bộ nhớ gốc. Bộ nhớ gốc này được giải phóng khi Bitmapđối tượng java tương ứng là GC'd.

Tuy nhiên , khi bạn sử dụng hết bộ nhớ gốc, dalvik GC không được kích hoạt, vì vậy có thể ứng dụng của bạn sử dụng rất ít bộ nhớ java, vì vậy dalvik GC không bao giờ được gọi, nhưng nó sử dụng rất nhiều bộ nhớ riêng cho bitmap mà cuối cùng gây ra lỗi OOM.

Ít nhất đó là suy đoán của tôi. Rất may trong Honeycomb trở lên, tất cả dữ liệu bitmap đều được lưu trữ trong VM nên bạn hoàn toàn không phải sử dụng recycle(). Nhưng đối với hàng triệu 2,3 ​​người dùng (phân mảnh rung chuyển ), bạn nên sử dụng recycle()bất cứ khi nào có thể (một rắc rối lớn). Hoặc cách khác, bạn có thể gọi GC thay thế.


21

Bạn sẽ cần gọi myBitmap.recycle () trước khi tải hình ảnh tiếp theo.

Tùy thuộc vào nguồn myFile của bạn (Ví dụ: nếu đó là thứ bạn không kiểm soát được kích thước ban đầu), khi tải một hình ảnh thay vì chỉ đơn giản lấy lại mẫu một số tùy ý, bạn nên chia tỷ lệ hình ảnh theo kích thước hiển thị.

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

Tôi lưu cache displayWidth & displayHeight ở trạng thái tĩnh mà tôi đã khởi tạo khi bắt đầu Hoạt động của mình.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();

3
Bạn không cần gọi recycle (), chỉ cần bạn muốn giải phóng bộ nhớ ngay lập tức là một ý kiến ​​hay.
Karu

13
Câu trả lời được chấp nhận là "Nếu bạn muốn giải phóng bộ nhớ càng sớm càng tốt, bạn nên gọi recycle ()". Câu trả lời của bạn là "Bạn sẽ cần gọi myBitmap.recycle ()". Có sự khác biệt giữa "should" và "need to", và cái sau không chính xác trong trường hợp này.
Karu,

1
Bối cảnh là quan trọng. Câu hỏi đặt ra là "Ngoài ra, có cách nào tốt hơn để tải các hình ảnh lớn và hiển thị chúng lần lượt tái chế trên đường".
djunod

3
Đối với Android 4.1, ví dụ trên có thể bị hỏng vì createScaledBitmap có thể trả về trong một số trường hợp giống như phiên bản gốc. Điều đó có nghĩa là bạn phải kiểm tra bản gốc đó! = MyBitmap trước khi tái chế bản gốc.
Jeremyfa

1
@Jeremyfa Nó chỉ trả về hình ảnh gốc nếu bạn chỉ định chiều rộng và chiều cao giống với hình gốc. Trong trường hợp đó, thay đổi tỷ lệ là một cuộc tranh luận, vì vậy nó cũng có thể lưu một số quy trình bằng cách bỏ qua nó và thay vào đó trả lại hình ảnh gốc. Nó không nên "phá vỡ" bất cứ điều gì mặc dù ...
Jabari

11

Khi bitmap đã được tải trong bộ nhớ, trên thực tế, nó được tạo bởi hai phần dữ liệu. Phần đầu tiên bao gồm một số thông tin về bitmap, phần khác bao gồm thông tin về pixel của bitmap (nó được ánh xạ theo mảng byte). Phần đầu tiên thoát trong bộ nhớ đã sử dụng Java, phần thứ hai thoát trong bộ nhớ đã sử dụng C ++. Nó có thể sử dụng bộ nhớ của nhau trực tiếp. Bitmap.recycle () được sử dụng để giải phóng bộ nhớ của C ++. Nếu bạn chỉ làm như vậy, GC sẽ thu thập một phần của java và bộ nhớ của C luôn được sử dụng.


+1 cho một cách thú vị nhưng rất tốt để mô tả lý do tại sao bộ nhớ không có sẵn cho GC ngay lập tức - một cách tốt.
Richard Le Mesurier,

8

Timmmm đã đúng.

theo: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

Ngoài ra, trước Android 3.0 (API Cấp 11), dữ liệu sao lưu của một bitmap được lưu trữ trong bộ nhớ gốc 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ớ trong thời gian ngắn và gặp sự cố.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.