Android 16(API 수준 36) 이상에서는 ProfilingManager를 사용하여 애플리케이션 응답 없음(ANR) 오류, 메모리 부족(OutOfMemoryError) 비정상 종료, 앱 시작 이벤트와 같은 시스템 트리거에 응답하여 프로필을 자동으로 캡처할 수 있습니다. 이러한 이벤트가 현장에서 언제 발생할지 예측할 수 없으므로 사전에 수동으로 녹화를 시작하는 것은 비현실적입니다. 트리거 기반 프로파일링을 사용하면 시스템에서 캡처를 관리하고 결과 프로필을 앱에 전송하므로 다음 작업을 할 수 있습니다.
- 안정성 문제 디버그: 예측할 수 없는 ANR 및 메모리 누수를 진단합니다.
- 중요한 사용자 여정(CUJ) 최적화: 앱 시작(
TRIGGER_TYPE_COLD_START및TRIGGER_TYPE_APP_FULLY_DRAWN)과 같은 흐름을 분석합니다. - 사용자 행동 이해: 사용자가 시작한 앱 종료와 같은 이벤트를 검사합니다.
사용 가능한 트리거의 전체 목록은 ProfilingTrigger를 참고하세요.
트리거 유형
트리거는 이벤트가 발생할 때 시스템이 프로필을 캡처하는 방식에 따라 다릅니다.
백그라운드 trace를 사용하는 트리거
ANR과 같은 많은 트리거는 이전 문제의 결과입니다. 시스템이 ANR을 감지한 후에만 트레이스를 시작하면 ANR을 일으킨 차단 작업을 놓치게 됩니다. 이벤트로 이어지는 활동을 캡처하기 위해 다음 트리거는 시스템의 활성 백그라운드 트레이스의 스냅샷을 저장합니다. 이벤트가 발생할 때 백그라운드 트레이스가 실행되지 않으면 프로필이 수집되지 않습니다.
TRIGGER_TYPE_ANR(API 수준 36)TRIGGER_TYPE_APP_FULLY_DRAWN(API 수준 36)TRIGGER_TYPE_APP_REQUEST_RUNNING_TRACE(API 수준 36.1)TRIGGER_TYPE_KILL_FORCE_STOP(API 수준 36.1)TRIGGER_TYPE_KILL_RECENTS(API 수준 36.1)TRIGGER_TYPE_KILL_TASK_MANAGER(API 수준 36.1)TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE(API 수준 37)
새 캡처를 시작하는 트리거
다음 트리거는 백그라운드 trace에 종속되지 않습니다. 대신 이벤트가 발생하면 시스템에서 새 프로필 캡처를 시작합니다.
TRIGGER_TYPE_COLD_START(API 수준 37)TRIGGER_TYPE_OOM(API 수준 37)
이상치에 따라 캡처 동작이 달라지는 트리거
다음 이상 감지 트리거의 경우 캡처된 프로필 유형(활성 백그라운드 트레이스의 스냅샷 저장 또는 새 캡처 시작)과 비율 제한 동작은 시스템에서 감지한 특정 이상치에 따라 달라집니다.
TRIGGER_TYPE_ANOMALY(API 수준 37)TRIGGER_TYPE_APP_COMPAT(API 수준 37)
트리거 설정
다음 코드는 TRIGGER_TYPE_APP_FULLY_DRAWN 트리거를 등록하고 대기 기간을 설정하는 방법을 보여줍니다.
Kotlin
@RequiresApi(Build.VERSION_CODES.BAKLAVA) fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) ?: return val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val callbackExecutor: Executor = Executors.newSingleThreadExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.resultFilePath ) setupProfileUploadWorker(profilingResult.resultFilePath) } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage ) } } profilingManager.registerForAllProfilingResults(callbackExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers) Choreographer.getInstance().postFrameCallback { frameTimeNanos -> // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. reportFullyDrawn() } }
자바
public void recordWithTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); triggerBuilder.setRateLimitingPeriodHours(1); triggers.add(triggerBuilder.build()); Executor callbackExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = new Consumer<ProfilingResult>() { @Override public void accept(ProfilingResult profilingResult) { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.getResultFilePath()); setupProfileUploadWorker(profilingResult.getResultFilePath()); } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.getErrorCode() + " errormsg=" + profilingResult.getErrorMessage()); } } }; profilingManager.registerForAllProfilingResults(callbackExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers); Choreographer.getInstance().postFrameCallback((f) -> { // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. reportFullyDrawn(); }); }
이 코드는 다음 단계를 수행합니다.
- 관리자 가져오기:
ProfilingManager서비스를 가져옵니다. - 트리거 정의:
TRIGGER_TYPE_APP_FULLY_DRAWN에 대해ProfilingTrigger를 빌드합니다. 이 이벤트는 활동에서 시작된 콜드 스타트 중에 앱이reportFullyDrawn()를 호출하여 시작이 완료되었고 상호작용이 가능하다고 보고할 때 발생합니다. - 트리거 대기 시간 설정: 시스템 비율 제한기 외에도
setRateLimitingPeriodHours를 사용하여 이 특정 트리거에 1시간의 대기 시간을 적용합니다. 이 대기 시간은 앱이 시간당 시작 프로필을 2개 이상 기록하지 못하도록 합니다. - 리스너 등록:
registerForAllProfilingResults를 호출하여 결과를 처리하는 콜백을 정의합니다. 이 콜백은getResultFilePath()를 통해 저장된 프로필의 경로를 수신합니다. - 트리거 추가:
addProfilingTriggers를 사용하여ProfilingManager에 트리거 목록을 등록합니다. - 이벤트 실행: 다음 프레임이 그려지면
reportFullyDrawn()를 호출하는Choreographer프레임 콜백을 게시합니다. 그러면TRIGGER_TYPE_APP_FULLY_DRAWN이벤트가 시스템에 전송되어 시스템 백그라운드 추적이 실행 중이고 비율 제한기 할당량이 사용 가능하다고 가정하고 프로필 수집을 트리거합니다. 이 트리거가 발생하려면 앱에서reportFullyDrawn()를 호출해야 합니다.
트리거 수명 주기
등록된 트리거는 앱 다시 시작 및 시스템 재부팅 시에도 유지됩니다. 앱이 시작될 때마다 또는 시스템 재부팅 후에 트리거를 다시 등록할 필요가 없습니다. addProfilingTriggers를 사용하여 트리거를 등록하면 시스템은 트리거를 삭제하거나 앱을 제거할 때까지 앱의 트리거를 자동으로 유지합니다.
트리거 등록을 해제해야 하는 경우
등록된 트리거는 앱 다시 시작 및 시스템 재부팅 후에도 유지되므로 이후 실행에서 addProfilingTriggers를 건너뛰어도 이전 세션에서 등록된 트리거가 중지되지 않습니다. 특정 이벤트 또는 모든 이벤트의 프로필 캡처를 중지하려면 removeProfilingTriggersByType 또는 clearProfilingTriggers를 명시적으로 호출합니다.
다음과 같은 상황에서 트리거를 등록 해제합니다.
- 동질 집단 타겟팅 또는 원격 플래그 롤백: 원격 구성 또는 A/B 테스팅 플래그를 사용하여 동질 집단 전반에서 프로파일링을 세분화할 수 있습니다. 기기가 샘플링된 코호트를 벗어나거나 플래그가 사용 중지될 때마다 트리거를 등록 취소합니다.
- 디버깅 캠페인 완료: 문제를 진단하거나 수정사항을 배포하기에 충분한 프로필을 수집한 후에는 트리거를 삭제합니다. 불필요한 트리거를 등록 해제하면 기기 저장용량과 다른 프로파일링 요청에 대한 앱의 비율 제한 할당량이 보존됩니다.
콜백 수명 주기
직접 프로파일링 요청과 달리 트리거 기반 프로필은 registerForAllProfilingResults에 등록된 전역 리스너에만 결과를 전송합니다. 콜백에서 ProfilingResult.getTriggerType를 호출하여 캡처를 시작한 트리거를 식별하고 ProfilingResult.getResultFilePath를 호출하여 저장된 프로필 파일을 찾습니다. 트리거 프로필 파일 이름, adb로 파일 가져오기, 백그라운드에서 결과 업로드에 대한 자세한 내용은 프로필 검색 및 업로드를 참고하세요.
트리거는 앱 다시 시작 및 시스템 재부팅 시에도 유지되지만 결과 콜백은 유지되지 않습니다. 앱 시작이 완료된 후 백그라운드 Executor에 콜백을 등록하여(콜드 스타트 속도가 느려지지 않음) 다음 두 시나리오에서 프로필을 수신합니다.
- 앱이 실행되는 동안: 앱을 중지하거나 비정상 종료하지 않는 트리거의 경우 트리거가 발생하고 프로필이 저장되면 시스템은 제공된
Executor에서 콜백을 호출합니다. 앱 종료 후: 앱 종료와 관련되거나 앱 종료 전에 발생하는 트리거(예:
TRIGGER_TYPE_ANR,TRIGGER_TYPE_OOM,TRIGGER_TYPE_KILL_RECENTS)의 경우 또는 캡처가 완료되기 전에 앱이 중지되는 경우 시스템은 백그라운드에서 프로필을 저장하고 앱 다시 시작 및 시스템 재부팅 전반에 걸쳐 결과를 큐에 추가합니다. 시스템은 전송되지 않은 대기열에 추가된 결과를 최대 7일 동안 보관하고 다음에 앱이 실행되고registerForAllProfilingResults를 호출할 때 전송합니다.
백그라운드 추적 작동 방식
유용한 아티팩트를 캡처하는 것과 프로파일링 오버헤드의 균형을 맞추기 위해 시스템은 백그라운드 트레이스를 지속적으로 실행하지 않습니다. 대신 임의의 간격으로 백그라운드 trace를 시작하여 주기적으로 샘플링합니다.
시스템 트리거 백그라운드 트레이스는 시스템 정의 최대 크기의 링 버퍼를 사용합니다. 버퍼가 가득 차면 새 trace 데이터가 가장 오래된 데이터를 덮어씁니다. 그림 1과 같이 백그라운드 트레이스가 활성 상태이고 비율 제한기 할당량이 사용 가능한 동안 등록된 트리거가 실행되면 시스템은 트리거로 이어지는 가장 최근 활동이 포함된 링 버퍼 스냅샷을 저장합니다.

트리거별 대기 시간 설정
고주파 트리거는 앱의 비율 제한기 할당량을 빠르게 소모할 수 있습니다.
단일 트리거 유형이 할당량을 소진하지 않도록 하려면 setRateLimitingPeriodHours를 사용하여 시스템 비율 제한기 위에 트리거별 최소 대기 기간을 추가합니다.
로컬에서 트리거 디버그
백그라운드 트레이스는 프로덕션에서 무작위 간격으로 실행되므로 트리거를 로컬에서 테스트하려면 연속 백그라운드 트레이스를 강제하고 패키지의 시스템 수준 비율 제한기를 우회해야 합니다(setRateLimitingPeriodHours로 설정된 트리거별 대기 시간은 계속 적용되며 rate_limiter.disabled는 앱 기반 requestProfiling 호출에만 적용됨).
백그라운드 트레이스를 강제하고 명령줄에서 트리거를 시뮬레이션하고 서비스 상태를 검사하는 adb 명령어는 로컬 프로파일링을 위한 디버그 명령어의 트리거 테스트를 위해 백그라운드 트레이스 강제 사용 설정을 참고하세요.