perf(android): Avoid exception-driven control flow in getResourceId by runningcode · Pull Request #5631 · getsentry/sentry-java · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,10 @@ private static ViewHierarchyNode viewToNode(@NotNull final View view) {
node.setType(className);

try {
final String identifier = ViewUtils.getResourceId(view);
node.setIdentifier(identifier);
final @Nullable String identifier = ViewUtils.getResourceIdOrNull(view);
if (identifier != null) {
node.setIdentifier(identifier);
}
} catch (Throwable e) {
// ignored
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.sentry.android.core.internal.gestures;

import android.content.res.Resources;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ScrollView;
Expand Down Expand Up @@ -42,13 +41,12 @@ && isViewScrollable(view, isAndroidXAvailable.getValue())) {
}

private UiElement createUiElement(final @NotNull View targetView) {
try {
final String resourceName = ViewUtils.getResourceId(targetView);
@Nullable String className = ClassUtil.getClassName(targetView);
return new UiElement(targetView, className, resourceName, null, ORIGIN);
} catch (Resources.NotFoundException ignored) {
final @Nullable String resourceName = ViewUtils.getResourceIdOrNull(targetView);
if (resourceName == null) {
return null;
}
@Nullable String className = ClassUtil.getClassName(targetView);
return new UiElement(targetView, className, resourceName, null, ORIGIN);
}

private static boolean isViewTappable(final @NotNull View view) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,32 +150,37 @@ private static final class ViewWithLocation {
* @return human-readable view id
*/
static String getResourceIdWithFallback(final @NotNull View view) {
final int viewId = view.getId();
try {
return getResourceId(view);
} catch (Resources.NotFoundException e) {
final @Nullable String resourceId = getResourceIdOrNull(view);
if (resourceId == null) {
// fall back to hex representation of the id
return "0x" + Integer.toString(viewId, 16);
return "0x" + Integer.toString(view.getId(), 16);
}
return resourceId;
}

/**
* Retrieves the human-readable view id based on {@code view.getContext().getResources()}.
* Retrieves the human-readable view id based on {@code view.getContext().getResources()}, or
* {@code null} when the view has no resource-backed id. Returning {@code null} rather than
* throwing avoids exception-driven control flow on hot, main-thread paths such as view-hierarchy
* snapshots and gesture target resolution.
*
* @param view - the view whose id is being retrieved
* @return human-readable view id
* @throws Resources.NotFoundException in case the view id was not found
* @return human-readable view id, or {@code null} if it cannot be resolved
*/
public static String getResourceId(final @NotNull View view) throws Resources.NotFoundException {
Comment thread
runningcode marked this conversation as resolved.
public static @Nullable String getResourceIdOrNull(final @NotNull View view) {
final int viewId = view.getId();
if (viewId == View.NO_ID || isViewIdGenerated(viewId)) {
throw new Resources.NotFoundException();
return null;
}
final Resources resources = view.getContext().getResources();
if (resources != null) {
if (resources == null) {
return "";
}
try {
return resources.getResourceEntryName(viewId);
} catch (Resources.NotFoundException e) {
return null;
}
return "";
}

private static boolean isViewIdGenerated(int id) {
Expand Down
Loading