Skip to content

Commit

Permalink
Convert modules/core/ReactChoreographer to Kotlin (facebook#45811)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#45811

# Changelog:
[Internal] -

Converts both ReactChoreographer.java and ChoreographerCompat.java to Kotlin.

Reviewed By: mdvacca

Differential Revision: D60445731
  • Loading branch information
rshest authored and facebook-github-bot committed Aug 1, 2024
1 parent c30ee46 commit fa72667
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 213 deletions.
16 changes: 12 additions & 4 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -3265,10 +3265,12 @@ public abstract interface class com/facebook/react/modules/core/RCTNativeAppEven
}

public final class com/facebook/react/modules/core/ReactChoreographer {
public static fun getInstance ()Lcom/facebook/react/modules/core/ReactChoreographer;
public static fun initialize (Lcom/facebook/react/internal/ChoreographerProvider;)V
public fun postFrameCallback (Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;Landroid/view/Choreographer$FrameCallback;)V
public fun removeFrameCallback (Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;Landroid/view/Choreographer$FrameCallback;)V
public static final field Companion Lcom/facebook/react/modules/core/ReactChoreographer$Companion;
public synthetic fun <init> (Lcom/facebook/react/internal/ChoreographerProvider;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public static final fun getInstance ()Lcom/facebook/react/modules/core/ReactChoreographer;
public static final fun initialize (Lcom/facebook/react/internal/ChoreographerProvider;)V
public final fun postFrameCallback (Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;Landroid/view/Choreographer$FrameCallback;)V
public final fun removeFrameCallback (Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;Landroid/view/Choreographer$FrameCallback;)V
}

public final class com/facebook/react/modules/core/ReactChoreographer$CallbackType : java/lang/Enum {
Expand All @@ -3277,10 +3279,16 @@ public final class com/facebook/react/modules/core/ReactChoreographer$CallbackTy
public static final field NATIVE_ANIMATED_MODULE Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;
public static final field PERF_MARKERS Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;
public static final field TIMERS_EVENTS Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;
public static fun values ()[Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType;
}

public final class com/facebook/react/modules/core/ReactChoreographer$Companion {
public final fun getInstance ()Lcom/facebook/react/modules/core/ReactChoreographer;
public final fun initialize (Lcom/facebook/react/internal/ChoreographerProvider;)V
}

public final class com/facebook/react/modules/core/TimingModule : com/facebook/fbreact/specs/NativeTimingSpec, com/facebook/react/modules/core/JavaScriptTimerExecutor {
public static final field Companion Lcom/facebook/react/modules/core/TimingModule$Companion;
public static final field NAME Ljava/lang/String;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.modules.core

import android.view.Choreographer

public open class ChoreographerCompat {

@Deprecated("Use Choreographer.FrameCallback instead")
public abstract class FrameCallback : Choreographer.FrameCallback
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.modules.core

import android.view.Choreographer
import com.facebook.common.logging.FLog
import com.facebook.infer.annotation.Assertions
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.common.ReactConstants
import com.facebook.react.common.annotations.VisibleForTesting
import com.facebook.react.internal.ChoreographerProvider
import java.util.ArrayDeque

/**
* A simple wrapper around Choreographer that allows us to control the order certain callbacks are
* executed within a given frame. The wrapped Choreographer instance will always be the main thread
* one and the API's are safe to use from any thread.
*/
public class ReactChoreographer private constructor(choreographerProvider: ChoreographerProvider) {
public enum class CallbackType(internal val order: Int) {
/** For use by perf markers that need to happen immediately after draw */
PERF_MARKERS(0),
/** For use by [com.facebook.react.uimanager.UIManagerModule] */
DISPATCH_UI(1),
/** For use by [com.facebook.react.animated.NativeAnimatedModule] */
NATIVE_ANIMATED_MODULE(2),
/** Events that make JS do things. */
TIMERS_EVENTS(3),
/**
* Event used to trigger the idle callback. Called after all UI work has been dispatched to JS.
*/
IDLE_EVENT(4)
}

private var choreographer: ChoreographerProvider.Choreographer? = null
private val callbackQueues: Array<ArrayDeque<Choreographer.FrameCallback>> =
Array(CallbackType.entries.size) { ArrayDeque() }
private var totalCallbacks = 0
private var hasPostedCallback = false

private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
synchronized(callbackQueues) {

// Callbacks run once and are then automatically removed, the callback will
// be posted again from postFrameCallback
hasPostedCallback = false
for (i in callbackQueues.indices) {
val callbackQueue = callbackQueues[i]
val initialLength = callbackQueue.size
for (callback in 0 until initialLength) {
val frameCallback = callbackQueue.pollFirst()
if (frameCallback != null) {
frameCallback.doFrame(frameTimeNanos)
totalCallbacks--
} else {
FLog.e(ReactConstants.TAG, "Tried to execute non-existent frame callback")
}
}
}
maybeRemoveFrameCallback()
}
}

init {
UiThreadUtil.runOnUiThread { choreographer = choreographerProvider.getChoreographer() }
}

public fun postFrameCallback(type: CallbackType, callback: Choreographer.FrameCallback) {
synchronized(callbackQueues) {
callbackQueues[type.order].addLast(callback)
totalCallbacks++
Assertions.assertCondition(totalCallbacks > 0)
if (!hasPostedCallback) {
if (choreographer == null) {
// Schedule on the main thread, at which point the constructor's async work will have
// completed
UiThreadUtil.runOnUiThread {
synchronized(callbackQueues) { postFrameCallbackOnChoreographer() }
}
} else {
postFrameCallbackOnChoreographer()
}
}
}
}

public fun removeFrameCallback(type: CallbackType, frameCallback: Choreographer.FrameCallback?) {
synchronized(callbackQueues) {
if (callbackQueues[type.order].removeFirstOccurrence(frameCallback)) {
totalCallbacks--
maybeRemoveFrameCallback()
} else {
FLog.e(ReactConstants.TAG, "Tried to remove non-existent frame callback")
}
}
}

/**
* This method writes on mHasPostedCallback and it should be called from another method that has
* the lock on [callbackQueues].
*/
private fun postFrameCallbackOnChoreographer() {
choreographer?.postFrameCallback(frameCallback)
hasPostedCallback = true
}

/**
* This method reads and writes on mHasPostedCallback and it should be called from another method
* that already has the lock on [callbackQueues].
*/
private fun maybeRemoveFrameCallback() {
Assertions.assertCondition(totalCallbacks >= 0)
if (totalCallbacks == 0 && hasPostedCallback) {
choreographer?.removeFrameCallback(frameCallback)
hasPostedCallback = false
}
}

public companion object {
private var choreographer: ReactChoreographer? = null

@JvmStatic
public fun initialize(choreographerProvider: ChoreographerProvider) {
if (choreographer == null) {
choreographer = ReactChoreographer(choreographerProvider)
}
}

@JvmStatic
public fun getInstance(): ReactChoreographer =
checkNotNull(choreographer) { "ReactChoreographer needs to be initialized." }

@VisibleForTesting
internal fun overrideInstanceForTest(instance: ReactChoreographer?): ReactChoreographer? =
choreographer.also { choreographer = instance }
}
}
Loading

0 comments on commit fa72667

Please sign in to comment.