Android 15 (API düzeyi 35) ve sonraki sürümlerde, uygulama tabanlı profil oluşturma özelliği sayesinde uygulamanız, ProfilingManager API'sini veya AndroidX Core (androidx.core:core 1.15.0 veya sonraki sürümler) derleyicilerini kullanarak kodda desteklenen profil kayıtlarını açıkça başlatıp durdurabilir. Profil oluşturma zamanlamasını veya oturum parametrelerini (ör. süreler, arabellek politikaları ve örnekleme hızları) kontrol etmek ya da belirli bir kullanıcı yolculuğunu veya çalışma zamanı koşulunu hedeflemek istediğinizde doğrudan profil oluşturmayı kullanın.
Bağımlılık ekleme
ProfilingManager API'sini en iyi şekilde kullanmak için aşağıdaki Jetpack kitaplıklarını build.gradle.kts dosyanıza ekleyin.
Kotlin
dependencies { implementation("androidx.tracing:tracing-ktx:2.0.5") implementation("androidx.core:core:1.19.1") }
Modern
dependencies { implementation 'androidx.tracing:tracing-ktx:2.0.5' implementation 'androidx.core:core:1.19.1' }
Sistem izleme kaydetme
Gerekli bağımlılıkları ekledikten sonra sistem izi kaydetmek için aşağıdaki kodu kullanın. Bu örnekte, ana iş parçacığı dışındaki yoğun işlemleri güvenli bir şekilde yönetirken profil oluşturma oturumunun nasıl başlatılacağı gösterilmektedir.
Kotlin
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @Composable fun ProfiledScreen(modifier: Modifier = Modifier) { // Use the application context: requestProfiling resolves the ProfilingManager // system service from it, so there's no reason to hand it a short-lived Activity. val appContext = LocalContext.current.applicationContext val scope = rememberCoroutineScope() Button( onClick = { // Run the orchestration off the main thread. Profiling a heavy operation // on the UI thread would freeze the UI (ANR) and distort the very metrics // you're trying to capture. // // Note: this scope is tied to composition. If the user leaves this screen // mid-session, the coroutine is cancelled and stopSignal.cancel() might not // run, but setDurationMs() acts as a safety net and ends the profile. scope.launch(Dispatchers.Default) { val callbackExecutor = Dispatchers.IO.asExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d("ProfileTest", "Result file: ${profilingResult.resultFilePath}") } else { // errorMessage explains the failure (e.g., rate limiting); keep it. Log.e( "ProfileTest", "Profiling failed errorCode=${profilingResult.errorCode} " + "errorMessage=${profilingResult.errorMessage}" ) } } val stopSignal = CancellationSignal() val requestBuilder = SystemTraceRequestBuilder().apply { setCancellationSignal(stopSignal) setTag("FOO") // Caller-supplied tag for identification. setDurationMs(60000) // Hard cap: ends the session if cancel() never fires. setBufferFillPolicy(BufferFillPolicy.RING_BUFFER) setBufferSizeKb(32768) } // 1. Start the session. This is asynchronous system IPC. The tracing // engine takes a moment to start and allocate buffers. requestProfiling(appContext, requestBuilder.build(), callbackExecutor, resultCallback) // 2. The API exposes no "profiling started" signal, so pad with a short, // best-effort delay before running the code you care about. This is // approximate. Increase it on slower or heavily loaded devices. delay(STARTUP_PADDING_MS) // 3. The session is already recording every thread in your app. This slice // doesn't scope what's captured. It just labels this region of the // timeline so heavyOperation() is easier to find. trace { } closes the // section even if the block throws. trace("MyApp:HeavyOperation") { heavyOperation() } // 4. Stop recording. Until this fires or the setDurationMs() cap is // reached (whichever comes first), the session keeps capturing app-wide // activity. stopSignal.cancel() } } ) { Text("Run & Profile Heavy Operation") } } // Best-effort wait for the system trace engine to initialize before profiling. // There is no deterministic start callback; tune this for your target devices. private const val STARTUP_PADDING_MS = 100L fun heavyOperation() { // Background computations to profile. }
Java
void sampleRecordSystemTrace(Context context) { // Use the application context: requestProfiling resolves the ProfilingManager // system service from it, so there's no reason to hand it a short-lived Activity. Context appContext = context.getApplicationContext(); // Run the orchestration off the main thread. Profiling a heavy operation // on the UI thread would freeze the UI (ANR) and distort the very metrics // you're trying to capture. profilingExecutor.execute(() -> { Executor callbackExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = profilingResult -> { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d("ProfileTest", "Result file: " + profilingResult.getResultFilePath()); } else { // errorMessage explains the failure (e.g., rate limiting); keep it. Log.e( "ProfileTest", "Profiling failed errorCode=" + profilingResult.getErrorCode() + " errorMessage=" + profilingResult.getErrorMessage()); } }; CancellationSignal stopSignal = new CancellationSignal(); SystemTraceRequestBuilder requestBuilder = new SystemTraceRequestBuilder(); requestBuilder.setCancellationSignal(stopSignal); requestBuilder.setTag("FOO"); // Caller-supplied tag for identification. requestBuilder.setDurationMs(60000); // Hard cap: ends the session if cancel() never fires. requestBuilder.setBufferFillPolicy(BufferFillPolicy.RING_BUFFER); requestBuilder.setBufferSizeKb(32768); // 1. Start the session. This is asynchronous system IPC. The tracing // engine takes a moment to start and allocate buffers. Profiling.requestProfiling( appContext, requestBuilder.build(), callbackExecutor, resultCallback); // 2. The API exposes no "profiling started" signal, so pad with a short, // best-effort delay before running the code you care about. This is // approximate. Increase it on slower or heavily loaded devices. try { Thread.sleep(STARTUP_PADDING_MS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); stopSignal.cancel(); return; } // 3. The session is already recording every thread in your app. This slice // doesn't scope what's captured. It just labels this region of the // timeline so heavyOperation() is easier to find. try/finally closes the // section even if heavyOperation() throws. Trace.beginSection("MyApp:HeavyOperation"); try { heavyOperation(); } finally { Trace.endSection(); } // 4. Stop recording. Until this fires or the setDurationMs() cap is // reached (whichever comes first), the session keeps capturing app-wide // activity. stopSignal.cancel(); }); } // Best-effort wait for the system trace engine to initialize before profiling. // There is no deterministic start callback; tune this for your target devices. private static final long STARTUP_PADDING_MS = 100L; // Single background thread that runs the profiling orchestration. private final ExecutorService profilingExecutor = Executors.newSingleThreadExecutor(); void heavyOperation() { // Background computations to profile. }
Örnek kod, profil oluşturma oturumunu aşağıdaki adımlarda ayarlar ve yönetir:
- Yürütücüyü ayarlayın: Profil oluşturma sonuçlarını ana iş parçacığından almak için kullanıcı arayüzü olmayan bir
Executoroluşturun. - Profil oluşturma sonuçlarını işleme:
ProfilingManager'ten tamamlanan sonucu veya hatayı almak için birConsumer<ProfilingResult>geri çağırma işlevi oluşturun. Profil oluşturma isteğini oluşturun: Oturum ayarlarını özelleştirmek için bir
SystemTraceRequestBuilderoluşturun (veya sistem varsayılanlarını kullanmak için özelleştirmeleri atlayın):setTag():ProfilingResult.getTagtarafından döndürülen ve çıkış dosya adına eklenen, arayan tarafından sağlanan bir etiket ekler (ilk 20 küçük harfli alfanümerik karakter ve kısa çizgiye kadar).setDurationMs(): Maksimum kayıt süresini milisaniye cinsinden ayarlar (örneğin, 60 saniye için60000).setBufferFillPolicy(): Arabelleğin kapasite sınırlarını nasıl işlediğini tanımlar:BufferFillPolicy.RING_BUFFER, dolduğunda en eski verilerin üzerine yazar,BufferFillPolicy.DISCARDise arabellek dolduğunda yeni verileri kaydetmeyi durdurur.setBufferSizeKb(): Kayıt arabellek boyutunu kilobayt cinsinden ayarlar.
İsteğe bağlı: Kaydı erken durdurun: Hedef işlem biter bitmez oturumu durdurmak, o ana kadar kaydedilen verileri kaydetmek ve
ProfilingResultdeğerini geri çağırma işlevinize iletmek için birCancellationSignal(stopSignal) iletin. Ayrıntılar için Etkin bir kaydı durdurma başlıklı makaleyi inceleyin.Başlatma ve sonuçları alma: Arka planda kaydı başlatmak için
requestProfiling()işlevini çağırın. Oturum sona erdiğinderesultCallback.accept(),ProfilingResultalır:- İşlem başarılı olursa (
ERROR_NONE) cihaz üzerindeki yolu almak içingetResultFilePath()yöntemini çağırın.adbile dosya alma veyaWorkManagerile dosya yükleme talimatları için Profilleri alma ve yükleme başlıklı makaleyi inceleyin. - Hata durumunda
getErrorCode()vegetErrorMessage()dosyalarını inceleyin. İstekler, sıklık sınırlaması ve diğer çeşitli nedenlerden dolayı başarısız olabilir. - Uygulamanız sonuç yayınlanmadan önce sonlandırılırsa sistem sonucu sıraya alır ve uygulamanız
registerForAllProfilingResultsile bir genel dinleyici kaydettiğinde yayınlar.
- İşlem başarılı olursa (
Özel izleme noktaları ekleme: Zaman çizelgesinin bölgelerini etiketlemek için işlemleri
trace("...") { ... }(veyaTrace.beginSection()/Trace.endSection()) ile sarmalayın.
Etkin bir kaydı durdurma
setDurationMs() süresinin dolmasını beklemek yerine bir etkileşim tamamlanır tamamlanmaz yakalamayı durdurmak için setCancellationSignal() veya requestProfiling() yöntemine bir CancellationSignal iletin. Hem CancellationSignal hem de setDurationMs() değerini belirtirseniz hangisi önce gerçekleşirse oturum sona erer. İkisini de belirtmezseniz sistem varsayılan bir süre uygular.
cancel() numarasını aradığınızda kayıt silinmez. Sistem, yakalama işlemini durdurur, o ana kadar toplanan verileri çıkartıp kaydeder ve ProfilingResult öğesini ERROR_NONE ile dinleyicinize gönderir.
Çağrı yığını örneklemesiyle sistem izlerini kaydetme
Android 17 (API düzeyi 37) ve sonraki sürümleri (veya güncellenmiş Profiling ana modülüne sahip Android 16) çalıştıran cihazlarda ProfilingManager, tek bir oturumda sistem izleme ile birlikte örneklenmiş çağrı yığınlarının toplanmasını destekler. Böylece, açık izleme araçları olmayan yöntemler için CPU çağrı yığınlarıyla birlikte iş parçacığı durumları, kareler ve özel izleme dilimleri gibi zaman çizelgesi etkinliklerini görebilirsiniz.
Bu örnekte, desteklenen profil türlerinden (PROFILING_TYPE_SYSTEM_TRACE) birini belirterek ve bir yapılandırma Bundle ileterek Jetpack oluşturucuları olmadan doğrudan ProfilingManager.requestProfiling yönteminin nasıl çağrılacağı da gösterilmektedir:
Kotlin
val profilingManager = context.getSystemService(ProfilingManager::class.java) // Pass a Bundle to customize the capture, or pass null for default settings. val config = Bundle().apply { putInt("KEY_DURATION_MS", 15000) // 15 seconds putInt("KEY_SIZE_KB", 20480) // 20 MB buffer size // Collect sampled CPU call stacks alongside the system trace. putBoolean("KEY_COLLECT_STACK_SAMPLING", true) putInt("KEY_FREQUENCY_HZ", 100) // Sampling frequency in Hz // Optional: Set KEY_SAMPLE_BINDER_ONLY to true to sample only binder calls. } profilingManager?.requestProfiling( ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, config, "trace-with-callstacks", null, callbackExecutor, resultCallback )
Java
ProfilingManager profilingManager = context.getSystemService(ProfilingManager.class); if (profilingManager == null) return; // Pass a Bundle to customize the capture, or pass null for default settings. Bundle config = new Bundle(); config.putInt("KEY_DURATION_MS", 15000); // 15 seconds config.putInt("KEY_SIZE_KB", 20480); // 20 MB buffer size // Collect sampled CPU call stacks alongside the system trace. config.putBoolean("KEY_COLLECT_STACK_SAMPLING", true); config.putInt("KEY_FREQUENCY_HZ", 100); // Sampling frequency in Hz // Optional: Set KEY_SAMPLE_BINDER_ONLY to true to sample only binder calls. profilingManager.requestProfiling( ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, config, "trace-with-callstacks", null, callbackExecutor, resultCallback );
Elde edilen profili Perfetto kullanıcı arayüzünde açtığınızda, örneklenen çağrı yığınları uygulamanızın normal izleme dilimlerinin yanında özel Çağrı yığınları kanalları olarak görünür. Şekil 1'de gösterildiği gibi, ilgili zaman aralığı için örneklenen çağrı yığınlarının alev grafiğini görüntülemek üzere çağrı yığını parçasının bir bölgesini seçin.
Java yığın dökümü yakalama
JavaHeapDumpRequestBuilder (veya PROFILING_TYPE_JAVA_HEAP_DUMP) kullanarak uygulamanızın Java belleğinin anlık görüntüsünü isteyebilirsiniz.
Debug.dumpHprofData ile yakalanan standart .hprof yığın dökümlerinin aksine, ProfilingManager yığın dökümleri üretimde güvenli ve düşük ek yükle toplama için tasarlanmıştır:
- Donmayan toplama: Sistem, uygulamanızın işleminin yazma sırasında kopyalama klonunu oluşturur ve yığın anlık görüntüsünü klonlanan işlemden toplar. Böylece, döküm diske yazılırken uygulamanız donmaz.
- Yalnızca referans grafiği (ham nesne verisi yok):
ProfilingManager,.hprofdosyası gibi ham bellek içeriklerini dökümlemek yerine sınıflar, nesne boyutları, referans zincirleri ve çöp toplama (GC) köklerini içeren bir.perfetto-java-heap-dumpprotobuf grafiği kaydeder. İlkel değerler, dize içerikleri ve dizi yükleri ise atlanır. Bu, kullanıcı gizliliğini korur ve dosya boyutunu küçültür. Tam döküm için ProfilingManager yığın dökümlerinin HPROF dökümlerinden farkı başlıklı makaleyi inceleyin.
Sistem izleme kaydetme bölümünde gösterilen Context, Executor ve resultCallback kurulumunu kullanarak JavaHeapDumpRequestBuilder ile bir istek oluşturun:
Kotlin
val heapDumpRequest = JavaHeapDumpRequestBuilder() .setTag("memory-leak-check") .build() requestProfiling( appContext, heapDumpRequest, callbackExecutor, resultCallback )
Java
ProfilingRequest heapDumpRequest = new JavaHeapDumpRequestBuilder() .setTag("memory-leak-check") .build(); Profiling.requestProfiling( getApplicationContext(), heapDumpRequest, callbackExecutor, resultCallback );
Bellek sızıntısını teşhis etmek için Java yığın dökümünü analiz etme sürecinin tamamını adım adım incelemek isterseniz Örnek olay: OOM kilitlenmesinde hata ayıklama başlıklı makaleye göz atın.
Sonraki adımlar
Profilleri yakaladıktan sonra adb ile yerel olarak alın veya WorkManager ile arka ucunuza yükleyin.