@bits-innovate/react-native-vstarcam 1.0.52 → 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,14 +11,13 @@ import com.facebook.react.bridge.ReactApplicationContext;
11
11
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
12
12
  import com.facebook.react.bridge.ReactMethod;
13
13
  import com.facebook.react.bridge.WritableMap;
14
+ import com.facebook.react.bridge.LifecycleEventListener;
14
15
  import com.facebook.react.module.annotations.ReactModule;
15
16
  import com.facebook.react.modules.core.DeviceEventManagerModule;
16
17
 
17
18
  import java.io.BufferedReader;
18
19
  import java.io.InputStreamReader;
19
- import java.lang.reflect.InvocationHandler;
20
20
  import java.lang.reflect.Method;
21
- import java.lang.reflect.Proxy;
22
21
  import java.net.HttpURLConnection;
23
22
  import java.net.URL;
24
23
  import java.util.HashMap;
@@ -46,7 +45,7 @@ import org.json.JSONObject;
46
45
  * - init(ClientStateListener, ClientCommandListener, ClientReleaseListener)
47
46
  */
48
47
  @ReactModule(name = VStarCamModule.MODULE_NAME)
49
- public class VStarCamModule extends ReactContextBaseJavaModule {
48
+ public class VStarCamModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
50
49
  private static final String TAG = "VStarCamModule";
51
50
  public final static String MODULE_NAME = "VStarCam";
52
51
 
@@ -204,6 +203,9 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
204
203
  this.reactContext = reactContext;
205
204
  this.executor = Executors.newCachedThreadPool();
206
205
 
206
+ // Register for lifecycle events to tear down JNI on fast-refresh
207
+ reactContext.addLifecycleEventListener(this);
208
+
207
209
  // Update current context for static proxies
208
210
  currentContext = new java.lang.ref.WeakReference<>(reactContext);
209
211
 
@@ -234,6 +236,39 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
234
236
  }
235
237
  }
236
238
 
239
+ @Override
240
+ public void onHostResume() {
241
+ }
242
+
243
+ @Override
244
+ public void onHostPause() {
245
+ }
246
+
247
+ @Override
248
+ public void onHostDestroy() {
249
+ Log.d(TAG, "onHostDestroy: Tearing down all P2P connections before JNI environment goes out of scope.");
250
+ executor.execute(() -> {
251
+ try {
252
+ if (jniApiClass != null) {
253
+ Method disconnectMethod = jniApiClass.getMethod("disconnect", long.class);
254
+ Method destroyMethod = jniApiClass.getMethod("destroy", long.class);
255
+
256
+ for (Map.Entry<Integer, ClientInfo> entry : clients.entrySet()) {
257
+ long sdkPtr = entry.getValue().sdkClientPtr;
258
+ if (sdkPtr != 0) {
259
+ Log.d(TAG, "Teardown SDK client: " + sdkPtr);
260
+ try { disconnectMethod.invoke(null, sdkPtr); } catch (Exception e) {}
261
+ try { destroyMethod.invoke(null, sdkPtr); } catch (Exception e) {}
262
+ }
263
+ }
264
+ clients.clear();
265
+ }
266
+ } catch (Exception e) {
267
+ Log.e(TAG, "Failed to teardown P2P in onHostDestroy", e);
268
+ }
269
+ });
270
+ }
271
+
237
272
  private void initializeP2P() {
238
273
  if (!VStarCamModule.isNativeLibraryLoaded || VStarCamModule.jniApiClass == null) {
239
274
  Log.e(TAG, "Cannot initialize P2P: native library or JNIApi not available");
@@ -246,126 +281,62 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
246
281
  }
247
282
 
248
283
  try {
249
- // Create dynamic proxy for ClientStateListener
250
- stateListenerProxy = Proxy.newProxyInstance(
251
- stateListenerClass.getClassLoader(),
252
- new Class<?>[] { stateListenerClass },
253
- new InvocationHandler() {
284
+ // Create CONCRETE listener implementations (NOT dynamic proxies).
285
+ // Dynamic Proxies ($Proxy0 classes) crash under CheckJNI when invoked
286
+ // from the C++ SDK's native pthreads via CallVoidMethod. Concrete
287
+ // anonymous classes have real vtables that work correctly.
288
+
289
+ com.vstarcam.app_p2p_api.ClientStateListener stateListener =
290
+ new com.vstarcam.app_p2p_api.ClientStateListener() {
254
291
  @Override
255
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
292
+ public void stateListener(long clientPtr, int state) {
256
293
  try {
257
- String methodName = method.getName();
258
-
259
- // Handle standard Object methods
260
- if (method.getDeclaringClass() == Object.class) {
261
- if (methodName.equals("equals")) return proxy == args[0];
262
- if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
263
- if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
264
- }
265
-
266
- Log.d(TAG, "ClientStateListener." + methodName + " called");
267
- if (methodName.equals("stateListener")) {
268
- // args: long clientPtr, int state
269
- long clientPtr = 0;
270
- int state = 0;
271
-
272
- if (args != null && args.length >= 2) {
273
- if (args[0] instanceof Number) clientPtr = ((Number) args[0]).longValue();
274
- if (args[1] instanceof Number) state = ((Number) args[1]).intValue();
275
- }
276
-
277
- Log.d(TAG, "State callback: clientPtr=" + clientPtr + ", state=" + state);
278
-
279
- // Post to main thread for React Native compatibility
280
- VStarCamModule.handleStateChange(clientPtr, state);
281
- }
294
+ Log.d(TAG, "State callback: clientPtr=" + clientPtr + ", state=" + state);
295
+ VStarCamModule.handleStateChange(clientPtr, state);
282
296
  } catch (Exception e) {
283
297
  Log.e(TAG, "Error in stateListener callback", e);
284
298
  }
285
- return null;
286
299
  }
287
- }
288
- );
289
- jniProxyRoots.add(stateListenerProxy);
300
+ };
290
301
 
291
- // Create dynamic proxy for ClientCommandListener
292
- commandListenerProxy = Proxy.newProxyInstance(
293
- commandListenerClass.getClassLoader(),
294
- new Class<?>[] { commandListenerClass },
295
- new InvocationHandler() {
302
+ com.vstarcam.app_p2p_api.ClientCommandListener commandListener =
303
+ new com.vstarcam.app_p2p_api.ClientCommandListener() {
296
304
  @Override
297
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
305
+ public void commandListener(long clientPtr, byte[] data, int length) {
298
306
  try {
299
- String methodName = method.getName();
300
-
301
- // Handle standard Object methods
302
- if (method.getDeclaringClass() == Object.class) {
303
- if (methodName.equals("equals")) return proxy == args[0];
304
- if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
305
- if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
306
- }
307
-
308
- Log.d(TAG, "ClientCommandListener." + methodName + " called");
309
- if (methodName.equals("commandListener")) {
310
- // args: long clientPtr, byte[] data, int length (from interface)
311
- long clientPtr = 0;
312
- byte[] data = null;
313
- int length = 0;
314
-
315
- if (args != null && args.length >= 3) {
316
- if (args[0] instanceof Number) clientPtr = ((Number) args[0]).longValue();
317
- if (args[1] instanceof byte[]) data = (byte[]) args[1];
318
- if (args[2] instanceof Number) length = ((Number) args[2]).intValue();
319
- }
320
-
321
- Log.d(TAG, "Command callback: clientPtr=" + clientPtr + ", dataLen=" + (data != null ? data.length : 0));
322
-
323
- // Post to main thread for React Native compatibility
324
- VStarCamModule.handleCommandReceive(clientPtr, 0, data);
325
- }
307
+ Log.d(TAG, "Command callback: clientPtr=" + clientPtr + ", dataLen=" + (data != null ? data.length : 0));
308
+ VStarCamModule.handleCommandReceive(clientPtr, 0, data);
326
309
  } catch (Exception e) {
327
310
  Log.e(TAG, "Error in commandListener callback", e);
328
311
  }
329
- return null;
330
312
  }
331
- }
332
- );
333
- jniProxyRoots.add(commandListenerProxy);
313
+ };
334
314
 
335
- // Create dynamic proxy for ClientReleaseListener
336
- releaseListenerProxy = Proxy.newProxyInstance(
337
- releaseListenerClass.getClassLoader(),
338
- new Class<?>[] { releaseListenerClass },
339
- new InvocationHandler() {
315
+ com.vstarcam.app_p2p_api.ClientReleaseListener releaseListener =
316
+ new com.vstarcam.app_p2p_api.ClientReleaseListener() {
340
317
  @Override
341
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
318
+ public void releaseListener(long clientPtr) {
342
319
  try {
343
- String methodName = method.getName();
344
-
345
- // Handle standard Object methods
346
- if (method.getDeclaringClass() == Object.class) {
347
- if (methodName.equals("equals")) return proxy == args[0];
348
- if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
349
- if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
350
- }
351
-
352
- Log.d(TAG, "ClientReleaseListener." + methodName + " called");
320
+ Log.d(TAG, "Release callback: clientPtr=" + clientPtr);
353
321
  } catch (Exception e) {
354
322
  Log.e(TAG, "Error in releaseListener callback", e);
355
323
  }
356
- return null;
357
324
  }
358
- }
359
- );
360
- jniProxyRoots.add(releaseListenerProxy);
325
+ };
326
+
327
+ // Keep strong references to prevent GC
328
+ stateListenerProxy = stateListener;
329
+ commandListenerProxy = commandListener;
330
+ releaseListenerProxy = releaseListener;
331
+ jniProxyRoots.add(stateListener);
332
+ jniProxyRoots.add(commandListener);
333
+ jniProxyRoots.add(releaseListener);
361
334
 
362
335
  // Call JNIApi.init(stateListener, commandListener, releaseListener)
363
- Method initMethod = VStarCamModule.jniApiClass.getMethod("init",
364
- VStarCamModule.stateListenerClass, VStarCamModule.commandListenerClass, VStarCamModule.releaseListenerClass);
365
- initMethod.invoke(null, stateListenerProxy, commandListenerProxy, releaseListenerProxy);
336
+ com.vstarcam.JNIApi.init(stateListener, commandListener, releaseListener);
366
337
 
367
338
  VStarCamModule.isP2PInitialized = true;
368
- Log.d(TAG, "P2P system initialized successfully!");
339
+ Log.d(TAG, "P2P system initialized successfully with concrete listeners!");
369
340
  } catch (Exception e) {
370
341
  Log.e(TAG, "Failed to initialize P2P: " + e.getMessage(), e);
371
342
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bits-innovate/react-native-vstarcam",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
4
4
  "description": "React Native bridge for VStarCam P2P SDK",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",