From e805edd360b2bb07520097d715a9f004b6f395ed Mon Sep 17 00:00:00 2001 From: huewu Date: Tue, 21 Jul 2026 14:36:19 +0900 Subject: [PATCH 1/6] Add UI threading violation and context switching snippets * Create ViewThreadingViolationSnippet.kt * Add snippet for switching to Dispatchers.Main in coroutines * Add snippet for View.registerCalledFromWrongThreadListener (API 37) --- .../anr/ViewThreadingViolationSnippet.kt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt diff --git a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt new file mode 100644 index 000000000..8818711da --- /dev/null +++ b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt @@ -0,0 +1,49 @@ +package com.example.snippets.anr + +import android.view.View +import android.widget.TextView +import androidx.annotation.RequiresApi +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +// Mock/stub classes so the documentation snippets compile cleanly. +interface MyData { + val title: String +} + +interface MyRepository { + suspend fun getData(): MyData +} + +@SuppressWarnings("unused") +object ViewThreadingViolationSnippet { + + fun switchContext( + lifecycleOwner: LifecycleOwner, + myRepository: MyRepository, + myTextView: TextView + ) { + // [START android_view_threading_violation_coroutine_kotlin] + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + val data = myRepository.getData() + withContext(Dispatchers.Main) { // Switch context to Main + myTextView.text = data.title + } + } + // [END android_view_threading_violation_coroutine_kotlin] + } + + @RequiresApi(37) + fun checkViewThreadingViolation() { + // [START android_view_threading_violation_listener_kotlin] + val wrongThreadListener = View.CalledFromWrongThreadListener { + // Handle the issue, e.g. crash if this is a dev build, or log an event + // Unregister the listener to avoid redundant notifications for the same issue + } + View.registerCalledFromWrongThreadListener(wrongThreadListener) + // [END android_view_threading_violation_listener_kotlin] + } +} \ No newline at end of file From b14acd13214182ca3bbfbd7374fff35838bb1b75 Mon Sep 17 00:00:00 2001 From: huewu Date: Tue, 21 Jul 2026 15:12:49 +0900 Subject: [PATCH 2/6] Add UI threading violation and context switching snippets * Create ViewThreadingViolationSnippet.kt * Add snippet for switching to Dispatchers.Main in coroutines * Add snippet for View.registerCalledFromWrongThreadListener (API 37) # Conflicts: # misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt --- .../snippets/anr/ViewThreadingViolationSnippet.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt index 8818711da..86f52ca6e 100644 --- a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt +++ b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt @@ -39,11 +39,15 @@ object ViewThreadingViolationSnippet { @RequiresApi(37) fun checkViewThreadingViolation() { // [START android_view_threading_violation_listener_kotlin] - val wrongThreadListener = View.CalledFromWrongThreadListener { - // Handle the issue, e.g. crash if this is a dev build, or log an event - // Unregister the listener to avoid redundant notifications for the same issue + val listener = object : View.CalledFromWrongThreadListener { + override fun onCalledFromWrongThread() { + // Handle the issue, e.g. crash if this is a dev build, or log an event + // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") + // Unregister the listener to avoid redundant notifications for the same issue + View.unregisterCalledFromWrongThreadListener(this) + } } - View.registerCalledFromWrongThreadListener(wrongThreadListener) + View.registerCalledFromWrongThreadListener(listener) // [END android_view_threading_violation_listener_kotlin] } } \ No newline at end of file From 5183cb8989f3cdbb8579af9e0a333addf31bc569 Mon Sep 17 00:00:00 2001 From: huewu <1445837+huewu@users.noreply.github.com> Date: Tue, 21 Jul 2026 06:24:51 +0000 Subject: [PATCH 3/6] Apply Spotless --- .../anr/ViewThreadingViolationSnippet.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt index 86f52ca6e..e16127945 100644 --- a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt +++ b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.snippets.anr import android.view.View @@ -50,4 +66,4 @@ object ViewThreadingViolationSnippet { View.registerCalledFromWrongThreadListener(listener) // [END android_view_threading_violation_listener_kotlin] } -} \ No newline at end of file +} From 0583469d2bbd06cd6aaebaa19c397f464ba86e37 Mon Sep 17 00:00:00 2001 From: Chansuk Yang Date: Tue, 21 Jul 2026 15:25:30 +0900 Subject: [PATCH 4/6] Update misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../com/example/snippets/anr/ViewThreadingViolationSnippet.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt index e16127945..6296b5a4c 100644 --- a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt +++ b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt @@ -34,7 +34,7 @@ interface MyRepository { suspend fun getData(): MyData } -@SuppressWarnings("unused") +@Suppress("unused") object ViewThreadingViolationSnippet { fun switchContext( From dc17a03f79a42df6a6fc7176cbbb16b69ba40a35 Mon Sep 17 00:00:00 2001 From: huewu Date: Thu, 23 Jul 2026 14:40:32 +0900 Subject: [PATCH 5/6] Add UI threading violation and context switching snippets --- .../anr/ViewThreadingViolationSnippet.kt | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 views/src/main/java/com/example/example/snippet/views/anr/ViewThreadingViolationSnippet.kt diff --git a/views/src/main/java/com/example/example/snippet/views/anr/ViewThreadingViolationSnippet.kt b/views/src/main/java/com/example/example/snippet/views/anr/ViewThreadingViolationSnippet.kt new file mode 100644 index 000000000..bcedb5e97 --- /dev/null +++ b/views/src/main/java/com/example/example/snippet/views/anr/ViewThreadingViolationSnippet.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.example.snippet.views.anr + +import android.view.View +import android.widget.TextView +import androidx.annotation.RequiresApi +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +// Mock/stub classes so the documentation snippets compile cleanly. +interface MyData { + val title: String +} + +interface MyRepository { + suspend fun getData(): MyData +} + +@Suppress("unused") +object ViewThreadingViolationSnippet { + + fun switchContext( + lifecycleOwner: LifecycleOwner, + myRepository: MyRepository, + myTextView: TextView + ) { + // [START android_view_threading_violation_coroutine_kotlin] + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + val data = myRepository.getData() + withContext(Dispatchers.Main) { // Switch context to Main + myTextView.text = data.title + } + } + // [END android_view_threading_violation_coroutine_kotlin] + } + + @RequiresApi(37) + fun checkViewThreadingViolation() { + // [START android_view_threading_violation_listener_kotlin] + val listener = object : View.CalledFromWrongThreadListener { + override fun onCalledFromWrongThread() { + // Handle the issue, e.g. crash if this is a dev build, or log an event + // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") + // Unregister the listener to avoid redundant notifications for the same issue + View.unregisterCalledFromWrongThreadListener(this) + } + } + View.registerCalledFromWrongThreadListener(listener) + // [END android_view_threading_violation_listener_kotlin] + } +} From 364545c3b8025415529297bfa29bbf34aada94ef Mon Sep 17 00:00:00 2001 From: huewu Date: Thu, 23 Jul 2026 14:42:53 +0900 Subject: [PATCH 6/6] Move ViewThreadingViolationSnippet to views module --- .../anr/ViewThreadingViolationSnippet.kt | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt diff --git a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt b/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt deleted file mode 100644 index 6296b5a4c..000000000 --- a/misc/src/main/java/com/example/snippets/anr/ViewThreadingViolationSnippet.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2026 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.snippets.anr - -import android.view.View -import android.widget.TextView -import androidx.annotation.RequiresApi -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -// Mock/stub classes so the documentation snippets compile cleanly. -interface MyData { - val title: String -} - -interface MyRepository { - suspend fun getData(): MyData -} - -@Suppress("unused") -object ViewThreadingViolationSnippet { - - fun switchContext( - lifecycleOwner: LifecycleOwner, - myRepository: MyRepository, - myTextView: TextView - ) { - // [START android_view_threading_violation_coroutine_kotlin] - lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { - val data = myRepository.getData() - withContext(Dispatchers.Main) { // Switch context to Main - myTextView.text = data.title - } - } - // [END android_view_threading_violation_coroutine_kotlin] - } - - @RequiresApi(37) - fun checkViewThreadingViolation() { - // [START android_view_threading_violation_listener_kotlin] - val listener = object : View.CalledFromWrongThreadListener { - override fun onCalledFromWrongThread() { - // Handle the issue, e.g. crash if this is a dev build, or log an event - // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") - // Unregister the listener to avoid redundant notifications for the same issue - View.unregisterCalledFromWrongThreadListener(this) - } - } - View.registerCalledFromWrongThreadListener(listener) - // [END android_view_threading_violation_listener_kotlin] - } -}