fix: Improve Android resource efficiency/cleanup (use class members for CoroutineScope
and FrameProcessorThread
) (#335)
* fix: Use custom CoroutineScope * fix: Use custom `CoroutineScope` * Make `frameProcessorThread` and `coroutineScope` instance variables * Update VisionCameraScheduler.java * Remove `HybridData::resetNative()` calls they're called by a Java GC destructor anyways. * Update CameraViewManager.kt * Update CameraView.kt
This commit is contained in:
parent
c7fb89170e
commit
ff5a8b8900
@ -11,6 +11,6 @@ class CameraPackage : ReactPackage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
||||||
return listOf(CameraViewManager())
|
return listOf(CameraViewManager(reactContext))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import com.mrousavy.camera.utils.*
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.guava.await
|
import kotlinx.coroutines.guava.await
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -60,8 +61,16 @@ import kotlin.math.min
|
|||||||
// TODO: takePhoto() return with jsi::Value Image reference for faster capture
|
// TODO: takePhoto() return with jsi::Value Image reference for faster capture
|
||||||
|
|
||||||
@Suppress("KotlinJniMissingFunction") // I use fbjni, Android Studio is not smart enough to realize that.
|
@Suppress("KotlinJniMissingFunction") // I use fbjni, Android Studio is not smart enough to realize that.
|
||||||
@SuppressLint("ClickableViewAccessibility") // suppresses the warning that the pinch to zoom gesture is not accessible
|
@SuppressLint("ClickableViewAccessibility", "ViewConstructor")
|
||||||
class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
class CameraView(context: Context, private val frameProcessorThread: ExecutorService) : FrameLayout(context), LifecycleOwner {
|
||||||
|
companion object {
|
||||||
|
const val TAG = "CameraView"
|
||||||
|
const val TAG_PERF = "CameraView.performance"
|
||||||
|
|
||||||
|
private val propsThatRequireSessionReconfiguration = arrayListOf("cameraId", "format", "fps", "hdr", "lowLightBoost", "photo", "video", "enableFrameProcessor")
|
||||||
|
private val arrayListOfZoom = arrayListOf("zoom")
|
||||||
|
}
|
||||||
|
|
||||||
// react properties
|
// react properties
|
||||||
// props that require reconfiguring
|
// props that require reconfiguring
|
||||||
var cameraId: String? = null // this is actually not a react prop directly, but the result of setting device={}
|
var cameraId: String? = null // this is actually not a react prop directly, but the result of setting device={}
|
||||||
@ -95,6 +104,7 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
private val cameraExecutor = Executors.newSingleThreadExecutor()
|
private val cameraExecutor = Executors.newSingleThreadExecutor()
|
||||||
internal val takePhotoExecutor = Executors.newSingleThreadExecutor()
|
internal val takePhotoExecutor = Executors.newSingleThreadExecutor()
|
||||||
internal val recordVideoExecutor = Executors.newSingleThreadExecutor()
|
internal val recordVideoExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
private var coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||||
|
|
||||||
internal var camera: Camera? = null
|
internal var camera: Camera? = null
|
||||||
internal var imageCapture: ImageCapture? = null
|
internal var imageCapture: ImageCapture? = null
|
||||||
@ -172,7 +182,6 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
scaleGestureDetector = ScaleGestureDetector(context, scaleGestureListener)
|
scaleGestureDetector = ScaleGestureDetector(context, scaleGestureListener)
|
||||||
touchEventListener = OnTouchListener { _, event -> return@OnTouchListener scaleGestureDetector.onTouchEvent(event) }
|
touchEventListener = OnTouchListener { _, event -> return@OnTouchListener scaleGestureDetector.onTouchEvent(event) }
|
||||||
|
|
||||||
|
|
||||||
hostLifecycleState = Lifecycle.State.INITIALIZED
|
hostLifecycleState = Lifecycle.State.INITIALIZED
|
||||||
lifecycleRegistry = LifecycleRegistry(this)
|
lifecycleRegistry = LifecycleRegistry(this)
|
||||||
reactContext.addLifecycleEventListener(object : LifecycleEventListener {
|
reactContext.addLifecycleEventListener(object : LifecycleEventListener {
|
||||||
@ -206,10 +215,6 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun finalize() {
|
|
||||||
mHybridData.resetNative()
|
|
||||||
}
|
|
||||||
|
|
||||||
private external fun initHybrid(): HybridData
|
private external fun initHybrid(): HybridData
|
||||||
private external fun frameProcessorCallback(frame: ImageProxy)
|
private external fun frameProcessorCallback(frame: ImageProxy)
|
||||||
|
|
||||||
@ -252,8 +257,8 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
fun update(changedProps: ArrayList<String>) = previewView.post {
|
fun update(changedProps: ArrayList<String>) = previewView.post {
|
||||||
// TODO: Does this introduce too much overhead?
|
// TODO: Does this introduce too much overhead?
|
||||||
// I need to .post on the previewView because it might've not been initialized yet
|
// I need to .post on the previewView because it might've not been initialized yet
|
||||||
// I need to use GlobalScope.launch because of the suspend fun [configureSession]
|
// I need to use CoroutineScope.launch because of the suspend fun [configureSession]
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
try {
|
try {
|
||||||
val shouldReconfigureSession = changedProps.containsAny(propsThatRequireSessionReconfiguration)
|
val shouldReconfigureSession = changedProps.containsAny(propsThatRequireSessionReconfiguration)
|
||||||
val shouldReconfigureZoom = shouldReconfigureSession || changedProps.contains("zoom")
|
val shouldReconfigureZoom = shouldReconfigureSession || changedProps.contains("zoom")
|
||||||
@ -334,7 +339,7 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
val imageAnalysisBuilder = ImageAnalysis.Builder()
|
val imageAnalysisBuilder = ImageAnalysis.Builder()
|
||||||
.setTargetRotation(rotation)
|
.setTargetRotation(rotation)
|
||||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||||
.setBackgroundExecutor(CameraViewModule.FrameProcessorThread)
|
.setBackgroundExecutor(frameProcessorThread)
|
||||||
|
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
// let CameraX automatically find best resolution for the target aspect ratio
|
// let CameraX automatically find best resolution for the target aspect ratio
|
||||||
@ -473,12 +478,4 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "CameraView"
|
|
||||||
const val TAG_PERF = "CameraView.performance"
|
|
||||||
|
|
||||||
private val propsThatRequireSessionReconfiguration = arrayListOf("cameraId", "format", "fps", "hdr", "lowLightBoost", "photo", "video", "enableFrameProcessor")
|
|
||||||
private val arrayListOfZoom = arrayListOf("zoom")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,67 @@
|
|||||||
package com.mrousavy.camera
|
package com.mrousavy.camera
|
||||||
|
|
||||||
import android.util.Log
|
import com.facebook.react.bridge.ReactApplicationContext
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
import com.facebook.react.common.MapBuilder
|
import com.facebook.react.common.MapBuilder
|
||||||
import com.facebook.react.uimanager.SimpleViewManager
|
import com.facebook.react.uimanager.SimpleViewManager
|
||||||
import com.facebook.react.uimanager.ThemedReactContext
|
import com.facebook.react.uimanager.ThemedReactContext
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp
|
import com.facebook.react.uimanager.annotations.ReactProp
|
||||||
|
import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class CameraViewManager : SimpleViewManager<CameraView>() {
|
@Suppress("unused")
|
||||||
private fun addChangedPropToTransaction(view: CameraView, changedProp: String) {
|
class CameraViewManager(reactContext: ReactApplicationContext) : SimpleViewManager<CameraView>() {
|
||||||
if (cameraViewTransactions[view] == null) {
|
private val frameProcessorThread = Executors.newSingleThreadExecutor()
|
||||||
cameraViewTransactions[view] = ArrayList()
|
private var frameProcessorManager: FrameProcessorRuntimeManager? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (frameProcessorManager == null) {
|
||||||
|
frameProcessorThread.execute {
|
||||||
|
frameProcessorManager = FrameProcessorRuntimeManager(reactContext, frameProcessorThread)
|
||||||
|
|
||||||
|
reactContext.runOnJSQueueThread {
|
||||||
|
frameProcessorManager!!.installJSIBindings()
|
||||||
}
|
}
|
||||||
cameraViewTransactions[view]!!.add(changedProp)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun destroy() {
|
||||||
|
frameProcessorManager = null
|
||||||
|
frameProcessorThread.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCatalystInstanceDestroy() {
|
||||||
|
super.onCatalystInstanceDestroy()
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
super.invalidate()
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun createViewInstance(context: ThemedReactContext): CameraView {
|
||||||
|
return CameraView(context, frameProcessorThread)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAfterUpdateTransaction(view: CameraView) {
|
||||||
|
super.onAfterUpdateTransaction(view)
|
||||||
|
val changedProps = cameraViewTransactions[view] ?: ArrayList()
|
||||||
|
view.update(changedProps)
|
||||||
|
cameraViewTransactions.remove(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>? {
|
||||||
|
return MapBuilder.builder<String, Any>()
|
||||||
|
.put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized"))
|
||||||
|
.put("cameraError", MapBuilder.of("registrationName", "onError"))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return TAG
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactProp(name = "cameraId")
|
@ReactProp(name = "cameraId")
|
||||||
@ -78,6 +127,7 @@ class CameraViewManager : SimpleViewManager<CameraView>() {
|
|||||||
view.format = format
|
view.format = format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Change when TurboModules release.
|
||||||
// We're treating -1 as "null" here, because when I make the fps parameter
|
// We're treating -1 as "null" here, because when I make the fps parameter
|
||||||
// of type "Int?" the react bridge throws an error.
|
// of type "Int?" the react bridge throws an error.
|
||||||
@ReactProp(name = "fps", defaultInt = -1)
|
@ReactProp(name = "fps", defaultInt = -1)
|
||||||
@ -144,36 +194,16 @@ class CameraViewManager : SimpleViewManager<CameraView>() {
|
|||||||
view.enableZoomGesture = enableZoomGesture
|
view.enableZoomGesture = enableZoomGesture
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAfterUpdateTransaction(view: CameraView) {
|
|
||||||
super.onAfterUpdateTransaction(view)
|
|
||||||
val changedProps = cameraViewTransactions[view] ?: ArrayList()
|
|
||||||
view.update(changedProps)
|
|
||||||
cameraViewTransactions.remove(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun createViewInstance(context: ThemedReactContext): CameraView {
|
|
||||||
return CameraView(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>? {
|
|
||||||
return MapBuilder.builder<String, Any>()
|
|
||||||
.put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized"))
|
|
||||||
.put("cameraError", MapBuilder.of("registrationName", "onError"))
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDropViewInstance(view: CameraView) {
|
|
||||||
Log.d(TAG, "onDropViewInstance() called!")
|
|
||||||
super.onDropViewInstance(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName(): String {
|
|
||||||
return TAG
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "CameraView"
|
const val TAG = "CameraView"
|
||||||
|
|
||||||
val cameraViewTransactions: HashMap<CameraView, ArrayList<String>> = HashMap()
|
val cameraViewTransactions: HashMap<CameraView, ArrayList<String>> = HashMap()
|
||||||
|
|
||||||
|
private fun addChangedPropToTransaction(view: CameraView, changedProp: String) {
|
||||||
|
if (cameraViewTransactions[view] == null) {
|
||||||
|
cameraViewTransactions[view] = ArrayList()
|
||||||
|
}
|
||||||
|
cameraViewTransactions[view]!!.add(changedProp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,19 +15,16 @@ import androidx.core.content.ContextCompat
|
|||||||
import com.facebook.react.bridge.*
|
import com.facebook.react.bridge.*
|
||||||
import com.facebook.react.modules.core.PermissionAwareActivity
|
import com.facebook.react.modules.core.PermissionAwareActivity
|
||||||
import com.facebook.react.modules.core.PermissionListener
|
import com.facebook.react.modules.core.PermissionListener
|
||||||
import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager
|
|
||||||
import com.mrousavy.camera.parsers.*
|
import com.mrousavy.camera.parsers.*
|
||||||
import com.mrousavy.camera.utils.*
|
import com.mrousavy.camera.utils.*
|
||||||
import java.util.concurrent.ExecutorService
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.guava.await
|
import kotlinx.coroutines.guava.await
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "CameraView"
|
const val TAG = "CameraView"
|
||||||
var RequestCode = 10
|
var RequestCode = 10
|
||||||
val FrameProcessorThread: ExecutorService = Executors.newSingleThreadExecutor()
|
|
||||||
|
|
||||||
fun parsePermissionStatus(status: Int): String {
|
fun parsePermissionStatus(status: Int): String {
|
||||||
return when (status) {
|
return when (status) {
|
||||||
@ -38,24 +35,22 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var frameProcessorManager: FrameProcessorRuntimeManager? = null
|
private val coroutineScope = CoroutineScope(Dispatchers.Default) // TODO: or Dispatchers.Main?
|
||||||
|
|
||||||
override fun initialize() {
|
private fun cleanup() {
|
||||||
super.initialize()
|
if (coroutineScope.isActive) {
|
||||||
if (frameProcessorManager == null) {
|
coroutineScope.cancel("CameraViewModule has been destroyed.")
|
||||||
FrameProcessorThread.execute {
|
|
||||||
frameProcessorManager = FrameProcessorRuntimeManager(reactApplicationContext)
|
|
||||||
reactApplicationContext.runOnJSQueueThread {
|
|
||||||
frameProcessorManager!!.installJSIBindings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCatalystInstanceDestroy() {
|
override fun onCatalystInstanceDestroy() {
|
||||||
super.onCatalystInstanceDestroy()
|
super.onCatalystInstanceDestroy()
|
||||||
frameProcessorManager?.destroy()
|
cleanup()
|
||||||
frameProcessorManager = null
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
super.invalidate()
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName(): String {
|
override fun getName(): String {
|
||||||
@ -66,7 +61,7 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun takePhoto(viewTag: Int, options: ReadableMap, promise: Promise) {
|
fun takePhoto(viewTag: Int, options: ReadableMap, promise: Promise) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
withPromise(promise) {
|
withPromise(promise) {
|
||||||
val view = findCameraView(viewTag)
|
val view = findCameraView(viewTag)
|
||||||
view.takePhoto(options)
|
view.takePhoto(options)
|
||||||
@ -74,9 +69,10 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun takeSnapshot(viewTag: Int, options: ReadableMap, promise: Promise) {
|
fun takeSnapshot(viewTag: Int, options: ReadableMap, promise: Promise) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
withPromise(promise) {
|
withPromise(promise) {
|
||||||
val view = findCameraView(viewTag)
|
val view = findCameraView(viewTag)
|
||||||
view.takeSnapshot(options)
|
view.takeSnapshot(options)
|
||||||
@ -87,7 +83,7 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
// TODO: startRecording() cannot be awaited, because I can't have a Promise and a onRecordedCallback in the same function. Hopefully TurboModules allows that
|
// TODO: startRecording() cannot be awaited, because I can't have a Promise and a onRecordedCallback in the same function. Hopefully TurboModules allows that
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun startRecording(viewTag: Int, options: ReadableMap, onRecordCallback: Callback) {
|
fun startRecording(viewTag: Int, options: ReadableMap, onRecordCallback: Callback) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
val view = findCameraView(viewTag)
|
val view = findCameraView(viewTag)
|
||||||
try {
|
try {
|
||||||
view.startRecording(options, onRecordCallback)
|
view.startRecording(options, onRecordCallback)
|
||||||
@ -112,7 +108,7 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun focus(viewTag: Int, point: ReadableMap, promise: Promise) {
|
fun focus(viewTag: Int, point: ReadableMap, promise: Promise) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
withPromise(promise) {
|
withPromise(promise) {
|
||||||
val view = findCameraView(viewTag)
|
val view = findCameraView(viewTag)
|
||||||
view.focus(point)
|
view.focus(point)
|
||||||
@ -126,7 +122,7 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun getAvailableCameraDevices(promise: Promise) {
|
fun getAvailableCameraDevices(promise: Promise) {
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
withPromise(promise) {
|
withPromise(promise) {
|
||||||
val extensionsManager = ExtensionsManager.getInstance(reactApplicationContext).await()
|
val extensionsManager = ExtensionsManager.getInstance(reactApplicationContext).await()
|
||||||
val cameraProvider = ProcessCameraProvider.getInstance(reactApplicationContext).await()
|
val cameraProvider = ProcessCameraProvider.getInstance(reactApplicationContext).await()
|
||||||
|
@ -39,14 +39,6 @@ public abstract class FrameProcessorPlugin {
|
|||||||
mHybridData = initHybrid(name);
|
mHybridData = initHybrid(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
super.finalize();
|
|
||||||
if (mHybridData != null) {
|
|
||||||
mHybridData.resetNative();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native @NonNull HybridData initHybrid(@NonNull String name);
|
private native @NonNull HybridData initHybrid(@NonNull String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,9 +10,10 @@ import com.mrousavy.camera.CameraView
|
|||||||
import com.mrousavy.camera.ViewNotFoundError
|
import com.mrousavy.camera.ViewNotFoundError
|
||||||
import com.swmansion.reanimated.Scheduler
|
import com.swmansion.reanimated.Scheduler
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
|
||||||
@Suppress("KotlinJniMissingFunction") // I use fbjni, Android Studio is not smart enough to realize that.
|
@Suppress("KotlinJniMissingFunction") // I use fbjni, Android Studio is not smart enough to realize that.
|
||||||
class FrameProcessorRuntimeManager(context: ReactApplicationContext) {
|
class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProcessorThread: ExecutorService) {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "FrameProcessorRuntime"
|
const val TAG = "FrameProcessorRuntime"
|
||||||
val Plugins: ArrayList<FrameProcessorPlugin> = ArrayList()
|
val Plugins: ArrayList<FrameProcessorPlugin> = ArrayList()
|
||||||
@ -30,7 +31,7 @@ class FrameProcessorRuntimeManager(context: ReactApplicationContext) {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
val holder = context.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl
|
val holder = context.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl
|
||||||
mScheduler = VisionCameraScheduler()
|
mScheduler = VisionCameraScheduler(frameProcessorThread)
|
||||||
mContext = WeakReference(context)
|
mContext = WeakReference(context)
|
||||||
mHybridData = initHybrid(context.javaScriptContextHolder.get(), holder, mScheduler)
|
mHybridData = initHybrid(context.javaScriptContextHolder.get(), holder, mScheduler)
|
||||||
initializeRuntime()
|
initializeRuntime()
|
||||||
@ -42,10 +43,7 @@ class FrameProcessorRuntimeManager(context: ReactApplicationContext) {
|
|||||||
Log.i(TAG, "Successfully installed ${Plugins.count()} Frame Processor Plugins!")
|
Log.i(TAG, "Successfully installed ${Plugins.count()} Frame Processor Plugins!")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
@Suppress("unused")
|
||||||
mHybridData.resetNative()
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
@Keep
|
@Keep
|
||||||
fun findCameraViewById(viewId: Int): CameraView {
|
fun findCameraViewById(viewId: Int): CameraView {
|
||||||
|
@ -2,28 +2,26 @@ package com.mrousavy.camera.frameprocessor;
|
|||||||
|
|
||||||
import com.facebook.jni.HybridData;
|
import com.facebook.jni.HybridData;
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
import com.mrousavy.camera.CameraViewModule;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
@SuppressWarnings("JavaJniMissingFunction") // using fbjni here
|
@SuppressWarnings("JavaJniMissingFunction") // using fbjni here
|
||||||
public class VisionCameraScheduler {
|
public class VisionCameraScheduler {
|
||||||
|
@SuppressWarnings({"unused", "FieldCanBeLocal"})
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
private final HybridData mHybridData;
|
private final HybridData mHybridData;
|
||||||
|
private final ExecutorService frameProcessorThread;
|
||||||
|
|
||||||
public VisionCameraScheduler() {
|
public VisionCameraScheduler(ExecutorService frameProcessorThread) {
|
||||||
|
this.frameProcessorThread = frameProcessorThread;
|
||||||
mHybridData = initHybrid();
|
mHybridData = initHybrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
mHybridData.resetNative();
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private native HybridData initHybrid();
|
private native HybridData initHybrid();
|
||||||
private native void triggerUI();
|
private native void triggerUI();
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
private void scheduleTrigger() {
|
private void scheduleTrigger() {
|
||||||
CameraViewModule.Companion.getFrameProcessorThread().submit(this::triggerUI);
|
frameProcessorThread.submit(this::triggerUI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user