Uygulama bellek bütçeleri, uygulamaların kendileri için bir bellek bütçesi bildirmesine olanak tanır. Bu, uygulama belirlenen bütçesinden daha fazla bellek kullandığında sisteme bellek kullanımını azaltmasını söyler. Bu özellik özellikle sistem ve paketlenmiş uygulamalar veya geliştiricinin beklenen bellek çalışma kümesini bildiği ve uygulamasının sistemin paylaşılan RAM kaynaklarının çok fazla kullanmamasını istediği, bellek açısından kısıtlı cihazları hedefleyen uygulamalar için yararlıdır.
Bütçe, son zamanlarda kullanılmayan bellek sayfalarını kaldırmak için bellek çıkarma ve takas kullanılarak dengede tutulur. Böylece uygulamanın bellek ayak izi, mevcut çalışma kümesine odaklanır. Bir uygulama, beyan edilen bütçesini aştığında işletim sistemi hedefleri özellikle bu uygulamayı geri kazanır:
- Dosya destekli temiz sayfalar (ör. etkin olmayan kod ve eşlenmiş öğeler) gerekirse depolamadan yeniden okunabildikleri için önce çıkarılır.
- Dirty file-backed pages (Dosya destekli kirli sayfalar) depolamaya geri yazılır ve çıkarılır.
- Anonim bellek sayfaları (ör. yığın ayırmaları) sıkıştırılır ve zRAM'e takas edilir.
Bütçe, çalışma kümesini aşmadığı sürece uygulama, belirlenen bütçesindeki bellekten daha fazlasını kullanmadan iyi bir şekilde çalışır. İşletim sistemi, kullanılmayan belleği boşaltır ve etkin olmayan yığın sayfalarını takas için sıkıştırır. Böylece bellek ayırmaları, işlemi sonlandırmadan sınırlı kalır.
Android manifest dosyasında bütçeleri tanımlama
AndroidManifest.xml içinde bellek bütçelerinizi beyan etmek, bütçeleri tanımlamak için kullanılan birincil ve önerilen yöntemdir. Çalışma zamanı kodu gerektirmez, işlem başlatıldıktan hemen sonra geçerli olur ve işletim sistemi için net bir sözleşme sağlar.
<memory-budget> beyanları, Android 17 QPR2 (API düzeyi 37.2) ve sonraki sürümleri çalıştıran cihazlarda geçerli olur. Daha düşük Android sürümlerinde, platform manifesti ayrıştırıcısı tanınmayan XML öğelerini güvenli bir şekilde yoksayar. Bu nedenle, geriye dönük uyumluluğu etkilemeden <memory-budget> öğesini kullanabilirsiniz.
Referans bütçe beyan etme
Çoğu uygulama için, uygulama için tek bir bütçe tanımlamanız yeterlidir. <memory-budget> öğesini doğrudan <application> etiketinin içinde bildirin:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simpleapp">
<application
android:label="@string/app_name">
<!-- Baseline budget for the application -->
<memory-budget android:maxMb="256" />
</application>
</manifest>
Bu, paket için tüm işlemler ve durumlar genelinde 256 MB kalıcı bellek bütçesi belirler. Uygulamanın bellekte kapladığı yer 256 MB'ı aştığında işletim sistemi, etkin olmayan bellek sayfalarını çıkarma ve takas kullanarak kırpar.
Bütçeleri süreç durumuna göre değiştirme
Bir uygulamanın ihtiyaç duyduğu bellek miktarı, kullanıcı görünürlüğüne bağlı olarak değişir:
- Ön plan: İşlem, kullanıcıyla etkileşimde bulunan görünür bir Etkinliğe ev sahipliği yapıyor. Bu durum, etkin kullanıcı arayüzü ve grafikler nedeniyle genellikle en büyük ayak izine sahiptir.
- Algılanabilir: İşlem, kullanıcı tarafından algılanabilir ancak görünür bir pencere barındırmaz (ör. medya oynatma ön plan hizmeti, adım adım navigasyon veya etkin bir giriş yöntemi barındırma).
- Arka plan: İşlem, arka plan işlerini, alıcılarını veya veri senkronizasyonlarını çalıştırıyor. Minimum yer kaplaması beklenir.
Aşağıdaki durumlarla eşleşmek için birden fazla <memory-budget> ifadesi bildirebilirsiniz:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simpleapp">
<application
android:label="@string/app_name">
<!-- Default budget for visible foreground UI -->
<memory-budget android:maxMb="200" />
<!-- Tighter budget when playing audio in background -->
<memory-budget
android:maxMb="120"
android:state="perceptible" />
<!-- Minimal budget when fully in background -->
<memory-budget
android:maxMb="48"
android:state="background" />
</application>
</manifest>
android:state içermeyen temel bir madde gerekli değildir. Yalnızca eyalete özgü maddeler (örneğin, android:state="background") belirtirseniz diğer eyaletler uygulama bütçesiyle sınırlanmaz. Eklendiğinde, android:state içermeyen bir ifade, belirtilmemiş durumlar (ör. ön plan) için varsayılan geri dönüş olarak işlev görür. Uygulama perceptible veya background durumlarına geçtiğinde sonraki daha kısıtlayıcı ifadeler bu ifadeyi geçersiz kılar.
Çok işlemli uygulamalar
Uygulamanız işini birden fazla işleme bölüyorsa <processes> içinde <process> etiketini kullanarak ayrılmış işlem bütçeleri yapılandırın.
Örneğin, bir müzik dinleme uygulamasını (com.example.radio) ele alalım:
- Ana işlem: Görünür kullanıcı arayüzünü ve ses oynatma motorunu barındırır
(
MediaSessionServiceilemediaPlaybackön plan hizmeti). Görünür olduğunda işlem, 180 MB ön plan bütçesiyle çalışır. Kullanıcı, müzik çalmaya devam ederken uygulamadan çıktığında işlemperceptibledurumuna girer. Bu durumda, oynatma motoru ve ses arabelleği için 64 MB'lık bir bütçe yeterlidir. - Senkronizasyon süreci (
:sync): Arka planda meta veri senkronizasyonu ve indirme dizine ekleme işlemini yürüten özel süreç. Bu işlem yalnızca arka planda etkin olduğundanstate="background"değerini açıkça belirtmeniz gerekmez. Tek bir bütçe geçerlidir.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.radio">
<application
android:label="@string/app_name">
<!-- Package baseline: main process with UI and audio playback -->
<memory-budget android:maxMb="180" />
<!-- Tighter budget when audio plays in the background -->
<memory-budget
android:maxMb="64"
android:state="perceptible" />
<!-- Dedicated background sync process -->
<processes>
<process android:process=":sync">
<memory-budget android:maxMb="32" />
</process>
</processes>
<service
android:name=".playback.AudioPlayerService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
<service
android:name=".sync.PlaylistSyncService"
android:process=":sync"
android:exported="false" />
</application>
</manifest>
Herhangi bir alt süreçteki bellek kullanımı hem kendi süreç bütçesine hem de kapsayan paket bütçesine dahil edilir. Bir işlem, hangisi önce gerçekleşirse işlem bütçesini veya paket bütçesini aştığında çalışma zamanında bellek baskısı yaşar.
Yüksek yoğunluklu ekranlar için bütçeleri ölçeklendirme
Bellek ayak izi, ekranda aynı anda çizilecek piksel sayısına göre önemli ölçüde ölçeklenen uygulamalar (ör. ekrana göre boyutlandırılmış bit eşlemleri önbelleğe alan bir fotoğraf galerisi uygulaması) için Android, bütçeleri ekran özelliklerine göre dinamik olarak ölçeklendirmek üzere iki alternatif mekanizma sağlar:
Görüntü yoğunluğu grubuna göre ölçeklendirme (
android:additionalMbPerDensity):mdpi(1,0x / 160 dpi) değerine kıyasla ekranın yoğunluk oranına orantılı olarak megabayt ekler. Bu, bellek kullanımı, daha yüksek çözünürlüklü raster çizilebilir öğeleri veya kullanıcı arayüzü öğelerini önbelleğe alma gibi kullanıcı arayüzü yoğunluğu gruplarıyla ölçeklendiğinde uygundur:<!-- Baseline 180MB + 16MB per 1.0x density ratio --> <memory-budget android:maxMb="180" android:additionalMbPerDensity="16" />mdpiekranında (1,0x) bütçe 180 + 16 × 1 = 196 MB'tır.xxhdpiekranında (3,0x), bütçe 180 + 16 × 3 = 228 MB olarak ölçeklendirilir.Fiziksel ekran çözünürlüğüne göre ölçeklendirme (
android:additionalBytesPerDisplayPixel): Baytları doğrudan fiziksel ekran pikseli başına (Genişlik × Yükseklik) ekler. Bu, aşağıdaki durumlarda idealdir: Bellek tüketiminin kullanıcı arayüzü yoğunluğu grupları yerine doğrudan ham ekran piksel sayısı ile ölçeklendiği tam ekran grafik yüzeyleri, oluşturma arabellekleri veya tam çözünürlüklü fotoğraf önbellekleri ayıran uygulamalar:<!-- Baseline 128MB + 16 bytes per physical display pixel --> <!-- For example, a 4-byte RGBA full-screen buffer with double or quadruple buffering --> <memory-budget android:maxMb="128" android:additionalBytesPerDisplayPixel="16" />1080p ekranda (1080 × 2400 ≈ 2,59 milyon piksel) bu, temel bütçeye yaklaşık 41,4 MB ekler. 1440p ekranda (1440 × 3120 ≈ 4,49 milyon piksel) yaklaşık 71,8 MB ekler.
Bu iki özellik alternatiflidir. Uygulamanızın birincil ölçeklendirme faktörüyle eşleşen özelliği seçin ve her ikisini de aynı maddede birleştirmeyin.
Cihaz form faktörlerine göre uzmanlaşma
Telefon, tablet ve Wear OS'te APK gönderirken farklı donanım hedefleri için bütçeleri ayarlamak üzere android:feature özelliğini kullanın.
Wear OS kol saatlerinde RAM sınırlıdır ve uygulamanın kullanıcı arayüzü ile özellik seti çok daha basittir. watch özelliği için daha sıkı bir bütçe belirleyebilirsiniz:
<!-- General phone and tablet baseline -->
<memory-budget android:maxMb="180" />
<!-- Wear OS override: simpler UI and constrained hardware -->
<memory-budget
android:maxMb="48"
android:feature="watch" />
Çözüm kuralı: Geçerli olan son madde yürürlüğe girer
Bir uygulama veya süreç için birden fazla <memory-budget> öğesi tanımlarken sistem bunları manifest dosyasında bildirildikleri sırayla değerlendirir. Geçerli olan son bütçe maddesi uygulanır.
Son geçerli bütçe kazandığı için sıralama önemlidir. En genel temel bütçeyi her zaman önce, ardından daha spesifik geçersiz kılma işlemlerini (ör. eyalete özgü veya donanıma özgü maddeler) yerleştirin.
XML özelliği referansı
Tüm bellek boyutu özellikleri megabayt (MB) cinsinden ifade edilir ve Linux cgroup memory.current ücretiyle (Zygote gibi paylaşılan bellek hariç) eşlenir.
| Özellik | Biçim | Varsayılan | Açıklama |
|---|---|---|---|
android:maxMb |
Tam sayı (> 0) | Zorunlu | MB cinsinden temel yerleşik bellek bütçe sınırı. |
android:state |
Enum | Tümü | Bu bütçenin geçerli olduğu süreç durumu: foreground, perceptible veya background. Atlanırsa madde, belirtilmeyen tüm durumlar için yedek olarak işlev görür. |
android:additionalMbPerDensity |
Tam sayı (≥ 0) | 0 |
mdpi (1,0x) değerine kıyasla görüntü yoğunluğu oranı birimi başına eklenecek ek megabayt. |
android:additionalBytesPerDisplayPixel |
Tam sayı (≥ 0) | 0 |
Yüzey arabellekleri ve bit eşlemler için yararlı olan, fiziksel ekran pikseli başına (Genişlik × Yükseklik) ayrılan ek baytlar. |
android:feature |
Dize | Tümü | Maddeyi, belirli donanım özelliklerini (watch, automotive veya leanback) bildiren cihazlarla sınırlandırır. |
Çalışma zamanı API'leri (ikincil dinamik seçenek)
AndroidManifest.xml içinde bütçeleri statik olarak bildirmek, neredeyse tüm uygulamalar için tercih edilen çözümdür. Ancak dinamik iş yüklerine sahip uygulamalar veya çalışma zamanı denemeleri için Android, ikincil seçenek olarak çalışma zamanı SDK'sı ve NDK API'leri sunar.
Çalışma zamanı API'si şunları yapmanıza olanak tanır:
- Mevcut bellek kullanımını ve etkin bütçeleri sorgulayın.
- İşlem bütçenizi dinamik olarak aşağı yönlü ayarlayın.
- İşletim sistemi doğrudan geri kazanmayı tetiklemeden önce önbellekleri proaktif olarak temizlemek için bütçeyi aşan etkinlikleri dinleyin.
Android SDK API'si (MemoryBudgetManager)
MemoryBudgetManager sisteme ait hizmet, Android 17 QPR2'den (küçük SDK sürümü, API düzeyi 37.2 / Build.VERSION_CODES_FULL.CINNAMON_BUN_2) itibaren Kotlin ve Java ile yazılmış uygulamalarda kullanılabilir.
Hizmeti geri alma
MemoryBudgetManager'ye erişmeden önce SDK_INT_FULL kullanarak cihazda Android 17 QPR2'den daha eski bir sürümün çalışmadığını kontrol edin:
if (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_2) {
val budgetManager = context.getSystemService(MemoryBudgetManager::class.java)
}
Sorgu kullanımı ve bütçeler
// Query current memory charged to this process and the package UID
val processUsageBytes = budgetManager.processCurrentUsageBytes
val packageUsageBytes = budgetManager.packageCurrentUsageBytes
// Query effective budgets (returns LIMIT_IS_DISABLED if unconstrained)
val processBudgetBytes = budgetManager.processBudgetBytes
val packageBudgetBytes = budgetManager.packageBudgetBytes
Bütçeleri dinamik olarak ayarlama veya temizleme
Hafif görevler sırasında belleği kısıtlamak için çalışma zamanında daha sıkı bir bütçe belirleyebilir veya görev tamamlandığında bütçeyi temizleyebilirsiniz:
// Set a tighter dynamic budget on the current process (e.g., 96 MB)
try {
budgetManager.processBudgetBytes = 96L * 1024L * 1024L
} catch (e: IllegalArgumentException) {
// Thrown if the budget is <= 0 or exceeds the manifest ceiling or system limit
Log.e(TAG, "Requested budget exceeds manifest or system ceiling", e)
}
// Clear the dynamic process budget to restore the manifest limit (or unconstrained baseline)
budgetManager.clearProcessBudget()
Bütçe aşımı baskısı geri çağırmalarını dinleme
Uygulamalar, bellek kullanımı bütçe eşiğini aştığında bildirim almak için bir dinleyici kaydedebilir. Bu, işletim sistemi doğrudan geri kazanma gecikmesini tetiklemeden önce uygulamanın proaktif uygulama düzeyinde temizlik (ör. bellek içi bit eşlem önbelleklerini temizleme) yapmasına olanak tanır:
val listener = MemoryBudgetManager.OnOverBudgetListener { budgetBytes ->
Log.w(TAG, "Process exceeded memory budget of $budgetBytes bytes")
// Proactively evict caches to release memory
imageTileCache.evictAll()
}
// Register on the main Looper
budgetManager.registerProcessOverBudgetListener(mainLooper, listener)
// When done (e.g., in onStop)
budgetManager.unregisterProcessOverBudgetListener(listener)
Bütçeyi aşan geri aramalarla ilgili en iyi uygulamalar:
- Hızlı olun: Geri alma işlemleri anında rahatlama sağlamalıdır. Bilişim yükü sırasında yapılan karmaşık hesaplamalar performansı düşürür.
- Ayırmalardan kaçının: Geri çağırma içinde yeni nesneler ayırmayın veya yeni iş parçacıkları başlatmayın. Aksi takdirde, işletim sistemi doğrudan geri isteme işlemi hemen tetiklenebilir.
- Yüksek verimli hedeflere odaklanın: Büyük bit eşlemleri, oluşturma arabelleklerini kaldırmak veya belleğe eşlenmiş dosyaları kapatmak, çok sayıda küçük nesneyi serbest bırakmaktan çok daha etkilidir.
Yerel NDK API'si (<android/memory_budget_manager.h>)
Yerel uygulamalar, Android 17 QPR2'den (API düzeyi 37.2) itibaren libandroid.so tarafından kullanıma sunulan C NDK API'sini kullanabilir.
CMake yapılandırması
find_library(android-lib android)
target_link_libraries(my_native_engine PRIVATE ${android-lib})
Başlık ve sorgu kullanımını dahil etme
#include <android/memory_budget_manager.h>
// Query current memory usage
int64_t process_usage = AMemoryBudgetManager_getProcessCurrentUsageBytes();
int64_t package_usage = AMemoryBudgetManager_getPackageCurrentUsageBytes();
// Query current budget
int64_t process_budget = 0;
AMemoryBudgetResult result = AMemoryBudgetManager_getProcessBudget(&process_budget);
if (result == AMEMORY_BUDGET_RESULT_SUCCESS) {
// Current budget available in process_budget
} else if (result == AMEMORY_BUDGET_RESULT_LIMIT_IS_DISABLED) {
// No budget is currently active
}
Yerel bütçeyi dinamik olarak yapılandırma
// Set a tighter process budget (e.g. 160MB)
AMemoryBudgetResult result = AMemoryBudgetManager_setProcessBudget(160LL * 1024 * 1024);
if (result != AMEMORY_BUDGET_RESULT_SUCCESS) {
const char* error_msg = AMemoryBudgetManager_resultToString(result);
// Handle error (e.g. AMEMORY_BUDGET_RESULT_ERROR_EXCEEDS_MANIFEST_LIMIT)
}
// Clear the dynamic budget to resume manifest limits (or unconstrained baseline)
AMemoryBudgetManager_clearProcessBudget();
Bellek baskısı etkinliklerini izleme
NDK, bellek etkinliklerini izlemek için iki yöntem sunar:
- Üst Düzey İzleyici (
AMemoryBudgetManager_Watcher_create):ALooperüzerindeki etkinlikleri otomatik ardışık tekrar önleme ile izler. - Düşük Düzeyli Dosya Tanımlayıcısı:
AMemoryBudgetManager_getProcessMemoryPressureFd, doğrudan özel birepollmotor döngüsüne entegre edilebilen yerel bir dosya tanımlayıcısı döndürür.
void onMemoryPressure(int32_t event_mask, const AMemoryBudgetEvents* events, void* userdata) {
// High-yield eviction of unused native textures or geometry caches
purgeNativeTextureCaches();
}
// Register watcher on an ALooper with a 1000ms debounce interval
AMemoryBudgetManagerWatcher* watcher = AMemoryBudgetManager_Watcher_create(
looper,
AMEMORY_BUDGET_MANAGER_EVENT_PROCESS,
1000 /* debounce_ms */,
&onMemoryPressure,
NULL /* userdata */
);
// When done:
AMemoryBudgetManager_Watcher_destroy(watcher);
Çalışma zamanı API'si örnekleri
Aşağıdaki örneklerde, Android SDK API'si (Kotlin ile yazılmıştır) ve Native NDK API'si (C++ ile yazılmıştır) kullanılarak çalışma zamanı API'lerinin nasıl uygulanacağı gösterilmektedir.
Android SDK örneği: Uyarlanabilir resim düzenleyici
Bu örnekte, çok katmanlı tuvalini barındırmak için manifest dosyasında 256 MB sınır beyan eden bir resim düzenleme uygulaması (com.example.imageeditor) gösterilmektedir:
<manifest ... >
<application ... >
<!-- Manifest ceiling accommodates the heaviest editing workload -->
<memory-budget android:maxMb="256" />
</application>
</manifest>
Kullanıcı, küçük resim galerisine göz atarken uygulama, işlem bütçesini dinamik olarak 96 MB'a düşürmek için Kotlin'deki Android SDK API'sini kullanır.
Kullanıcı çok katmanlı düzenleme tuvalini açtığında uygulama, 256 MB'lık tam bildirim tavanını geri yüklemek için dinamik bütçeyi temizler. Ayrıca, önbelleğe alınmış önizleme bit eşlemlerini baskı altında çıkarmak için bir OnOverBudgetListener kaydeder.
package com.example.imageeditor.ui
import android.app.Activity
import android.app.MemoryBudgetManager
import android.graphics.Bitmap
import android.os.Bundle
import android.util.Log
import android.util.LruCache
class ImageEditorActivity : Activity() {
private lateinit var budgetManager: MemoryBudgetManager
// In-memory cache for rendered preview tiles (32MB limit)
private val previewCache = object : LruCache<String, Bitmap>(32 * 1024 * 1024) {
override fun sizeOf(key: String, value: Bitmap): Int = value.byteCount
}
private val overBudgetListener = MemoryBudgetManager.OnOverBudgetListener { budgetBytes ->
Log.w(TAG, "Process memory pressure detected (budget: ${budgetBytes / 1048576}MB). Evicting preview cache.")
previewCache.evictAll()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
budgetManager = getSystemService(MemoryBudgetManager::class.java)
}
override fun onStart() {
super.onStart()
// Register listener for process-level memory breaches
budgetManager.registerProcessOverBudgetListener(mainLooper, overBudgetListener)
// Constrain memory during lightweight gallery browsing
applyGalleryBudget()
}
override fun onStop() {
super.onStop()
budgetManager.unregisterProcessOverBudgetListener(overBudgetListener)
}
/**
* Called when the user enters the high-resolution editing canvas.
* Clears the dynamic budget, restoring the full 256MB manifest ceiling.
*/
fun enterEditingCanvas() {
// Clear the tighter dynamic budget to restore the full manifest ceiling (256MB)
budgetManager.clearProcessBudget()
Log.i(TAG, "Restored manifest budget ceiling (256MB) for editing canvas")
}
/**
* Called when the user exits the editor back to the thumbnail gallery.
* Re-applies the tighter dynamic budget.
*/
fun exitToGallery() {
previewCache.trimToSize(8 * 1024 * 1024)
applyGalleryBudget()
}
private fun applyGalleryBudget() {
try {
// Dynamically tighten budget to 96MB for the lightweight gallery view
budgetManager.processBudgetBytes = 96L * 1024L * 1024L
Log.i(TAG, "Tighter dynamic budget applied for gallery: 96MB")
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Could not apply dynamic budget", e)
}
}
companion object {
private const val TAG = "ImageEditor"
}
}
NDK C++ örneği: Yerel 3D motor
Bu örnekte, uygulamanın manifest dosyasında yüksek kaliteli grafiklere (android:maxMb="512") yer vermek için 512 MB'lık bir sınır tanımlandığı varsayılarak, etkin grafik kalite seviyesine göre bellek bütçelerini yöneten yerel bir C++ oyun motoru gösterilmektedir. Motor, daha düşük kaliteli hazır ayarlar için bütçeyi dinamik olarak daraltır ve bütçeyi aşması durumunda doku mipmap'lerini boşaltmak için ALooper üzerinde AMemoryBudgetManager_Watcher_create kullanır.
#include <android/memory_budget_manager.h>
#include <android/looper.h>
#include <android/log.h>
#define LOG_TAG "Native3DEngineMemory"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
class MemoryGovernor {
public:
MemoryGovernor() : mWatcher(nullptr) {}
~MemoryGovernor() {
stopMonitoring();
}
// Configures process budget based on user graphics quality settings
bool setQualityBudget(int qualityLevel) {
int64_t targetBytes = 0;
switch (qualityLevel) {
case 0: // Low (budget: 128MB)
targetBytes = 128LL * 1024 * 1024;
break;
case 1: // Medium (budget: 256MB)
targetBytes = 256LL * 1024 * 1024;
break;
case 2: // High (budget: 512MB)
targetBytes = 512LL * 1024 * 1024;
break;
default:
// Clear dynamic override and restore manifest limit
AMemoryBudgetManager_clearProcessBudget();
return true;
}
AMemoryBudgetResult result = AMemoryBudgetManager_setProcessBudget(targetBytes);
if (result != AMEMORY_BUDGET_RESULT_SUCCESS) {
LOGW("Could not set quality budget: %s", AMemoryBudgetManager_resultToString(result));
return false;
}
return true;
}
bool startMonitoring(ALooper* looper) {
if (!looper) return false;
// Monitor process budget events, debounced to at most once every 1000ms
mWatcher = AMemoryBudgetManager_Watcher_create(
looper,
AMEMORY_BUDGET_MANAGER_EVENT_PROCESS,
1000,
&MemoryGovernor::onPressureEvent,
this
);
return mWatcher != nullptr;
}
void stopMonitoring() {
if (mWatcher) {
AMemoryBudgetManager_Watcher_destroy(mWatcher);
mWatcher = nullptr;
}
}
void unloadUnusedTextures() {
LOGW("Memory pressure callback triggered. Purging cached texture mipmaps...");
// Fast, high-yield eviction without allocating memory
}
private:
static void onPressureEvent(
int32_t event_mask,
const AMemoryBudgetEvents* events,
void* userdata
) {
auto* governor = static_cast<MemoryGovernor*>(userdata);
governor->unloadUnusedTextures();
}
AMemoryBudgetManagerWatcher* mWatcher;
};