Skip to content

Commit 5968a00

Browse files
tritterPureWeen
authored andcommitted
Fix Android crash when changing shared Drawable tint on Searchbar (dotnet#33071)
App crashes because of changing a shared Drawable on the fly. I found out it is the SearchBar. It only happens on Android and in large apps with lots of screens. Unfortunately I can't reproduce it in the TestSuite. But by reading the docs of Android this crash is common and caused by changing a Drawable which is still referenced. With a custom SearchBarHandler the issues is resolved. ```Backtrace (top frames): #00 pc 00000000006b8694 /system/lib64/libhwui.so android::getRootAlpha(_JNIEnv*, _jobject*, long) +4 #1 pc 0000000002256c90 /memfd:jit-cache (deleted) art_jni_trampoline +112 #2 pc 000000000223bc4c /memfd:jit-cache (deleted) android.graphics.drawable.VectorDrawable.-$$Nest$smnGetRootAlpha +108 #3 pc 000000000223bb20 /memfd:jit-cache (deleted) android.graphics.drawable.VectorDrawable$VectorDrawableState.getAlpha +144 #4 pc 00000000025c50e0 /memfd:jit-cache (deleted) android.graphics.drawable.VectorDrawable.getAlpha +128 #5 pc 00000000025c4f9c /memfd:jit-cache (deleted) android.graphics.drawable.VectorDrawable.getOpacity +124 #6 pc 00000000025c1ea8 /memfd:jit-cache (deleted) android.widget.ImageView.isOpaque +152 #7 pc 000000000227979c /memfd:jit-cache (deleted) android.view.View.invalidateInternal +428 #8 pc 00000000025c4790 /memfd:jit-cache (deleted) android.widget.ImageView.invalidateDrawable +256 #9 pc 000000000224419c /memfd:jit-cache (deleted) android.graphics.drawable.Drawable.invalidateSelf +156 #10 pc 000000000260e710 /memfd:jit-cache (deleted) android.graphics.drawable.VectorDrawable.setTintList +192 #11 pc 00000000025d0094 /memfd:jit-cache (deleted) **android.graphics.drawable.Drawable.setTint +148** ``` ### Description of Change - Changes tinting of Androids SearchBar to unified setTint instead of setColorFilter - Mutates the drawable before setting the tint. ### Issues Fixed Issue is fixed with a custom handler for now. Fixes dotnet#33070
1 parent a7e791d commit 5968a00

1 file changed

Lines changed: 28 additions & 13 deletions

File tree

src/Core/src/Platform/Android/SearchViewExtensions.cs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static void UpdatePlaceholderColor(this SearchView searchView, ISearchBar
4444
editText.SetHintTextColor(color);
4545

4646
var searchMagIconImage = searchView.FindViewById<ImageView>(Resource.Id.search_mag_icon);
47-
searchMagIconImage?.Drawable?.SetTint(color);
47+
SafeSetTint(searchMagIconImage, color);
4848
}
4949
}
5050

@@ -57,7 +57,7 @@ internal static void UpdateTextColor(this SearchView searchView, ITextStyle entr
5757
editText.SetTextColor(color);
5858

5959
var searchMagIconImage = searchView.FindViewById<ImageView>(Resource.Id.search_mag_icon);
60-
searchMagIconImage?.Drawable?.SetTint(color);
60+
SafeSetTint(searchMagIconImage, color);
6161
}
6262
}
6363

@@ -129,14 +129,10 @@ public static void UpdateCancelButtonColor(this SearchView searchView, ISearchBa
129129
if (searchCloseButtonIdentifier > 0)
130130
{
131131
var image = searchView.FindViewById<ImageView>(searchCloseButtonIdentifier);
132-
133-
if (image is not null && image.Drawable is Drawable drawable)
134-
{
135-
if (searchBar.CancelButtonColor is not null)
136-
drawable.SetColorFilter(searchBar.CancelButtonColor, FilterMode.SrcIn);
137-
else if (TryGetDefaultStateColor(searchView, AAttribute.TextColorPrimary, out var color))
138-
drawable.SetColorFilter(color, FilterMode.SrcIn);
139-
}
132+
if (searchBar.CancelButtonColor is not null)
133+
SafeSetTint(image, searchBar.CancelButtonColor.ToPlatform());
134+
else if (TryGetDefaultStateColor(searchView, AAttribute.TextColorPrimary, out var color))
135+
SafeSetTint(image, color);
140136
}
141137
}
142138

@@ -154,9 +150,9 @@ internal static void UpdateSearchIconColor(this SearchView searchView, ISearchBa
154150
if (image?.Drawable is not null)
155151
{
156152
if (searchBar.SearchIconColor is not null)
157-
image.Drawable.SetColorFilter(searchBar.SearchIconColor, FilterMode.SrcIn);
153+
SafeSetTint(image, searchBar.SearchIconColor.ToPlatform());
158154
else
159-
image.Drawable.ClearColorFilter();
155+
SafeSetTint(image, Color.Transparent);
160156
}
161157
}
162158
}
@@ -240,5 +236,24 @@ static bool TryGetDefaultStateColor(SearchView searchView, int attribute, out Co
240236
color = new Color(cs.GetColorForState(state, Color.Black));
241237
return true;
242238
}
239+
240+
/// <summary>
241+
/// Safely applies tint to an ImageView's drawable by mutating it first.
242+
/// This prevents crashes when the drawable is shared across multiple views.
243+
/// </summary>
244+
/// <remarks>
245+
/// Android shares Drawable resources for memory efficiency. Modifying a shared
246+
/// drawable without calling Mutate() first causes race conditions and crashes.
247+
/// See: https://developer.android.com/reference/android/graphics/drawable/Drawable#mutate()
248+
/// </remarks>
249+
internal static void SafeSetTint(ImageView? imageView, Color color)
250+
{
251+
if (imageView?.Drawable is not Drawable drawable)
252+
return;
253+
254+
var safe = drawable.Mutate();
255+
safe.SetTint(color);
256+
imageView?.SetImageDrawable(safe);
257+
}
243258
}
244-
}
259+
}

0 commit comments

Comments
 (0)