Hướng dẫn

Ưu tiên hiệu suất bộ nhớ: Các bước cần thiết cho Android 17

10 phút đọc
3 Tác giả
Alice Yuan, Ajesh Pai, Fung Lam

Mặc dù hiệu suất của ứng dụng thường được đánh giá bằng giao diện người dùng mượt mà và thời gian khởi động nhanh, nhưng bộ nhớ đóng vai trò là nền tảng thầm lặng để xây dựng các chỉ số hữu hình này. Không có gì bí mật khi chúng ta thấy sự thay đổi trong đó bộ nhớ thiết bị quan trọng hơn bao giờ hết. Không chỉ đạt được những bước tiến trong việc tối ưu hoá bộ nhớ Android bằng Android 17, chúng tôi còn cung cấp công cụ và hỗ trợ API để giúp bạn đáp ứng các yêu cầu nghiêm ngặt hơn về bộ nhớ vào cuối năm nay.

Để đảm bảo tính ổn định của thiết bị, bắt đầu từ Android 17, hệ thống sẽ bắt đầu thực thi hạn mức bộ nhớ ứng dụng dựa trên tổng dung lượng RAM của thiết bị. Nếu một ứng dụng vượt quá các giới hạn đó, Android sẽ huỷ quy trình mà không có dấu vết ngăn xếp liên kết.

Ngoài những trường hợp chấm dứt bắt buộc này, việc sử dụng bộ nhớ không được tối ưu hoá chắc chắn sẽ làm giảm trải nghiệm người dùng. Khi ứng dụng sắp đạt đến giới hạn bộ nhớ heap, ứng dụng sẽ kích hoạt quá trình thu thập rác thường xuyên, dẫn đến tình trạng giật cục đáng chú ý trên giao diện người dùng. Hơn nữa, khi thiết bị hết bộ nhớ khả dụng, hệ thống sẽ cố gắng thu hồi các trang, gây ra tình trạng căng thẳng cho CPU, độ trễ giao diện người dùng và tiêu hao pin. Nếu tình trạng thiếu bộ nhớ quá nghiêm trọng, thì có thể xảy ra các sự kiện Low Memory Killer (LMK) (Trình diệt bộ nhớ thấp) đột ngột chấm dứt các quy trình ở chế độ nền và buộc các ứng dụng phải khởi động nguội chậm và mất trạng thái người dùng.

Để tạo các ứng dụng có hiệu suất cao và tránh những trường hợp chấm dứt bắt buộc này, bạn nên áp dụng các chiến lược tối ưu hoá bộ nhớ sau đây:

  1. Tối đa hoá việc tối ưu hoá mã byte bằng R8
  2. Tối ưu hoá hoạt động tải hình ảnh
  3. Phát hiện và khắc phục lỗi rò rỉ bộ nhớ bằng Android Studio
  4. Cắt bớt bộ nhớ khi ứng dụng rời khỏi trạng thái hiển thị
  5. Khả năng quan sát bộ nhớ nâng cao bằng ProfilingManager

Chúng tôi cũng có phiên bản rút gọn của bài đăng này dưới dạng video, hãy xem ngay!

Tìm hiểu về hạn mức bộ nhớ ứng dụng trên Android 17

Hạn mức bộ nhớ ứng dụng sẽ được ra mắt trong Android 17 để ngăn chặn "một đối tượng xấu" phá huỷ trải nghiệm đa nhiệm và tính ổn định của toàn bộ thiết bị người dùng.

Sau đây là thông tin chi tiết về những lý do dẫn đến thay đổi này về cấu trúc:

  • Ngăn chặn các lỗi LMK liên tiếp: Khi một ứng dụng trở nên quá tải hoặc rò rỉ bộ nhớ trong khi giữ trạng thái đặc quyền (ví dụ: ứng dụng đang chạy một Dịch vụ trên nền trước), ban đầu, ứng dụng sẽ được bảo vệ khỏi Low Memory Killer (LMK) của hệ thống. Khi ứng dụng duy nhất này phát triển không kiểm soát và chiếm dụng RAM, LMK buộc phải bù đắp bằng cách loại bỏ hàng chục ứng dụng nhỏ hơn, hoạt động tốt và các tác vụ ở chế độ nền đã được lưu vào bộ nhớ đệm để giải phóng dung lượng cho ứng dụng chiếm dụng bộ nhớ.
     
  • Duy trì khả năng đa nhiệm và trạng thái người dùng: Khi hệ thống buộc phải xoá các ứng dụng được lưu vào bộ nhớ đệm để phù hợp với một quy trình rò rỉ duy nhất, trải nghiệm đa nhiệm sẽ bị suy giảm nghiêm trọng. Người dùng quay lại các ứng dụng đã lưu vào bộ nhớ đệm trước đó sẽ gặp phải tình trạng khởi động nguội chậm chạp thay vì tiếp tục hoạt động gần như tức thì. Sự thiếu hiệu quả này tạo ra nhiều tải hơn cho CPU và đẩy nhanh quá trình tiêu hao pin. Thao tác này cũng có thể xoá ngữ cảnh của người dùng trong các ứng dụng đã dùng gần đây, chẳng hạn như vị trí cuộn, ngăn xếp điều hướng và tiến trình trong trò chơi.

Để xác định xem phiên hoạt động của ứng dụng có bị ảnh hưởng bởi những hạn chế này hay không, bạn có thể gọi getDescription() trong ApplicationExitInfo. Nếu hệ thống áp dụng một giới hạn, lý do thoát sẽ được báo cáo là REASON_OTHER và chuỗi nội dung mô tả sẽ chứa "MemoryLimiter:AnonSwap". Bạn cũng có thể tận dụng phân tích tài nguyên dựa trên điều kiện kích hoạt bằng cách sử dụng TRIGGER_TYPE_ANOMALY để tự động ghi tệp báo lỗi khi đạt đến hạn mức bộ nhớ. Ngoài ra, Android đang nỗ lực cung cấp thêm các chỉ số về bộ nhớ trong cho nhà phát triển trong Google Play Console.

Chúng tôi cũng đã mở rộng tài liệu về giới hạn bộ nhớ để bổ sung các lệnh gỡ lỗi cục bộ, cho phép bạn mô phỏng các hạn chế về bộ nhớ trong môi trường cục bộ và xác thực hành vi của ứng dụng trong mọi trường hợp thực thi giới hạn bộ nhớ. 

Tối đa hoá việc tối ưu hoá mã byte bằng R8

Một cách rất hiệu quả để giảm mức sử dụng bộ nhớ của ứng dụng là bật trình tối ưu hoá R8. Bằng cách rút gọn các lớp, phương thức và trường thành tên ngắn hơn, đồng thời loại bỏ mã và tài nguyên không dùng đến, R8 giúp giảm đáng kể mức sử dụng bộ nhớ của ứng dụng bằng cách giảm thiểu lượng mã thường trú cần thiết trong quá trình thực thi. 

R8 giảm thiểu mã thường trú, giảm mức sử dụng bộ nhớ và giảm nguy cơ LMK chấm dứt. Điều này dẫn đến việc khởi động ấm thường xuyên hơn so với khởi động nguội chậm. Ngoài ra, mã byte được tinh giản giúp giảm mức hao tổn CPU của luồng chính, từ đó trực tiếp giảm tỷ lệ lỗi ANR để mang lại trải nghiệm mượt mà hơn cho người dùng. Ví dụ: ngân hàng kỹ thuật số Monzo đã bật chế độ tối ưu hoá R8 hoàn toàn và nhận thấy tỷ lệ lỗi ANR giảm 35%, tỷ lệ khởi động nguội tăng 30% và kích thước ứng dụng tổng thể giảm 9%.

pic1-IO26_113_TSV-monzo-casestudy.jpg
Ngân hàng kỹ thuật số Monzo đã bật tính năng tối ưu hoá R8 đầy đủ và tăng các chỉ số hiệu suất lên đến 35%.

Cách định cấu hình R8 đúng cách trong tệp build.gradle:

  • Đặt isShrinkResources = trueisMinifyEnabled = true.
  • Sử dụng proguard-android-optimize.txt thay vì proguard-android.txt cũ. proguard-android.txt thực sự ngăn chặn các hoạt động tối ưu hoá và không còn được hỗ trợ trong Trình bổ trợ Android cho Gradle 9 nữa.
  • Xoá android.enableR8.fullMode = false khỏi gradle.properties.

Nếu bạn đang sử dụng tính năng phản chiếu trong toàn bộ mã nguồn, hãy thêm Quy tắc giữ lại để ngăn R8 tối ưu hoá những phần đó của mã. Hãy nhớ giới hạn phạm vi của các quy tắc giữ lại để tối ưu hoá tối đa. 

Để tối ưu hoá tối đa, hãy nhớ làm theo các phương pháp hay nhất sau đây trong tệp quy tắc giữ lại.

  • Xoá các lựa chọn chung như -dontoptimize, -dontshrink-dontobfuscate để ngăn R8 tối ưu hoá toàn bộ cơ sở mã
  • Xoá các quy tắc giữ lại ngăn việc tối ưu hoá các thành phần Android như Hoạt động, Dịch vụ, Khung hiển thị hoặc Trình thu phát sóng.
  • Tinh chỉnh các quy tắc giữ rộng trên toàn bộ gói để chỉ nhắm đến các lớp hoặc phương thức cụ thể. 

Để xem thêm các phương pháp hay nhất, hãy xem tài liệu về quy tắc giữ lại của chúng tôi.

Các phương pháp hay nhất về R8 dành cho nhà phát triển thư viện

Nếu bạn là nhà phát triển thư viện, hãy đặt các quy tắc mà người dùng cần vào consumer-rules file và giữ các quy tắc bảo vệ nội bộ của thư viện trong tệp proguard-rules.pro. Để biết thêm thông tin về cách tối ưu hoá thư viện, hãy xem bài viết Tối ưu hoá cho tác giả thư viện.

Trình phân tích cấu hình R8

Để kiểm tra quá trình tối ưu hoá R8, hãy sử dụng Trình phân tích cấu hình. Trình phân tích cấu hình cho biết trạng thái tối ưu hoá hiện tại bằng điểm số  Làm rối mã nguồn, Tối ưu hoá và Rút gọn. Với trình phân tích cấu hình, bạn cũng có thể biết được số lượng lớp, phương thức hoặc trường bị ngăn tối ưu hoá theo từng quy tắc giữ lại. Tinh chỉnh các quy tắc giữ lại rộng rãi trên toàn bộ gói này để khai thác tối đa khả năng tối ưu hoá. 

Bằng cách sử dụng trình phân tích cấu hình, bạn cũng có thể xác định các quy tắc giữ lại đang bao hàm các quy tắc giữ lại khác, các quy tắc giữ lại dư thừa và các quy tắc giữ lại không dùng đến.

pic2-r8-config-analyzer.png
Trình phân tích cấu hình cho biết trạng thái tối ưu hoá hiện tại bằng điểm số về việc làm rối mã nguồn, tối ưu hoá và giảm kích thước.

Kỹ năng của tác nhân R8 

Bạn cũng có thể tận dụng R8 Agent Skill bằng tác nhân Android Studio hoặc các công cụ AI khác để giải quyết các lỗi cấu hình và tinh chỉnh các quy tắc của bạn, từ đó cải thiện hiệu suất của ứng dụng. (Thông tin chi tiết từ các kỹ năng dựa trên AI sẽ cần được xác minh về mặt kỹ thuật)

Tối ưu hoá hoạt động tải hình ảnh

Bitmap thường là các đối tượng chung lớn nhất nằm trong bộ nhớ của ứng dụng. Đây là giai đoạn cuối cùng của quy trình tải hình ảnh, trong đó các tệp nén (chẳng hạn như JPEG hoặc PNG) được giải mã thành dữ liệu thô về pixel để hiển thị. Điều này có nghĩa là một hình ảnh nén có kích thước nhỏ 100 KB có thể tăng lên thành vài megabyte RAM vì mức tiêu thụ bộ nhớ được xác định bằng kích thước pixel và độ sâu màu của hình ảnh. Vì các thao tác bitmap thường nằm trên đường dẫn quan trọng để vẽ khung hình, nên những hình ảnh chưa được tối ưu hoá sẽ gây ra tình trạng phình bộ nhớ nghiêm trọng và hiện tượng giật giao diện người dùng.

Google đề xuất tận dụng các thư viện tải hình ảnh Coil cho các dự án ưu tiên Kotlin, đặc biệt là khi phát triển bằng Jetpack Compose và Glide cho các ứng dụng dựa trên Java.

Áp dụng 5 phương pháp hay nhất này

  1. Giảm tần số lấy mẫu hình ảnh: Nếu bạn đang tải bitmap theo cách thủ công, hãy tránh tải một hình ảnh lớn vào một khung hiển thị hình thu nhỏ nhỏ; hãy dùng inSampleSize để tải một phiên bản nhỏ hơn. Theo mặc định, Glide và Coil sẽ giảm mẫu hình ảnh và bạn có thể định cấu hình chiến lược giảm mẫu này bằng cách sử dụng DownsampleStrategyImageLoader tương ứng.
  2. Cắt:  Tránh nhúng khoảng đệm trực tiếp vào tệp hình ảnh cho mục đích tạo hiệu ứng hòm thư (ví dụ: tạo đường viền trong suốt để mở rộng kích thước hình ảnh). Thay vì kết hợp các đường viền này, hãy sử dụng InsetDrawable hoặc áp dụng khoảng đệm trực tiếp trong Khung hiển thị hoặc Thành phần kết hợp chứa ảnh bitmap.
  3. Cấu hình: Cân bằng bộ nhớ và chất lượng bằng cách chọn định dạng pixel phù hợp. Sử dụng RGB_565 khi không cần độ trong suốt. Định dạng này sử dụng một nửa bộ nhớ của định dạng ARGB_8888 mặc định. Trong Glide, bạn có thể định cấu hình chế độ này bằng cách sử dụng DecodeFormat và trong Coil, bạn có thể sử dụng thuộc tính bitmapConfig.
  4. Ưu tiên các vectơ vẽ được: Đối với các thành phần hình học cơ bản, hãy tận dụng ShapeDrawable làm giải pháp thay thế đơn giản cho việc giải mã các bitmap được tạo từ đường quét. Bằng cách xác định các thành phần này một lần thông qua XML, bạn đảm bảo chúng mở rộng quy mô liền mạch trên tất cả các mật độ hiển thị, đồng thời loại bỏ hiệu quả tình trạng tăng bộ nhớ do tài nguyên.
  5. Sử dụng lại: Nếu ứng dụng của bạn quản lý Bitmap theo cách thủ công, thì để giảm thiểu tình trạng nhồi nhét bộ nhớ, khi không cần bitmap nữa, ứng dụng sẽ gọi bitmap.recycle() và loại bỏ ngay tham chiếu Bitmap. Nếu bạn sử dụng một thư viện tải hình ảnh như Glide hoặc Coil, hãy trả lại bitmap cho nhóm được quản lý của thư viện. Bằng cách cung cấp một vùng đệm hiện có cho nhu cầu bộ nhớ trong tương lai, nhóm này sẽ tránh được chi phí chung của các hoạt động phân bổ mới.

Hãy tham khảo tài liệu của chúng tôi về Tối ưu hoá hiệu suất cho hình ảnh để tìm hiểu thêm.

Công cụ Android Studio

Bạn cũng có thể loại bỏ các bitmap dư thừa bằng Android Studio Narwhal 4. Sau đây là cách tìm ra những thông tin đó trong 5 bước đơn giản:

  1. Mở thẻ Profiler (Trình phân tích tài nguyên) trong Android Studio
  2. Nhấp vào Heap Dump (Kết xuất heap) (hoặc "Analyze Memory Usage" (Phân tích mức sử dụng bộ nhớ)) rồi nhấn vào nút ghi để chụp nhanh trạng thái bộ nhớ hiện tại của ứng dụng.
  3. Quét kết quả phân tích để tìm tam giác cảnh báo màu vàng ⚠️. Android Studio dùng biểu tượng này để gắn cờ các bitmap trùng lặp đang được lưu trữ nhiều lần. Ngoài ra, bạn có thể chuyển đến tiêu đề của trình phân tích tài nguyên, chọn "Lọc theo:" rồi chọn chế độ cài đặt "Duplicate Bitmaps" (Bitmap trùng lặp).
  4. Nhấp vào mục bất kỳ bị gắn cờ để mở ngăn Xem trước bitmap. Nhờ đó, bạn có thể biết chính xác hình ảnh nào là hình ảnh vi phạm nhiều lần.
  5. Hãy sử dụng thông tin xác nhận trực quan đó để theo dõi logic tải dư thừa trong mã của bạn và triển khai một chiến lược lưu vào bộ nhớ đệm hiệu quả hơn.
pic3-IO26_113_TSV -dup-bitmaps-cropped.jpg
Tìm biểu tượng cảnh báo hình tam giác màu vàng ⚠️ trong kết xuất heap khi sử dụng Trình phân tích tài nguyên của Android Studio.

Phát hiện và khắc phục lỗi rò rỉ bộ nhớ bằng Android Studio

Rò rỉ bộ nhớ trong Android xảy ra khi mã của bạn giữ lại thông tin tham chiếu của một đối tượng rất lâu sau khi vòng đời của đối tượng đó kết thúc. Điều này ngăn Trình thu gom rác (GC) thu hồi bộ nhớ đó, cuối cùng dẫn đến hiệu suất chậm hoặc OutOfMemoryError (OOM).

Android Studio Panda 3 có một tác vụ trình phân tích tài nguyên LeakCanary chuyên dụng, cho phép nhà phát triển phân tích các rò rỉ bộ nhớ theo thời gian thực và ánh xạ các dấu vết ngay trong IDE.

Tác vụ trình phân tích tài nguyên LeakCanary trong Android Studio chủ động chuyển hoạt động phân tích rò rỉ bộ nhớ từ thiết bị sang máy phát triển, giúp tăng hiệu suất đáng kể trong giai đoạn phân tích rò rỉ so với hoạt động phân tích rò rỉ trên thiết bị.

pic4-android-studio-leaks.png
 Phân tích rò rỉ bộ nhớ LeakCanary được ngữ cảnh hoá bằng Lệnh chuyển đến khai báo để gỡ lỗi

Ngoài ra, giờ đây, hoạt động phân tích rò rỉ được đặt trong ngữ cảnh trong IDE và tích hợp hoàn toàn với mã nguồn của bạn, cung cấp các tính năng như chuyển đến khai báo và các kết nối mã hữu ích khác giúp giảm đáng kể sự bất tiện và thời gian cần thiết để điều tra và khắc phục tình trạng rò rỉ bộ nhớ.  

Ví dụ về các lỗi rò rỉ bộ nhớ thường gặp 

Rò rỉ bộ nhớ xảy ra khi một đối tượng vẫn tồn tại trong bộ nhớ ngoài vòng đời dự kiến. Điều này thường xảy ra do:

  • Giữ lại các thông tin tham chiếu đến những Mảnh, Hoạt động hoặc Khung hiển thị không còn được dùng nữa.
  • Quản lý sai các tham chiếu Ngữ cảnh.
  • Không huỷ đăng ký đúng cách các đối tượng theo dõi, trình nghe và bộ nhận.
  • Tạo các tham chiếu tĩnh đến các đối tượng được liên kết với các thành phần có vòng đời ngắn hơn.

Dưới đây là một số trường hợp ví dụ:

Trường hợpVí dụ dựa trên Compose Ví dụ dựa trên lượt xem
Ngữ cảnh rò rỉ

Ví dụ:
Truyền LocalContext.current đến ViewModel

Khắc phục:
Giữ logic phụ thuộc vào Ngữ cảnh trong lớp giao diện người dùng. Đối với các lớp không phải giao diện người dùng, hãy tái cấu trúc để sử dụng phương thức chèn phần phụ thuộc hoặc theo dõi trạng thái giao diện người dùng bằng Luồng Kotlin.

Ví dụ: 
Lưu trữ một Activity trong đối tượng đồng hành hoặc biến tĩnh.

Khắc phục:
Đừng giữ các tham chiếu tĩnh đến các thành phần trên giao diện người dùng. Cải tiến để sử dụng chèn phần phụ thuộc hoặc theo dõi trạng thái giao diện người dùng bằng luồng Kotlin.

Trình xử lý rò rỉ

Ví dụ:
Sử dụng DisposableEffect để bắt đầu một trình nghe nhưng để trống onDispose.

Khắc phục:
Thực hiện quy trình huỷ đăng ký và dọn dẹp bên trong khối onDispose.

Ví dụ:
Đăng ký nhận thông tin cập nhật từ SensorManager và quên huỷ đăng ký.

Khắc phục:
Gọi unregisterListener() theo cách thủ công trong vòng đời onStop() hoặc onDestroy().

Lộ thông tin về các thành phần hiển thị

Ví dụ:
Giữ một tham chiếu đến View cũ bên trong AndroidView mà không có chiến lược phát hành.


Khắc phục:
Sử dụng khối release của thành phần kết hợp AndroidView để dọn dẹp View cũ.

Ví dụ:
Giữ một tham chiếu đến đối tượng liên kết khung hiển thị sau khi Fragment bị huỷ.

 

Khắc phục:
Đặt biến liên kết thành null bên trong phương thức vòng đời onDestroyView().

Cắt bớt bộ nhớ khi ứng dụng rời khỏi trạng thái hiển thị

Android có thể thu hồi bộ nhớ của ứng dụng hoặc dừng ứng dụng hoàn toàn nếu cần để giải phóng bộ nhớ cho các tác vụ quan trọng, như giải thích trong phần Tổng quan về việc quản lý bộ nhớ. Android thường sẽ thu hồi bộ nhớ từ ứng dụng của bạn khi ứng dụng không hiển thị cho người dùng, chẳng hạn như bằng cách loại bỏ một số trang mã và dữ liệu của ứng dụng trong bộ nhớ hoặc nén các hoạt động phân bổ vùng nhớ khối xếp. Khi người dùng tiếp tục sử dụng ứng dụng của bạn và ứng dụng cố gắng truy cập vào một số bộ nhớ đã được thu hồi, hệ điều hành sẽ hoán đổi bộ nhớ đó theo yêu cầu. Hành vi hoán đổi này có thể diễn ra chậm và gây ra hiện tượng giật hoặc khựng không mong muốn trong ứng dụng của bạn.

Nếu để hệ điều hành quyết định bộ nhớ cần thu hồi từ ứng dụng của bạn, bạn có thể thấy rằng hệ điều hành đã thu hồi bộ nhớ mà bạn sẽ cần ngay sau khi tiếp tục ứng dụng. Thay vào đó, ứng dụng của bạn có thể tự nguyện loại bỏ các hoạt động phân bổ bộ nhớ mà ứng dụng có thể tạo lại sau này, theo yêu cầu và với chi phí thấp. Để làm việc này, bạn có thể triển khai giao diện ComponentCallbacks2. Bạn có thể triển khai onTrimMemory trong Activity, Fragment, Service hoặc thậm chí là lớp Application tuỳ chỉnh của bạn. Việc sử dụng lớp này trong lớp Application rất hiệu quả cho việc quản lý bộ nhớ đệm toàn cầu.

Phương thức gọi lại onTrimMemory() được cung cấp sẽ thông báo cho ứng dụng của bạn về các sự kiện liên quan đến vòng đời hoặc bộ nhớ. Đây là cơ hội tốt để ứng dụng của bạn tự nguyện giảm mức sử dụng bộ nhớ.

Về việc quản lý vòng đời bộ nhớ, quá trình triển khai của bạn chỉ nên tập trung vào TRIM_MEMORY_UI_HIDDENTRIM_MEMORY_BACKGROUND. Kể từ Android 14, hệ thống đã ngừng gửi thông báo cho các hằng số cũ khác. Các hằng số này chính thức không được dùng nữa trong Android 15.

TRIM_MEMORY_UI_HIDDEN: Tín hiệu này cho biết giao diện người dùng của ứng dụng đã chuyển ra khỏi chế độ xem của người dùng. Điều này tạo cơ hội giải phóng các hoạt động phân bổ bộ nhớ đáng kể chỉ liên kết chặt chẽ với giao diện, chẳng hạn như Bitmap, vùng đệm phát video hoặc tài nguyên hoạt ảnh phức tạp.

TRIM_MEMORY_BACKGROUND: Ở cấp độ này, quy trình của bạn đang nằm ở chế độ nền và hiện là đối tượng có thể bị chấm dứt để đáp ứng nhu cầu bộ nhớ chung của hệ thống. Để kéo dài thời gian quy trình của bạn ở trạng thái được lưu vào bộ nhớ đệm và giảm số lần khởi động nguội ứng dụng, bạn nên giải phóng mọi tài nguyên có thể dễ dàng tạo lại khi người dùng tiếp tục phiên hoạt động của họ.

import android.content.ComponentCallbacks2
// Other import statements.

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    override fun onTrimMemory(level: Int) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }

        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

Lưu ý: Việc tích hợp onTrimMemory có thể phụ thuộc vào khả năng hỗ trợ SDK. Ví dụ: một số trò chơi dựa vào công cụ phát triển trò chơi để bật tính năng này. Vui lòng xem tài liệu tối ưu hoá bộ nhớ trò chơi.

Khả năng quan sát bộ nhớ nâng cao bằng ProfilingManager

Để phát hiện và chẩn đoán các vấn đề về bộ nhớ trong trường hợp không thể tái tạo cục bộ, bạn nên tận dụng ProfilingManager API. Được ra mắt trong Android 15, API khả năng quan sát nâng cao này cho phép bạn thu thập các hồ sơ Perfetto của người dùng thực theo cách lập trình. 

Đối với những nhóm không có cơ sở hạ tầng chuyên dụng để quản lý và lưu trữ các cấu phần phần mềm hiệu suất, Crashlytics đang tìm hiểu một giải pháp chuyên biệt để đơn giản hoá quy trình này. Họ đang mời các nhà phát triển đưa ra ý kiến phản hồi.

Android 17 giới thiệu các điều kiện kích hoạt mới dựa trên sự kiện, đáng chú ý nhất là TRIGGER_TYPE_OOMTRIGGER_TYPE_ANOMALY:

  • Trình kích hoạt OOM tự động thu thập một tệp báo lỗi heap Java tại thời điểm chính xác xảy ra sự cố OutOfMemoryError, cung cấp các trạng thái phân bổ chính xác. Hồ sơ OOM đã thu thập sẽ được cung cấp vào lần tiếp theo ứng dụng khởi động và đăng ký lệnh gọi lại registerForAllProfilingResults.
  • Trình kích hoạt bất thường phát hiện các vấn đề nghiêm trọng về hiệu suất, chẳng hạn như tình trạng spam liên kết quá mức hoặc vượt quá ngưỡng bộ nhớ. Bất thường về bộ nhớ sẽ cung cấp một kết xuất vùng nhớ khối xếp ngay trước khi hệ thống kết thúc ứng dụng.
    val profilingManager = 
applicationContext.getSystemService(ProfilingManager::class.java)
    val triggers = ArrayList<ProfilingTrigger>()  


    triggers.add(ProfilingTrigger.Builder(
                 ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
    val mainExecutor: Executor = Executors.newSingleThreadExecutor()
    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
            // upload profile result to server for further analysis          
            setupProfileUploadWorker(profilingResult.resultFilePath)
        } 

    profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

Sau khi thu thập tệp báo lỗi, bạn có thể tải hồ sơ xuống từ máy chủ hoặc cục bộ thông qua adb rồi kéo và thả tệp vào Giao diện người dùng Perfetto. Để đơn giản hoá quy trình gỡ lỗi bộ nhớ, hãy sử dụng Heap Dump Explorer (Trình khám phá tệp báo lỗi). Đây là chế độ xem mặc định mới cho tệp báo lỗi trong giao diện người dùng Perfetto. Công cụ này cung cấp một giao diện trực quan để kiểm tra các kết xuất heap Java, cho phép bạn hình dung hệ phân cấp phân bổ đối tượng, tính toán kích thước bộ nhớ được giữ lại và xác định đường dẫn ngắn nhất từ gốc thu gom rác. Bằng cách tận dụng Heap Dump Explorer, bạn có thể nhanh chóng xác định chính xác các vấn đề rò rỉ bộ nhớ, các đối tượng được giữ lại có kích thước quá lớn (chẳng hạn như việc phân bổ bitmap quá mức) và phân tích việc phân bổ đối tượng heap ở cùng một nơi.

pic5-perfettoheapdump-analyzer.png
Sử dụng flamegraph được nhúng của Heap Dump Explorer để kiểm tra trực quan và di chuyển qua các đối tượng có mức phân bổ heap cao nhất.

Kết luận

Việc tối ưu hoá mã byte bằng R8, áp dụng các phương pháp hay nhất để tải hình ảnh và giải quyết tình trạng rò rỉ bộ nhớ là những bước quan trọng để mang lại trải nghiệm người dùng chất lượng cao, đồng thời quản lý tài nguyên một cách hiệu quả trong điều kiện áp lực. Việc áp dụng các biện pháp chủ động này giúp duy trì độ ổn định và hiệu suất của ứng dụng, ngăn chặn các trường hợp chấm dứt ngoài dự kiến trong khi bảo vệ bối cảnh người dùng. Để nâng cao kiến thức chuyên môn về hiệu suất, hãy khám phá hướng dẫn về bộ nhớ đã được sửa đổi của chúng tôi.

Tác giả:
Đọc tiếp