Skip to content

Commit

Permalink
perf: lookup method only once (#682)
Browse files Browse the repository at this point in the history
## 📜 Description

In
#681
we started to use reflection for detection the amount of params. But
reflection is a slow mechanism, so in this PR I'm optimizing lookup
stage by calculating a correct method only one time.

## 💡 Motivation and Context

Lookup can be up to 10-100 times slower comparing to the direct function
call. If we call this method too frequently, we can encounter a
bottleneck. To make this code faster I decided to cache the method (thus
we do a lookup only one time).

This approach should almost neglect performance impact for a lookup
stage. The call stage (method invocation) still will add an overhead,
but it should have a small impact.

## 📢 Changelog

### Android

- cache method (make it lazy calculated);
- move constants to companion object.

## 🤔 How Has This Been Tested?

Tested on CI.

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
  • Loading branch information
kirillzyusko authored Nov 13, 2024
1 parent 47b1581 commit 1e48494
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,53 @@ import android.view.MotionEvent
import android.view.ViewGroup
import com.facebook.react.uimanager.JSPointerDispatcher
import com.facebook.react.uimanager.events.EventDispatcher
import java.lang.reflect.Method

/**
* Compat layer for `JSPointerDispatcher` interface for RN < 0.72
*/
class JSPointerDispatcherCompat(
private val viewGroup: ViewGroup,
viewGroup: ViewGroup,
) : JSPointerDispatcher(viewGroup) {
fun handleMotionEventCompat(
event: MotionEvent?,
eventDispatcher: EventDispatcher?,
isCapture: Boolean,
) {
private val handleMotionEventMethod: Method? by lazy {
try {
// Try to get the method with 3 parameters (for RN >= 0.72)
val method =
JSPointerDispatcher::class.java.getMethod(
"handleMotionEvent",
MotionEvent::class.java,
EventDispatcher::class.java,
Boolean::class.javaPrimitiveType,
)
method.invoke(this, event, eventDispatcher, isCapture)
// Try to get the 3-parameter method (for RN >= 0.72)
JSPointerDispatcher::class.java.getMethod(
HANDLE_MOTION_EVENT,
MotionEvent::class.java,
EventDispatcher::class.java,
Boolean::class.javaPrimitiveType,
)
} catch (_: NoSuchMethodException) {
// Fallback to 2-parameter version (for RN < 0.72)
val method =
try {
// Fallback to 2-parameter method (for RN < 0.72)
JSPointerDispatcher::class.java.getMethod(
"handleMotionEvent",
HANDLE_MOTION_EVENT,
MotionEvent::class.java,
EventDispatcher::class.java,
)
method.invoke(this, event, eventDispatcher)
} catch (_: NoSuchMethodException) {
null
}
}
}

fun handleMotionEventCompat(
event: MotionEvent?,
eventDispatcher: EventDispatcher?,
isCapture: Boolean,
) {
handleMotionEventMethod?.let { method ->
if (method.parameterCount == RN_72_PARAMS_COUNT) {
method.invoke(this, event, eventDispatcher, isCapture)
} else {
method.invoke(this, event, eventDispatcher)
}
}
}

companion object {
private const val HANDLE_MOTION_EVENT = "handleMotionEvent"
private const val RN_72_PARAMS_COUNT = 3
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,14 @@ class OverKeyboardRootViewGroup(

override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
eventDispatcher?.let {
jsPointerDispatcher?.handleMotionEventCompat(event, eventDispatcher, true)
jsPointerDispatcher?.handleMotionEventCompat(event, it, true)
}
return super.onHoverEvent(event)
}

override fun onHoverEvent(event: MotionEvent): Boolean {
eventDispatcher?.let {
jsPointerDispatcher?.handleMotionEventCompat(event, eventDispatcher, false)
jsPointerDispatcher?.handleMotionEventCompat(event, it, false)
}
return super.onHoverEvent(event)
}
Expand Down

0 comments on commit 1e48494

Please sign in to comment.