Files
react-native/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.java
T
David Vacca aa5edca0e2 Migrate Nullable and NonNull annotations to AndroidX
Summary:
This diff migrates the usages Nullable and NonNull annotations to AndroidX instead of javax.

The purpose of this change is to bring consistency in the annotations used by the core of RN

Reviewed By: makovkastar

Differential Revision: D16054504

fbshipit-source-id: 21d888854da088d2a14615a90d4dc058e5286b91
2019-07-11 16:23:29 -07:00

169 lines
6.1 KiB
Java

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>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;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.PowerManager;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
import com.facebook.react.jstasks.HeadlessJsTaskContext;
import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Base class for running JS without a UI. Generally, you only need to override {@link
* #getTaskConfig}, which is called for every {@link #onStartCommand}. The result, if not {@code
* null}, is used to run a JS task.
*
* <p>If you need more fine-grained control over how tasks are run, you can override {@link
* #onStartCommand} and call {@link #startTask} depending on your custom logic.
*
* <p>If you're starting a {@code HeadlessJsTaskService} from a {@code BroadcastReceiver} (e.g.
* handling push notifications), make sure to call {@link #acquireWakeLockNow} before returning from
* {@link BroadcastReceiver#onReceive}, to make sure the device doesn't go to sleep before the
* service is started.
*/
public abstract class HeadlessJsTaskService extends Service implements HeadlessJsTaskEventListener {
private final Set<Integer> mActiveTasks = new CopyOnWriteArraySet<>();
private static @Nullable PowerManager.WakeLock sWakeLock;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
HeadlessJsTaskConfig taskConfig = getTaskConfig(intent);
if (taskConfig != null) {
startTask(taskConfig);
return START_REDELIVER_INTENT;
}
return START_NOT_STICKY;
}
/**
* Called from {@link #onStartCommand} to create a {@link HeadlessJsTaskConfig} for this intent.
*
* @param intent the {@link Intent} received in {@link #onStartCommand}.
* @return a {@link HeadlessJsTaskConfig} to be used with {@link #startTask}, or {@code null} to
* ignore this command.
*/
protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
return null;
}
/**
* Acquire a wake lock to ensure the device doesn't go to sleep while processing background tasks.
*/
@SuppressLint("WakelockTimeout")
public static void acquireWakeLockNow(Context context) {
if (sWakeLock == null || !sWakeLock.isHeld()) {
PowerManager powerManager =
Assertions.assertNotNull((PowerManager) context.getSystemService(POWER_SERVICE));
sWakeLock =
powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, HeadlessJsTaskService.class.getCanonicalName());
sWakeLock.setReferenceCounted(false);
sWakeLock.acquire();
}
}
@Override
public @Nullable IBinder onBind(Intent intent) {
return null;
}
/**
* Start a task. This method handles starting a new React instance if required.
*
* <p>Has to be called on the UI thread.
*
* @param taskConfig describes what task to start and the parameters to pass to it
*/
protected void startTask(final HeadlessJsTaskConfig taskConfig) {
UiThreadUtil.assertOnUiThread();
acquireWakeLockNow(this);
final ReactInstanceManager reactInstanceManager =
getReactNativeHost().getReactInstanceManager();
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
invokeStartTask(reactContext, taskConfig);
reactInstanceManager.removeReactInstanceEventListener(this);
}
});
reactInstanceManager.createReactContextInBackground();
} else {
invokeStartTask(reactContext, taskConfig);
}
}
private void invokeStartTask(ReactContext reactContext, final HeadlessJsTaskConfig taskConfig) {
final HeadlessJsTaskContext headlessJsTaskContext =
HeadlessJsTaskContext.getInstance(reactContext);
headlessJsTaskContext.addTaskEventListener(this);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
int taskId = headlessJsTaskContext.startTask(taskConfig);
mActiveTasks.add(taskId);
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (getReactNativeHost().hasInstance()) {
ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext != null) {
HeadlessJsTaskContext headlessJsTaskContext =
HeadlessJsTaskContext.getInstance(reactContext);
headlessJsTaskContext.removeTaskEventListener(this);
}
}
if (sWakeLock != null) {
sWakeLock.release();
}
}
@Override
public void onHeadlessJsTaskStart(int taskId) {}
@Override
public void onHeadlessJsTaskFinish(int taskId) {
mActiveTasks.remove(taskId);
if (mActiveTasks.size() == 0) {
stopSelf();
}
}
/**
* Get the {@link ReactNativeHost} used by this app. By default, assumes {@link #getApplication()}
* is an instance of {@link ReactApplication} and calls {@link
* ReactApplication#getReactNativeHost()}. Override this method if your application class does not
* implement {@code ReactApplication} or you simply have a different mechanism for storing a
* {@code ReactNativeHost}, e.g. as a static field somewhere.
*/
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getApplication()).getReactNativeHost();
}
}