Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ material3-adaptive-navigation-suite = "1.4.0"
media3 = "1.8.0"
media3Ui = "1.8.0"
# @keep
minSdk = "35"
minSdk = "36"
okHttp = "5.2.1"
playServicesWearable = "19.0.0"
protobuf = "4.32.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@

import android.app.Activity;
import android.os.Bundle;
import android.os.ProfilingManager;
import android.os.ProfilingTrigger;
import android.util.Log;
import java.util.List;
import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import android.os.ProfilingResult;
import java.util.concurrent.Executors;
import android.os.CancellationSignal;
import android.view.Choreographer;
import androidx.tracing.Trace;
import androidx.core.os.Profiling;
import androidx.core.os.SystemTraceRequestBuilder;
import androidx.core.os.BufferFillPolicy;
import com.example.snippets.R;

public class ProfilingManagerJavaSnippets {
public class MainActivityJava extends Activity {
// [START android_profiling_manager_anr_case_study_java_snippet_2]
private static final int NETWORK_TIMEOUT_MILLISECS = 2000;
// [END android_profiling_manager_anr_case_study_java_snippet_2]
public static final String TAG = "ProfilingManager";

@Override
public void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -69,5 +80,117 @@ public void accept(ProfilingResult profilingResult) {
stopSignal.cancel();
}
// [END android_profiling_manager_record_system_trace_java]

// [START android_profiling_manager_triggered_trace_java]
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 mainExecutor = Executors.newSingleThreadExecutor();
Consumer<ProfilingResult> resultCallback =
new Consumer<ProfilingResult>() {
@Override
public void accept(ProfilingResult profilingResult) {
// ...
if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) {
Log.d(TAG,
"Received profiling result. file: " + profilingResult.getResultFilePath());
}
}
};
profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback);
profilingManager.addProfilingTriggers(triggers);

Choreographer.getInstance().postFrameCallback((f) -> {
// This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted.
reportFullyDrawn();
});
}
// [END android_profiling_manager_triggered_trace_java]

// [START android_profiling_manager_anr_case_study_java_snippet_1]
public void addANRTrigger() {
ProfilingManager profilingManager = getApplicationContext().getSystemService(ProfilingManager.class);
List<ProfilingTrigger> triggers = new ArrayList<>();
ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANR);
triggers.add(triggerBuilder.build());
Executor mainExecutor = Executors.newSingleThreadExecutor();
Consumer<ProfilingResult> resultCallback =
profilingResult -> {
// Handle uploading trace to your back-end
};
profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback);
profilingManager.addProfilingTriggers(triggers);
}
// [END android_profiling_manager_anr_case_study_java_snippet_1]

// [START android_profiling_manager_anr_case_study_java_snippet_2]
public void setupButtonCallback() {
findViewById(R.id.submit).setOnClickListener(submitButtonView -> {
Trace.beginSection("MyApp:SubmitButton");
onClickSubmit();
Trace.endSection();
});
}

public void onClickSubmit() {
prepareNetworkRequest();

boolean networkRequestSuccess = false;
int maxAttempts = 10;
while (!networkRequestSuccess && maxAttempts > 0) {
networkRequestSuccess = performNetworkRequest(NETWORK_TIMEOUT_MILLISECS);
maxAttempts--;
}

if (networkRequestSuccess) {
handleNetworkResponse();
}
}

boolean performNetworkRequest(int timeoutMiliseconds) {
// [START_EXCLUDE]
cpuIntensiveComputation(20);
try {
if (Math.random() < 0.2) {
// Simulate performing a network request by waiting a random period of time
int networkRequestTimeMs = (int)(Math.random() * timeoutMiliseconds);
Thread.sleep(networkRequestTimeMs);
return true;
} else {
// Simulate a timeout
Thread.sleep(timeoutMiliseconds);
}
} catch (InterruptedException e) {}
return false;
// [END_EXCLUDE]
}

// [START_EXCLUDE silent]
void cpuIntensiveComputation(int durationMs) {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < durationMs) {}
}
// [END_EXCLUDE silent]

void prepareNetworkRequest() {
// [START_EXCLUDE]
cpuIntensiveComputation(1000);
// [END_EXCLUDE]
}

public void handleNetworkResponse() {
Trace.beginSection("handleNetworkResponse");
// [START_EXCLUDE]
cpuIntensiveComputation(2000);
// [END_EXCLUDE]
Trace.endSection();
}
// [END android_profiling_manager_anr_case_study_java_snippet_2]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,29 @@ import android.app.Activity
import android.os.Build
import android.os.Bundle
import android.os.CancellationSignal
import android.os.ProfilingManager
import android.os.ProfilingResult
import android.os.ProfilingTrigger
import android.util.Log
import android.view.Choreographer
import androidx.annotation.RequiresApi
import androidx.core.os.BufferFillPolicy
import androidx.core.os.SystemTraceRequestBuilder
import androidx.core.os.requestProfiling
import androidx.tracing.Trace
import java.util.ArrayList
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.function.Consumer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor

class ProfilingManagerKotlinSnippets {
class MainActivity : Activity() {
companion object {
const val TAG = "MyApp"
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sampleRecordSystemTrace()
Expand Down Expand Up @@ -81,5 +90,34 @@ class ProfilingManagerKotlinSnippets {
// Computations you want to profile
}
// [END android_profiling_manager_record_system_trace_kotlin]

// [START android_profiling_manager_triggered_trace]
fun recordWithTrigger() {
val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java)

val triggers = ArrayList<ProfilingTrigger>()

val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)
.setRateLimitingPeriodHours(1)

triggers.add(triggerBuilder.build())

val mainExecutor: Executor = Executors.newSingleThreadExecutor()

val resultCallback = Consumer<ProfilingResult> { profilingResult ->
if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
Log.d(TAG, "Received profiling result. file: ${profilingResult.resultFilePath}")
}
}

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

Choreographer.getInstance().postFrameCallback { frameTimeNanos ->
// This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted.
reportFullyDrawn()
}
}
// [END android_profiling_manager_triggered_trace]
}
}
7 changes: 7 additions & 0 deletions misc/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="150dp"
android:text="Button" />

</androidx.constraintlayout.widget.ConstraintLayout>
Loading