All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.react.jstasks.HeadlessJsTaskContext Maven / Gradle / Ivy

There is a newer version: 0.52.u
Show newest version
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.react.jstasks;

import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import android.os.Handler;
import android.util.SparseArray;

import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.uimanager.AppRegistry;

/**
 * Helper class for dealing with JS tasks. Handles per-ReactContext active task tracking, starting /
 * stopping tasks and notifying listeners.
 */
public class HeadlessJsTaskContext {

  private static final WeakHashMap INSTANCES =
    new WeakHashMap<>();

  /**
   * Get the task helper instance for a particular {@link ReactContext}. There is only one instance
   * per context.
   * 

* Note: do not hold long-lived references to the object returned here, as that * will cause memory leaks. Instead, just call this method on-demand. */ public static HeadlessJsTaskContext getInstance(ReactContext context) { HeadlessJsTaskContext helper = INSTANCES.get(context); if (helper == null) { helper = new HeadlessJsTaskContext(context); INSTANCES.put(context, helper); } return helper; } private final WeakReference mReactContext; private final Set mHeadlessJsTaskEventListeners = new CopyOnWriteArraySet<>(); private final AtomicInteger mLastTaskId = new AtomicInteger(0); private final Handler mHandler = new Handler(); private final Set mActiveTasks = new CopyOnWriteArraySet<>(); private final SparseArray mTaskTimeouts = new SparseArray<>(); private HeadlessJsTaskContext(ReactContext reactContext) { mReactContext = new WeakReference(reactContext); } /** * Register a task lifecycle event listener. */ public void addTaskEventListener(HeadlessJsTaskEventListener listener) { mHeadlessJsTaskEventListeners.add(listener); } /** * Unregister a task lifecycle event listener. */ public void removeTaskEventListener(HeadlessJsTaskEventListener listener) { mHeadlessJsTaskEventListeners.remove(listener); } /** * Get whether there are any running JS tasks at the moment. */ public boolean hasActiveTasks() { return mActiveTasks.size() > 0; } /** * Start a JS task. Handles invoking {@link AppRegistry#startHeadlessTask} and notifying * listeners. * * @return a unique id representing this task instance. */ public synchronized int startTask(final HeadlessJsTaskConfig taskConfig) { UiThreadUtil.assertOnUiThread(); ReactContext reactContext = Assertions.assertNotNull( mReactContext.get(), "Tried to start a task on a react context that has already been destroyed"); if (reactContext.getLifecycleState() == LifecycleState.RESUMED && !taskConfig.isAllowedInForeground()) { throw new IllegalStateException( "Tried to start task " + taskConfig.getTaskKey() + " while in foreground, but this is not allowed."); } final int taskId = mLastTaskId.incrementAndGet(); mActiveTasks.add(taskId); reactContext.getJSModule(AppRegistry.class) .startHeadlessTask(taskId, taskConfig.getTaskKey(), taskConfig.getData()); if (taskConfig.getTimeout() > 0) { scheduleTaskTimeout(taskId, taskConfig.getTimeout()); } for (HeadlessJsTaskEventListener listener : mHeadlessJsTaskEventListeners) { listener.onHeadlessJsTaskStart(taskId); } return taskId; } /** * Finish a JS task. Doesn't actually stop the task on the JS side, only removes it from the list * of active tasks and notifies listeners. A task can only be finished once. * * @param taskId the unique id returned by {@link #startTask}. */ public synchronized void finishTask(final int taskId) { Assertions.assertCondition( mActiveTasks.remove(taskId), "Tried to finish non-existent task with id " + taskId + "."); Runnable timeout = mTaskTimeouts.get(taskId); if (timeout != null) { mHandler.removeCallbacks(timeout); mTaskTimeouts.remove(taskId); } UiThreadUtil.runOnUiThread(new Runnable() { @Override public void run() { for (HeadlessJsTaskEventListener listener : mHeadlessJsTaskEventListeners) { listener.onHeadlessJsTaskFinish(taskId); } } }); } /** * Check if a given task is currently running. A task is stopped if either {@link #finishTask} is * called or it times out. */ public synchronized boolean isTaskRunning(final int taskId) { return mActiveTasks.contains(taskId); } private void scheduleTaskTimeout(final int taskId, long timeout) { Runnable runnable = new Runnable() { @Override public void run() { finishTask(taskId); } }; mTaskTimeouts.append(taskId, runnable); mHandler.postDelayed(runnable, timeout); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy