Công cụ và hiệu suất

Các chiến lược hữu ích giúp giảm mức sử dụng bộ nhớ Bitmap của ứng dụng

Loa

Giới thiệu

Giới thiệu về bài thuyết trình

Việc quản lý bộ nhớ Bitmap là rất quan trọng để giảm thiểu rủi ro hết bộ nhớ (OOM) trong các ứng dụng Android có nhiều hình ảnh. Mặc dù các nhà phát triển biết cách giảm kích thước hình ảnh, nhưng để đạt được độ ổn định lâu dài cho ứng dụng, họ cần nắm vững hai trụ cột chính khác: Tái sử dụng và Tái chế. Phiên này cung cấp một cẩm nang thực tế để tối ưu hoá hiệu suất bộ nhớ bằng các công cụ hiện đại, thư viện nguồn mở như Glide và Coil, cũng như các API cấp hệ thống.

Trong phiên này, bạn sẽ tìm hiểu cách chẩn đoán tình trạng hồi quy bộ nhớ trong thực tế bằng Android vitals, triển khai các mẫu lưu vào bộ nhớ đệm nâng cao (Tái sử dụng) bằng Glide và Coil, cũng như chủ động giải phóng bộ nhớ (Tái chế) bằng lệnh gọi lại onTrimMemory.

Tóm tắt

  • Google Play sẽ áp dụng các ngưỡng hiệu suất bộ nhớ kể từ tháng 2 năm 2027. Việc vượt quá giới hạn phân vị thứ 90 sẽ ảnh hưởng trực tiếp đến kết quả đánh giá tình trạng của ứng dụng và có thể làm giảm khả năng xuất hiện trên Cửa hàng Play.
  • Bitmap mở rộng theo kích thước và độ sâu màu khi được giải mã, khiến chúng trở thành đối tượng tiêu thụ bộ nhớ lớn nhất trong hầu hết các ứng dụng. Chỉ một vài hình ảnh có độ phân giải đầy đủ cũng có thể chiếm hàng trăm megabyte.
  • Giảm tần số lấy mẫu hình ảnh và chỉ giải mã các vùng hiển thị để giảm mức tiêu thụ bộ nhớ. Yêu cầu kích thước hình ảnh phù hợp thông qua CDN hoặc sử dụng các API như BitmapRegionDecoder thay vì tải hình ảnh có độ phân giải đầy đủ quá lớn.
  • Nhiều trình tải hình ảnh sẽ nhân lên mức sử dụng bộ nhớ đệm vùng nhớ khối xếp mặc định và sao chép các Bitmap được lưu trữ. Chuẩn hoá trên một trình tải hoặc đặt ngân sách bộ nhớ rõ ràng trên các trình tải hiện có.
  • Giải phóng bộ nhớ hình ảnh khi ứng dụng của bạn chuyển sang chế độ nền bằng cách xử lý onTrimMemory. Dừng hoạt động thăm dò ý kiến và ảnh động trong nền trong onStop để ngăn chặn việc tăng bộ nhớ nền.

Tài nguyên

Gemini Notebook

Chủ đề

  • bitmap-optimization
  • android-memory
  • android-vitals
  • lưu hình ảnh vào bộ nhớ đệm
  • memory-profiler

Bản chép lời

Giới thiệu và tổng quan về bộ nhớ bitmap

  • Fung Lam

    Đúng vậy, chúng ta còn khoảng 1 giờ nữa là đến giờ ăn trưa, nên hãy tiếp tục theo dõi nhé.

  • Tôi là Fung Lam, kỹ sư phụ trách quan hệ với nhà phát triển Android tại London.

  • Vâng, cảm ơn bạn.

  • Trước khi bắt đầu, tôi có thể hỏi một câu được không?

  • Có bao nhiêu người trong số các bạn đã mua điện thoại mới trong năm nay?

  • Chà.

  • Chà.

  • Hãy thành thật nhé, tôi sẽ không trách bạn nếu bạn không mua Pixel đâu.

  • Well, it's more expensive, right, for the new phone.

  • Đúng vậy, đó là lý do một số người trong các bạn không mua điện thoại mới.

  • Tại sao giá lại cao hơn?

  • Bạn có biết?

  • STT

  • RAM.

  • RAM, đúng vậy.

  • Đúng vậy, RAM thực sự là một thách thức đối với Android và đối với chúng tôi, đối với nhà phát triển.

  • Đó là lý do hôm nay tôi có mặt ở đây để chia sẻ cách chúng ta có thể vượt qua thách thức về bộ nhớ, về RAM.

  • Trước đó, tại Google I/O và Google I/O Connect, chúng tôi đã có một số phiên về bộ nhớ, từ cách hệ điều hành phân bổ bộ nhớ, cách bạn có thể phân tích ứng dụng, cách bạn có thể tối ưu hoá mã bằng R8 và tất nhiên, tôi đã đề cập một chút về Bitmap vào thời điểm đó.

  • Bạn có thể thấy ảnh của tôi trong ảnh chụp màn hình.

  • Tối ưu hoá bộ nhớ là một lĩnh vực rộng lớn, bao gồm nhiều khía cạnh như Android Runtime, dọn rác, công cụ hiệu suất, phân bổ bộ nhớ đối tượng, trình biên dịch, v.v. Trong phiên này, trong 30 phút này, tôi sẽ cung cấp cho bạn một số thông tin về cách tối ưu hoá bộ nhớ Bitmap.

  • Bạn có thể hỏi, tại sao, tại sao lại là Bitmap?

  • Tại sao chúng ta nên tập trung vào bộ nhớ Bitmap?

  • Vì một hình ảnh đáng giá hơn nghìn lời nói.

  • Đúng vậy, tôi sẽ giải thích cho bạn lý do.

  • Trên màn hình điện thoại, giả sử như Pixel 7, Pixel 9 hoặc Pixel 11 Pro, độ phân giải màn hình là khoảng 2.500 x 1.200, 1.400.

  • Vì vậy, đối với mỗi pixel, chúng ta có độ sâu màu 32 bit.

  • Vì vậy, đối với hình ảnh toàn màn hình, ồ, tôi không có đủ ngón tay, nhưng, chẳng hạn như, nó sẽ chiếm khoảng 15 MB bộ nhớ, 50 MB.

  • Vậy nên, một từ trong hệ điều hành là 64 bit, tức là 8 byte.

  • Một nghìn từ chỉ có 8 MB.

  • Đúng vậy, 8K.

  • Vì vậy, chắc chắn một hình ảnh đáng giá hơn ngàn lời nói.

  • Đúng vậy, điều này rất quan trọng.

  • Một hình ảnh đã có kích thước 15 MB.

  • Nếu ứng dụng của bạn có 10, 100 hình ảnh thì sao?

  • Đó là một con số lớn.

  • Vì vậy, Bitmap luôn là đối tượng tiêu tốn nhiều bộ nhớ nhất trong ứng dụng của bạn.

  • Khi hình ảnh giải mã vào bộ nhớ, như tôi đã đề cập, hình ảnh sẽ mở rộng theo kích thước và độ sâu màu.

  • Đây không phải là kích thước tệp, mà là định dạng JPEG hay PNG.

  • Không, tệp này sẽ được giải mã và sử dụng bộ nhớ.

  • Vì vậy, chỉ cần một vài hình ảnh có độ phân giải đầy đủ là có thể dễ dàng chiếm hàng trăm megabyte bộ nhớ trên thiết bị của bạn.

  • Thứ hai, khi bộ nhớ rất eo hẹp, hệ điều hành sẽ phải tiêu tốn nhiều năng lượng hơn để xử lý mức tiêu thụ bộ nhớ.

  • Điều này sẽ khiến giao diện người dùng bị giật.

  • Hoặc hệ điều hành cũng có thể tắt ứng dụng của bạn để giải phóng bộ nhớ.

  • Hoặc ứng dụng của bạn cũng sẽ gặp lỗi Hết bộ nhớ, ngoại lệ Hết bộ nhớ.

  • May mắn thay, tin vui là mặt đồng hồ này thường mang lại thành công lớn mà không tốn nhiều công sức.

  • Hầu hết các mức tăng đột biến về bộ nhớ đều xuất phát từ lỗi phổ biến, có thể tránh được và chúng ta có thể khắc phục lỗi này mà không ảnh hưởng đến chất lượng hình ảnh.

  • Giờ thì bạn đã biết rằng bộ nhớ Bitmap rất quan trọng đối với ứng dụng của bạn, không chỉ giúp ứng dụng hoạt động mượt mà mà còn giúp ứng dụng ổn định và giữ chân người dùng.

  • Vì vậy, hôm nay chúng ta sẽ tìm hiểu 5 phần đơn giản để giúp ứng dụng của bạn hoạt động suôn sẻ.

  • Trước tiên, chúng ta sẽ tìm hiểu về chất lượng kỹ thuật trên Google Play, yêu cầu mới về chất lượng kỹ thuật, bao gồm cả giới hạn bộ nhớ sắp tới và nơi bạn có thể dễ dàng kiểm tra giới hạn này.

  • Thứ hai, tôi sẽ chia sẻ một nghiên cứu điển hình về 500 ứng dụng Android, cách các ứng dụng này sử dụng bộ nhớ, liệu chúng có đang sử dụng đúng cách hay không, hoặc liệu có điều gì chúng ta có thể học hỏi hay không?

  • Sau đó, tôi sẽ trình bày nguyên tắc tối ưu hoá mức sử dụng bộ nhớ Bitmap.

  • Cuối cùng, chúng tôi sẽ chia sẻ một số công cụ có thể giúp bạn kiểm tra ứng dụng của mình.

Tiêu chuẩn chất lượng của Google Play và các vấn đề thường gặp

  • Fung Lam

    Đầu tiên là yêu cầu mới của Google Play về chất lượng.

  • Bạn có thường xuyên truy cập vào Google Play Console không?

  • Mỗi tháng?

  • Mỗi tuần?

  • Chưa bao giờ?

  • Cười thế này là đồng ý đúng không?

  • Không ư?

  • STT

  • Bạn có ghé thăm phần Android vitals không?

  • Vâng, vâng.

  • Đúng vậy, chắc chắn là bạn đã biết điều gì đó trước khi tôi làm việc này.

  • Vì vậy, Google Play có các yêu cầu mới về chất lượng đối với ngưỡng hiệu suất.

  • Android và Play muốn người dùng sử dụng ứng dụng có chất lượng tốt.

  • Vì vậy, ngưỡng này cũng là một điều mà chúng tôi cố gắng mang lại điều tốt nhất cho người dùng cuối.

  • Nói một cách đơn giản, tức là "Này, khi ứng dụng của bạn đang chạy ở chế độ nền hoặc ở nền trước có thể quan sát được, thì mức sử dụng bộ nhớ ở phân vị thứ 90 không được vượt quá ngưỡng này.

  • Nếu không, ứng dụng của bạn có thể bị giảm khả năng xuất hiện trên Cửa hàng Play hoặc gặp phải vấn đề nào đó."

  • Chúng tôi sẽ bắt đầu thực thi chính sách này từ tháng 2 năm 2027 đối với các ứng dụng và trò chơi.

  • Việc vượt quá ngưỡng này sẽ ảnh hưởng trực tiếp đến kết quả đánh giá tình trạng ứng dụng.

  • Vì vậy, hãy nhớ truy cập vào Google Play Console và kiểm tra xem "Hôm nay, phân vị thứ 90 của tôi là bao nhiêu?"

  • Và cải thiện trước thời hạn.

  • Đây là ảnh động.

  • "Này, nếu truy cập vào Google Play Console, bạn có thể thấy Android vitals.

  • Có một phần bộ nhớ.

  • Và bạn có thể thấy bách phân vị thứ 90.

  • Và nếu nhấp vào đó, bạn có thể thấy mức tiêu thụ bộ nhớ trung bình theo thời gian.

  • Ngoài ra, còn có các cấp khác nhau theo RAM, theo kiểu dáng, theo mật độ màn hình, theo phiên bản hệ điều hành, giúp bạn tách biệt điểm tắc nghẽn bộ nhớ trên thiết bị cấp thấp cụ thể hoặc trên các bản phát hành ứng dụng cụ thể.

  • Bây giờ, tôi biết, vì bạn có thể nói, "Này, cảm ơn bạn đã cho tôi biết ứng dụng của tôi đã vượt quá ngưỡng.

  • Nhưng thực sự thì tôi không biết có vấn đề gì.

  • Bạn có thể giúp tôi không?"

  • Đúng vậy, đó là lý do tôi sẵn sàng hỗ trợ bạn.

  • Tôi sẽ cố gắng giải thích một cách dễ hiểu, chẳng hạn như, nếu bạn đang xem TV và TV của bạn không có hình ảnh hoặc âm thanh, bạn sẽ làm gì?

  • Bạn lấy sách hướng dẫn sử dụng TV ra, lật đến những trang cuối cùng, thường là hướng dẫn khắc phục sự cố, trong đó có ghi: "Này, nếu TV của bạn không có âm thanh, hãy thử nút này hoặc nút kia".

  • Đúng vậy, tương tự, hôm nay, tôi đã xác định được một số vấn đề thường gặp.

  • Và hy vọng đây là hướng dẫn khắc phục sự cố mà bạn cần.

  • Thông thường, khi đọc hướng dẫn sử dụng TV, chúng ta nên bắt đầu từ trang đầu tiên, thường là bộ công cụ chẩn đoán để thực sự hiểu cách TV hoạt động.

  • Nhưng giờ tôi biết những vấn đề đó ít thú vị hơn, nên hôm nay, xin cho phép tôi bắt đầu bằng việc khắc phục sự cố trước, rồi chúng ta sẽ quay lại sau.

  • Và thành thật mà nói, đây chính là cách tôi đọc hướng dẫn sử dụng TV.

  • Để tìm ra vấn đề thường gặp nhất về bộ nhớ Bitmap khi phân bổ Bitmap trong hệ sinh thái Android, tôi đã nghiên cứu khoảng 500 ứng dụng phổ biến trên Android.

  • Tôi thực sự chạy ứng dụng, khởi động ứng dụng, đăng nhập, truy cập vào các trang khác nhau, đi sâu vào và quay lại.

  • Tôi cũng thử nghiệm ứng dụng trên nhiều thiết bị có RAM khác nhau, chẳng hạn như thiết bị Android Go có bộ nhớ 2 GB, một số thiết bị cơ bản có RAM 4 GB, một số thiết bị tầm trung có RAM 4 GB, 6 GB đến 8 GB hoặc một số thiết bị gập cao cấp có màn hình rất lớn, chất lượng màn hình rất tốt và bộ nhớ 16 GB.

  • Chúng tôi đã đo lường mức sử dụng bộ nhớ trên thiết bị này khi ở các trạng thái ứng dụng khác nhau, thu thập tệp báo lỗi vùng nhớ khối xếp và sử dụng nhiều công cụ, bao gồm cả Kỹ năng AI của Perfetto, để phân tích quá trình phân bổ, nhằm xác định các mẫu phản thiết kế phổ biến, tức là những lỗi thường gặp mà chúng ta mắc phải, trên quy mô lớn.

  • Và đây là cách bộ nhớ thay đổi ở các trạng thái hoạt động khác nhau.

  • Và nhờ phân tích này, chúng tôi xác định được hơn 15 vấn đề thường gặp, từ hình ảnh quá khổ cho đến việc tăng bộ nhớ đệm nền.

  • Vì vậy, đây giống như nghiên cứu điển hình cốt lõi mà chúng ta sẽ chia sẻ và khám phá hôm nay.

Nghiên cứu điển hình: Định cỡ và giải mã Bitmap

  • Fung Lam

    Đúng vậy, tất cả nghiên cứu điển hình mà tôi đang chia sẻ đều liên quan đến nghiên cứu về 500 ứng dụng.

  • Vì các bạn đang ở đây, tôi nghĩ tất cả chúng ta đều yêu thích Android, đúng không?

  • Tất cả chúng ta đều yêu thích Android.

  • Giả sử có một người trong chúng ta cũng yêu thích Android, nhưng người đó không phải là chuyên gia phát triển Android.

  • Vì vậy, anh ấy tạo một ứng dụng Android để đặt hàng tượng nhỏ Android.

  • Nhưng có một số lỗi.

  • Hãy cùng thảo luận. Hãy xem, có thể đây là sai lầm đầu tiên.

  • "Này, trong trang sản phẩm, chúng ta có bố cục hai cột cho thấy các bức tượng nhỏ Android khác nhau.

  • Hình ảnh cho mỗi cột có kích thước khoảng 400 x 400 pixel.

  • Tuy nhiên, ứng dụng thực sự đang tải một hình ảnh xuống từ máy chủ, bất kể màn hình của tôi lớn đến mức nào.

  • Hệ thống đang tải hình ảnh 3000 x 3000 xuống.

  • Đúng vậy, vì nhà phát triển sẽ nghĩ: "Android thật đẹp, thật tuyệt vời.

  • Tôi muốn mang đến những điều tốt nhất cho người dùng".

  • Nhưng thực ra là không, vì nó bị giới hạn bởi kích thước của chế độ xem hình ảnh.

  • Vì vậy, ngay cả khi bạn tải hình ảnh lớn xuống, hình ảnh đó cũng không hiển thị tốt nhất cho người dùng.

  • Ngoài ra, hình ảnh lớn này còn làm tăng mức sử dụng bộ nhớ.

  • Và ngay cả khi chúng ta giảm tần số lấy mẫu, quá trình này cũng sử dụng thêm bộ nhớ.

  • Chúng ta nên giúp nhà phát triển này khắc phục vấn đề như thế nào?

  • Nếu bạn biết về CDN, thì CDN thực sự có thể xử lý một số thao tác với hình ảnh, chẳng hạn như cắt, đổi kích thước, xoay, áp dụng bộ lọc, chuyển hình ảnh từ hình vuông sang hình tròn.

  • Theo yêu cầu HTTP, CDN có thể xử lý một số việc.

  • Vì vậy, ngay cả khi phía máy chủ của bạn chỉ lưu trữ một hình ảnh, bạn vẫn có thể sử dụng CDN để gửi hình ảnh đã đổi kích thước đến thiết bị của mình.

  • Vì vậy, điều đầu tiên là khi bạn yêu cầu hình ảnh, hãy cố gắng sử dụng CDN để yêu cầu hình ảnh có kích thước ImageView.

  • Nhưng nếu không thể, chẳng hạn như đôi khi nó có thể lớn hơn một chút so với ImageView, hãy nhớ giảm tần số lấy mẫu để đổi kích thước trong mã.

  • Vì vậy, khi được tải vào bộ nhớ, hình ảnh được tải sẽ nhỏ hơn, chứ không phải là hình ảnh có độ phân giải đầy đủ.

  • Dưới đây là mã mẫu cho Coil.

    // 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
    val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"
    
    // 2. Enforce exact downsampling + Hardware Bitmap config
    AsyncImage(
        model = ImageRequest.Builder(LocalContext.current)
            .data(sizedUrl)
            .size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
            .precision(Precision.EXACT)      // Downsample to exact card size
            .bitmapConfig(Bitmap.Config.HARDWARE)
            .build(),
        contentDescription = product.title
    )
  • Bạn có thể truyền kích thước dưới dạng từ ngữ màu vàng.

  • Đúng vậy, bạn có thể truyền kích thước dưới dạng HTTP trong một yêu cầu HTTP.

  • Sau đó, trong đoạn mã được đánh dấu, bạn có thấy phần được đánh dấu không? Đúng vậy, đừng sử dụng Size.ORIGINAL.

  • Đúng vậy, hãy sử dụng chiều rộng và chiều cao để truyền dưới dạng tham số.

  • Sử dụng độ chính xác để giảm tần số lấy mẫu hình ảnh xuống kích thước chính xác.

  • Nếu có thể, hãy sử dụng Bitmap.Config.HARDWARE. Lớp này cũng sử dụng ít bộ nhớ hơn vì hình ảnh sẽ được lưu trữ trực tiếp trong VRAM.

  • Tương tự, giả sử trên ứng dụng này, họ phát hành một phiên bản mới của bộ sưu tập Android, nên có một hình ảnh dài mà người dùng có thể vuốt sang trái và phải để xem các thiết kế khác nhau của hình Android mới.

  • Nhưng cũng có một vấn đề khác ở đây.

  • Đối với hình ảnh dài, phần lớn hình ảnh thực sự nằm ngoài màn hình, nghĩa là người dùng không thể nhìn thấy hình ảnh đó, nhưng hình ảnh vẫn được tải vào bộ nhớ.

  • Chúng tôi có thể làm tốt hơn không?

  • Có.

  • Chúng ta có thể cắt hình ảnh thành các hình ảnh nhỏ hơn và chỉ tải hình ảnh hiển thị trên màn hình.

  • Những quảng cáo nằm ngoài màn hình sẽ không được tải hoặc sẽ bị huỷ tải.

  • Vì vậy, ứng dụng sẽ sử dụng ít bộ nhớ hơn.

  • Tương tự, trong trường hợp này, nó tiết kiệm được khoảng 60% bộ nhớ.

  • Tất nhiên, nếu bạn cắt thành hình chữ nhật nhỏ hơn, bạn sẽ tiết kiệm được nhiều hơn.

  • Nhưng tương tự, nếu người dùng muốn cung cấp một tính năng cho người dùng, bạn có thể phóng to và phóng to để xem mọi chi tiết của bức tượng Android, một số lỗi phổ biến là họ thực sự tải, một lần nữa, một hình ảnh rất lớn, vì vậy nó có thể được phóng to và phóng to một lần nữa.

  • Nhưng không, bạn không cần phải làm việc này.

  • Cách tốt hơn là khi người dùng phóng to hình ảnh có độ phân giải thấp hơn đã tải, và khi người dùng dừng chụm, bạn sẽ tính toán toạ độ rồi tải độ phân giải đầy đủ – chỉ tải khu vực đó, một khu vực được tính toán từ hình ảnh có độ phân giải cao.

  • Vì vậy, chỉ những vùng hiển thị đó mới được tải vào bộ nhớ.

  • Làm cách nào để thực hiện việc này?

  • Ồ, có một API tên là BitmapRegionDecoder, giúp bạn tải một vùng cụ thể của hình ảnh.

  • Và tất nhiên, cũng có một số công cụ và thư viện có thể giúp bạn.

  • Bạn có thể tìm kiếm trên GitHub.

  • Một số ứng dụng truyện tranh hoặc một số bản đồ đã sử dụng tính năng này.

  • Bạn có thể sử dụng các bản sao này để tham khảo.

  • Ngoài ra, chúng ta là con người, chúng ta có thể mắc sai lầm.

  • AI cũng có thể mắc sai sót.

  • Vì vậy, để tránh mắc lỗi, chúng ta cũng có thể thêm một số giới hạn khi tải hình ảnh.

  • Ví dụ: trong trường hợp này, tôi có thể nói: "Này, hãy sử dụng kích thước cửa sổ hoặc kích thước màn hình làm giới hạn và đặt kích thước đó vào hàm min, min của chiều rộng yêu cầu hoặc chiều rộng giới hạn".

    // Query active window bounds instead of hardcoding 1080 / 1920
    val bounds = WindowMetricsCalculator.getOrCreate()
        .computeCurrentWindowMetrics(activity).bounds
    
    // Clamp target dimensions so Bitmap NEVER exceeds the active window
    val safeWidth  = minOf(requestedWidthPx,  bounds.width())
    val safeHeight = minOf(requestedHeightPx, bounds.height())
    
    val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)
  • Vì vậy, với hàm này, bất cứ khi nào tôi tải một hình ảnh, hình ảnh đó sẽ không bao giờ vượt quá ranh giới.

  • Vì vậy, nó sẽ không tải hình ảnh lớn hơn độ phân giải màn hình.

  • Hãy nhớ rằng chỉ giải mã vùng hiển thị của hình ảnh để tiết kiệm bộ nhớ.

Quản lý bộ nhớ, bản sao và bộ nhớ đệm

  • Fung Lam

    Và một lần nữa, hãy nói rằng ứng dụng này thực sự không phải của một nhà phát triển.

  • Thực ra, tệp này dành cho nhiều người đóng góp và họ có thể đang sử dụng các công cụ khác nhau.

  • Ví dụ: một phần của việc này là sử dụng, xin lỗi.

  • Để tôi bắt đầu lại.

  • Điều tương tự cũng áp dụng cho ứng dụng này.

  • Này, tôi đang ở trang sản phẩm, tôi có một danh sách sản phẩm và tôi thấy hình ảnh này, tôi thêm nó vào giỏ hàng.

  • Vài phút sau, tôi chuyển đến trang thanh toán, và hình ảnh đó lại xuất hiện.

  • Nhưng đây có thực sự là cùng một hình ảnh hay thực ra đây là hai đối tượng khác nhau trong bộ nhớ?

  • Đúng vậy, một trong những lỗi phổ biến mà chúng tôi phát hiện là ứng dụng tải rất nhiều hình ảnh.

  • Tuy nhiên, họ cũng có rất nhiều hình ảnh trùng lặp trong bộ nhớ.

  • Nếu đang sử dụng trình tải hình ảnh, thì bạn thực sự có thể truyền một khoá để thực hiện việc lưu vào bộ nhớ đệm.

  • Vì vậy, khi sử dụng cùng một hình ảnh, bạn có thể sử dụng đối tượng Bitmap đã được phân bổ thay vì tạo một đối tượng mới.

  • Trong Android Studio, bạn có thể dùng Trình phân tích bộ nhớ để tìm xem ứng dụng của mình có Bitmap trùng lặp hay không.

  • Tôi đã đề cập đến vấn đề này trong phiên Google I/O của mình, vì vậy, bạn có thể quay lại YouTube để xem video.

  • Nếu không dùng Android Studio, bạn cũng có thể dùng lệnh adb để thu thập tệp báo lỗi khối nhớ, sau đó dùng ngôn ngữ SQL của biểu đồ khối nhớ Perfetto để truy vấn xem ứng dụng có Bitmap trùng lặp hay không.

  • Vì vậy, đây là một ví dụ về mã trong Coil, cách tôi thêm khoá để lưu Bitmap vào bộ nhớ đệm.

    // Strip volatile query tokens (?from=list / ?sig=...) for cache key
    val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))
    
    // On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
    AsyncImage(
        model = ImageRequest.Builder(context)
            .data(productUrl)
            .placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
            .memoryCacheKey(canonicalKey)
            .diskCacheKey(productUrl.substringBefore("?"))
            .build(),
        contentDescription = product.title
    )
  • Và giờ thì, đúng vậy.

  • Tôi quay lại ví dụ trước.

  • Chào bạn, ứng dụng của tôi có nhà phát triển khác.

  • Một số phần sử dụng Khung hiển thị, vì vậy, phần này sử dụng Glide làm trình tải hình ảnh.

  • Và một số người trong số họ đang sử dụng Coil vì họ đang sử dụng Compose.

  • Vậy có phải trình tải hình ảnh mà bạn vừa đề cập giúp chúng ta lưu vào bộ nhớ đệm không? Nếu tôi có nhiều trình tải hình ảnh thì có vấn đề gì không?

  • Chắc chắn là có vấn đề gì đó.

  • Vì vậy, đối với trình tải hình ảnh, theo mặc định, chúng sẽ sử dụng X% kích thước bộ nhớ khối xếp làm kích thước bộ nhớ đệm mặc định.

  • Vì vậy, nếu bạn có một trình tải hình ảnh, thì trình tải này sẽ sử dụng X% bộ nhớ của bạn.

  • Nếu bạn có hai trình tải hình ảnh, thì mức sử dụng bộ nhớ sẽ tăng gấp đôi.

  • Nếu bạn có 3 trình tải hình ảnh, thì con số này sẽ là 3X.

  • Và nếu trình tải hình ảnh của bạn không dùng chung một nhóm, thì điều tương tự sẽ xảy ra.

  • Giống như trong trang sản phẩm, tôi tải một hình ảnh bằng Glide, sau đó trong trang thanh toán, hình ảnh đó sẽ được tải bằng Coil.

  • Tuy nhiên, vẫn sẽ có hai hình ảnh trong kỷ niệm.

  • Điều đó không tốt.

  • Đúng vậy, để khắc phục vấn đề này, tất nhiên, lý tưởng nhất là chúng ta chỉ sử dụng một trình tải hình ảnh hoặc một nhóm dùng chung để lưu vào bộ nhớ đệm.

  • Nhưng nếu việc này đòi hỏi nhiều công sức kỹ thuật thì sao? Tôi không làm được.

  • Ồ, một cách khắc phục dễ dàng hơn là, này, tôi đo lường xem trình tải hình ảnh này sẽ xử lý bao nhiêu phần trăm hình ảnh, trình tải hình ảnh khác sẽ xử lý bao nhiêu phần trăm hình ảnh và đặt trọng số cho trình tải hình ảnh khác, giống như mã này.

    val totalBudgetBytes = computeAdaptiveCacheBytes(context)
    val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)
    
    // Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
    val loaderABudget = (totalBudgetBytes * 0.70).toInt()
    val loaderBBudget = (totalBudgetBytes * 0.30).toInt()
    
    // Approach B (Simple Fix without stats): Divide by number of loaders!
    val simpleSplitBudget = totalBudgetBytes / numberOfLoaders
    
    MemoryCache.Builder()
        .maxSizeBytes(simpleSplitBudget)
        .build()
  • Tôi có thể đặt loaderABudget là 70%, loaderBBudget là 30%, đó là một cách khác.

  • Nếu bạn thực sự không biết mỗi trình tải sẽ xử lý bao nhiêu hình ảnh, thì tốt hơn hết là bạn nên chia đều.

  • Này, tôi có hai trình tải hình ảnh, chia cho 2; ba trình tải hình ảnh, chia cho 3 thay vì sử dụng giá trị mặc định vì giá trị đó sẽ sử dụng quá mức bộ nhớ.

  • Và giả sử, trên trang sản phẩm, chúng ta muốn người dùng di chuyển xuống để xem thêm về sản phẩm.

  • Vì vậy, thông thường, chúng ta có thể lưu vào bộ nhớ đệm khoảng 50 hình ảnh hoặc 50 MB hình ảnh.

  • Nhưng nếu ứng dụng chuyển sang chế độ nền thì sao?

  • Ồ, nếu ứng dụng đang chạy ở chế độ nền, thì người dùng có thể không duyệt xem.

  • Nếu người dùng không duyệt xem, tôi có nên giữ lại lượng hình ảnh lưu trong bộ nhớ đệm này không?

  • Không, chúng ta nên phát hành.

  • Đúng vậy, chúng ta nên tuân thủ lệnh gọi lại onTrimMemory.

  • Đúng vậy, thực ra hiện nay có rất nhiều ứng dụng không triển khai hoặc không tuân thủ lệnh gọi lại onTrimMemory.

  • Đối với lệnh gọi lại onTrimMemory, có hai tín hiệu mà chúng ta cần lưu ý.

  • Một là UI_HIDDEN, giống như khi người dùng nhấn nút màn hình chính, và không phải ngay sau khi họ nhấn nút màn hình chính, mà là một khoảng thời gian sau khi họ nhấn nút màn hình chính, tức là "Ồ, ứng dụng của bạn thực sự đang ở trạng thái không có giao diện người dùng nào hiển thị cho người dùng".

  • Và chúng ta có thể dự đoán rằng: "Người dùng không sử dụng ứng dụng của bạn.

  • Bạn nên giải phóng tài nguyên cho hệ điều hành."

  • Vì vậy, chúng ta nên cắt bớt kích thước bộ nhớ đệm.

  • Và một trạng thái khác là TRIM_MEMORY_BACKGROUND, nghĩa là ứng dụng đã chạy trong nền trong một thời gian dài và hiện tại hệ điều hành thực sự cần một số tài nguyên.

  • Trong giai đoạn này, bạn nên giải phóng bộ nhớ càng nhiều càng tốt, gần như 100%.

  • Bằng cách này, bạn cũng giảm nguy cơ ứng dụng bị hệ điều hành tắt.

  • Vì kiểu như, "Này, họ muốn bộ nhớ, bạn cung cấp cho họ, sau đó hệ điều hành đó sẽ xử lý."

  • Nếu không, hệ điều hành không còn lựa chọn nào khác ngoài việc phải làm gì đó.

Ngăn chặn tình trạng tăng bộ nhớ nền

  • Fung Lam

    Một lỗi lớn khác mà tôi thấy là: "Này, tôi vừa đề cập rằng ứng dụng của bạn, nếu ứng dụng, nếu ứng dụng chuyển sang chế độ nền, thì ứng dụng đó sẽ sử dụng ít bộ nhớ hơn".

  • Tuy nhiên, tôi thấy các ứng dụng thực sự sử dụng nhiều bộ nhớ hơn trong nền, nhiều bộ nhớ hơn.

  • Tại sao?

  • Đó là lý do tôi nghiên cứu.

  • Tôi đã cố gắng tìm hiểu lý do.

  • Và một trong những lý do là, "Này, trong ứng dụng của họ, họ có một số ảnh động hoặc họ có một số băng chuyền.

  • Ví dụ: cứ sau 15 giây, các quảng cáo này lại tải một hình ảnh mới xuống và hiển thị hình ảnh mới đó cho người dùng."

  • Tuy nhiên, nhà phát triển không dừng ảnh động khi ứng dụng chuyển sang chế độ nền.

  • Vì vậy, ngay cả khi ứng dụng ở chế độ nền, mã vẫn chạy, vẫn nhận hình ảnh mới, giải mã hình ảnh mới và sử dụng nhiều bộ nhớ hơn.

  • Vì vậy, đây thực sự là điều mà ứng dụng không nên làm.

  • Đúng vậy.

  • Chúng ta nên dừng tất cả ảnh động, hoạt động lưu vào bộ nhớ đệm hoặc công việc không liên quan khi ứng dụng chuyển sang chế độ nền.

  • Vì vậy, nếu bạn biết về vòng đời hoạt động, thì sẽ có sự kiện onPause, onStop, hãy đảm bảo bạn triển khai onStop, onPause, onDestroyView để dừng ảnh động, dừng yêu cầu lấy hình ảnh hoặc những việc khác liên quan đến công việc mà người dùng không cần chú ý.

    override fun onStop() {
        super.onStop()
        // 1. Stop animations so they don't decode next frames in background!
        bannerAnimationView.cancelAnimation()
        animatedImageDrawable?.stop()
    
        // 2. Cancel next-image buffering / prefetch queues!
        nextImagesBufferingJob?.cancel()
        Glide.with(this).pauseRequests()
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        bannerAnimationView.clearAnimation() // Release resources!
    }

Khung 3R về sức khoẻ trí nhớ

  • Fung Lam

    Tôi biết tôi đã nói rất nhiều trường hợp với bạn, nhưng tôi đã nói: "Tôi có hơn 15 trường hợp".

  • Tôi biết rằng nếu tôi chia sẻ tất cả các trường hợp với bạn, bạn có thể quên hết.

  • Bạn chỉ cần quay lại khi về nhà, bạn chỉ cần tìm kiếm trên Google, "Này, làm cách nào để khắc phục vấn đề về bộ nhớ?"

  • Vì vậy, tôi giúp bạn dễ nhớ hơn.

  • Đúng vậy.

  • Tôi sẽ liên kết nó với một khung 3R rất nổi tiếng để giúp bạn ghi nhớ chúng.

  • Tôi nghĩ mọi người đều đã nghe nói đến khung 3R này.

  • Đầu tiên là giảm, cách giảm bộ nhớ khi tải hình ảnh.

  • Tất nhiên, như tôi đã đề cập, chúng ta có thể giảm tần số lấy mẫu, lấy đúng kích thước của hình ảnh khi lấy từ máy chủ.

  • Chúng tôi tối ưu hoá độ sâu màu hoặc sử dụng Bitmap phần cứng nếu có thể.

  • Nếu bạn không sử dụng CDN, hãy thử sử dụng CDN.

  • Vì vậy, khi lấy hình ảnh từ máy chủ, chúng ta chỉ lấy hình ảnh phù hợp nhất với kích thước.

  • Sử dụng lại, vì vậy, chúng ta sử dụng bộ nhớ đệm để hình ảnh tải vào bộ nhớ sẽ được sử dụng lại và chúng ta không có hình ảnh Bitmap trùng lặp trong bộ nhớ.

  • Và tất nhiên, hãy cố gắng không có nhiều trình tải hình ảnh trong mã của bạn, vì điều đó sẽ làm giảm hiệu quả.

  • Giảm thiểu, tái sử dụng và còn gì nữa?

  • Bạn có biết?

  • Tái chế.

  • Thử lại?

  • Tái chế, đúng vậy, tái chế.

  • Đúng vậy, chúng ta đã học về giảm thiểu, tái sử dụng và tái chế ở trường tiểu học, mặc dù chúng ta không học lập trình ở trường tiểu học.

  • Đúng vậy, tái chế, đúng vậy.

  • Đúng vậy, đó là cách giải phóng bộ nhớ để ứng dụng khác hoặc hệ điều hành có thể sử dụng bộ nhớ.

  • Có những thứ như tái chế.

  • Đầu tiên, tôi đã đề cập đến sự kiện onTrimMemory, có hai tín hiệu mà hệ điều hành sẽ gửi cho bạn khi giao diện người dùng bị ẩn hoặc hệ điều hành thực sự cần tài nguyên.

  • Đúng vậy, hãy thử xử lý yêu cầu này.

  • Và tất nhiên, nếu bạn có ảnh động, trình tải hình ảnh hoặc một số công việc khác đang diễn ra ở nền trước, hãy nhớ dừng công việc đó khi sự kiện này giống như trên onPause, onStop.

  • Vì vậy, hãy giảm thiểu, tái sử dụng và tái chế.

  • Dễ nhớ đúng không?

Bộ công cụ chẩn đoán và phân tích bằng AI dành cho nhà phát triển

  • Fung Lam

    Bây giờ, chúng ta sẽ quay lại phần ít thú vị hơn trong hướng dẫn sử dụng TV.

  • Làm cách nào để sử dụng các bộ công cụ khác nhau nhằm phát hiện và đo lường vấn đề về bộ nhớ này một cách hiệu quả?

  • Đầu tiên là Google Play Console, Android vitals và API báo cáo.

  • Có, hãy kiểm tra phân vị thứ 90 trong Google Play Console.

  • Bạn đang ở đâu?

  • Hoặc sử dụng Reporting API nếu bạn không truy cập vào trang web.

  • Bạn vẫn có thể sử dụng API để lấy giá trị.

  • Đúng vậy, hãy đảm bảo rằng ngưỡng là 200 MB và 400 MB.

  • Đảm bảo bạn không vượt quá ngưỡng.

  • Ngoài ra, còn có lệnh ADB giúp bạn thu thập tệp báo lỗi khối xếp hoặc gửi sự kiện để mô phỏng, chẳng hạn như ứng dụng của tôi hiện đang ở giai đoạn UI_HIDDEN.

    adb shell dumpsys meminfo <pkg>  # watch Native Heap!
    adb shell am send-trim-memory <pkg> UI_HIDDEN
  • Nếu bạn đang sử dụng Android Studio thì thật tuyệt.

  • Tuy nhiên, đây luôn là một người bạn tốt của bạn.

  • Bạn có thể sử dụng trình phân tích của Android Studio để thu thập tệp báo lỗi, để có giao diện người dùng xem mức sử dụng bộ nhớ thực tế của bạn là gì, mức phân bổ bộ nhớ thực tế là gì.

  • Và tất nhiên, chúng ta sẽ nói về AI với Gemini trong Android Studio hoặc các kỹ năng Android khác.

  • Công cụ này giúp bạn phân tích tệp báo lỗi khối xếp và sử dụng dấu vết SQL Perfetto để tìm Bitmap, thường cho biết ai là chủ sở hữu bộ nhớ đệm.

  • Hình ảnh của tôi được tải bằng Glide hay Coil?

  • Có lỗi rò rỉ bộ nhớ nào khác trong ứng dụng của tôi không?

  • Tôi biết một số bạn sẽ nói: "Này, thực ra tôi không còn dùng IDE nữa.

  • Tôi là kỹ sư thiết kế câu lệnh.

  • Tôi chỉ cần gửi câu lệnh cho tác nhân AI để viết mã."

  • Vâng, tôi biết.

  • Tôi cũng lập trình theo cảm hứng khi rảnh rỗi.

  • Vì vậy, tôi chia sẻ với bạn một mẫu.

  • Đúng vậy, đây chỉ là một phần của câu lệnh mà tôi dùng để nghiên cứu 500 ứng dụng.

  • Ừ, chỉ một phần thôi.

  • Bạn biết đấy, màn hình không lớn.

  • Nếu tôi cho nhiều hơn, bạn sẽ khó nhìn thấy.

  • Nhưng điều này giúp bạn hình dung được cách sử dụng AI để tự động hoá, ghi lại mức sử dụng bộ nhớ, bao gồm cả tệp báo lỗi, cũng như phân tích và cung cấp kết quả cho bạn.

  • Và tôi có thể nói thêm một câu nữa, "Vui lòng cung cấp cho tôi mã sửa lỗi cho mã của tôi".

  • Tôi hy vọng điều này có thể giúp bạn bớt lo lắng, kiểu như "Bộ nhớ là một miền rộng lớn.

  • Tôi không học về hệ điều hành hay kiến trúc hệ thống máy tính trong ngành khoa học máy tính ở trường đại học.

  • Nhưng thực ra, việc này không đáng sợ và cũng không khó đến thế."

  • Đúng vậy, việc này giúp bạn dễ dàng phân tích ứng dụng của mình.

  • Hãy thử xem.

  • Nhưng tất nhiên, AI sẽ mắc sai lầm, vì vậy, kiến thức cơ bản về cách sử dụng bộ nhớ cũng như cách hệ điều hành phân bổ bộ nhớ vẫn rất quan trọng.

  • Vì vậy, hãy đến nghe bài nói chuyện của tôi và xem video của chúng tôi trên YouTube.

Những điểm chính cần ghi nhớ và tóm tắt

  • Fung Lam

    Tóm lại, chúng ta có một số điểm chính.

  • Đầu tiên là giảm thiểu.

  • Đảm bảo bạn có được kích thước hình ảnh phù hợp.

  • Bạn chia hình ảnh thành hình ảnh nhỏ hơn để không tải mọi thứ vào bộ nhớ, đúng vậy, chỉ để tải hình ảnh mà người dùng nhìn thấy vào bộ nhớ.

  • Sử dụng bitmapConfig hoặc RGB_565 phần cứng bất cứ khi nào có thể.

  • Sử dụng lại, vì vậy, hãy đảm bảo bạn có chiến lược lưu vào bộ nhớ đệm, sử dụng trình tải hình ảnh để giúp bạn giảm số lượng Bitmap trùng lặp trong bộ nhớ.

  • Và tất nhiên, đừng sử dụng nhiều hình ảnh Bitmap, ngay cả khi bạn đã cố gắng ghi đè kích thước bộ nhớ đệm mặc định của các trình tải hình ảnh đó.

  • Chúng ta không tái sử dụng, tái chế.

  • Đúng vậy.

  • Quá trình quản lý bộ nhớ của ứng dụng phải thích ứng với trạng thái của ứng dụng.

  • Nếu giao diện người dùng bị ẩn, thì bạn nên sử dụng ít bộ nhớ hơn.

  • Nếu hệ điều hành thực sự yêu cầu bạn giải phóng bộ nhớ, hãy giải phóng nhiều nhất có thể.

  • Và ngừng tiêu hao tài nguyên, bất kể đó là Bitmap, nguồn điện hay tài nguyên tính toán.

  • Đúng vậy, dừng đồng hồ hẹn giờ của biểu ngữ, dừng ảnh động, dừng việc gộp hình ảnh.

  • Cuối cùng, hãy nhớ theo dõi.

  • Thường xuyên truy cập vào Google Play Console.

  • Hãy đảm bảo đây là một bài tập thực hành.

  • Đúng vậy, điều này sẽ giúp bạn hiểu được trạng thái hiện tại của phân vị thứ 90 về mức sử dụng bộ nhớ của ứng dụng trong toàn bộ nhóm người dùng.

  • Việc này cũng sẽ giúp bạn xác định vấn đề từ sớm.

  • Đúng vậy.

  • Để lập hồ sơ trên thiết bị, bạn có thể sử dụng trình phân tích bộ nhớ trong Android Studio.

  • Chúng ta cũng có một số lệnh ADB.

  • Chúng tôi cũng có kỹ năng về Android.

  • Nhưng hãy nhớ rằng nếu bạn muốn xem số liệu thống kê về người dùng thực tế, thì Google Play Console chính là nơi dành cho bạn.

  • Ngoài ra, bạn cũng có thể dùng API Google Play Reporting để lấy dữ liệu này mà không cần đăng nhập vào Play Console.

  • Như thường lệ, nếu bạn muốn tìm hiểu thêm, hãy truy cập vào developer.android.com/memory.

  • Giảm thiểu, tái sử dụng và tái chế bằng cách này.

  • Chúng ta có thể xây dựng một hệ sinh thái Android tốt hơn. Tôi hy vọng chúng ta có thể cùng nhau xây dựng một hệ sinh thái Android tốt hơn để vượt qua thách thức mà chúng ta đang gặp phải.

  • Cảm ơn bạn và chúc bạn lập trình vui vẻ.

  • Tôi sẽ sớm có giờ làm việc bên ngoài, vì vậy nếu bạn có câu hỏi, hãy thoải mái đến.

  • Và diễn giả tiếp theo, chúng ta còn một chút thời gian, vì vậy, vui lòng đợi diễn giả tiếp theo lên sân khấu và cảm ơn các bạn rất nhiều.

Mã

Đoạn mã

Đoạn mã Kotlin ở phút 11:50 trong "Nghiên cứu điển hình: Định cỡ và giải mã Bitmap"
// 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"

// 2. Enforce exact downsampling + Hardware Bitmap config
AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data(sizedUrl)
        .size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
        .precision(Precision.EXACT)      // Downsample to exact card size
        .bitmapConfig(Bitmap.Config.HARDWARE)
        .build(),
    contentDescription = product.title
)
Đoạn mã Kotlin ở phút 14:45 trong video "Nghiên cứu điển hình: Định cỡ và giải mã Bitmap"
// Query active window bounds instead of hardcoding 1080 / 1920
val bounds = WindowMetricsCalculator.getOrCreate()
    .computeCurrentWindowMetrics(activity).bounds

// Clamp target dimensions so Bitmap NEVER exceeds the active window
val safeWidth  = minOf(requestedWidthPx,  bounds.width())
val safeHeight = minOf(requestedHeightPx, bounds.height())

val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)
Đoạn mã Kotlin ở phút 16:51 trong video "Quản lý bộ nhớ, bản sao và bộ nhớ đệm"
// Strip volatile query tokens (?from=list / ?sig=...) for cache key
val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))

// On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
AsyncImage(
    model = ImageRequest.Builder(context)
        .data(productUrl)
        .placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
        .memoryCacheKey(canonicalKey)
        .diskCacheKey(productUrl.substringBefore("?"))
        .build(),
    contentDescription = product.title
)
Đoạn mã Kotlin ở phút 18:17 trong video "Quản lý bộ nhớ, bản sao và bộ nhớ đệm"
val totalBudgetBytes = computeAdaptiveCacheBytes(context)
val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)

// Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
val loaderABudget = (totalBudgetBytes * 0.70).toInt()
val loaderBBudget = (totalBudgetBytes * 0.30).toInt()

// Approach B (Simple Fix without stats): Divide by number of loaders!
val simpleSplitBudget = totalBudgetBytes / numberOfLoaders

MemoryCache.Builder()
    .maxSizeBytes(simpleSplitBudget)
    .build()
Đoạn mã Kotlin ở phút 22:05 trong video "Ngăn chặn tình trạng tăng mức sử dụng bộ nhớ trong nền"
override fun onStop() {
    super.onStop()
    // 1. Stop animations so they don't decode next frames in background!
    bannerAnimationView.cancelAnimation()
    animatedImageDrawable?.stop()

    // 2. Cancel next-image buffering / prefetch queues!
    nextImagesBufferingJob?.cancel()
    Glide.with(this).pauseRequests()
}

override fun onDestroyView() {
    super.onDestroyView()
    bannerAnimationView.clearAnimation() // Release resources!
}
Shell snippet at 25:17 in "Developer diagnostic toolkit & AI analysis"
adb shell dumpsys meminfo <pkg>  # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDEN