Skip to content

Instantly share code, notes, and snippets.

@aschrijver
Last active May 3, 2018 03:59
Show Gist options
  • Save aschrijver/c650614c3d75a5282880a83ed9598e4d to your computer and use it in GitHub Desktop.
Save aschrijver/c650614c3d75a5282880a83ed9598e4d to your computer and use it in GitHub Desktop.
RNNodeService using ScheduledExecutorService to cancel long-running NodeJS tasks (express webserver) - NOT Working
package world.fullcircle.nodeonandroid;
import android.app.Service;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.IBinder;
import android.util.Log;
import com.eclipsesource.v8.NodeJS;
import com.eclipsesource.v8.V8;
import com.eclipsesource.v8.utils.V8Runnable;
import com.eclipsesource.v8.utils.V8Thread;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class RNNodeService extends Service {
private static final String TAG = "RNNodeService";
private V8Thread nodeThread;
private ScheduledExecutorService shutdownMonitor;
private AtomicBoolean cancelled;
public RNNodeService() {
cancelled = new AtomicBoolean();
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
super.onDestroy();
stopNode();
}
public void stopNode() {
if (nodeThread != null && nodeThread.isAlive() && !nodeThread.isInterrupted()) {
Log.d(TAG, "Stopping NodeJS service");
// Interrupt node thread so no new messages are handled.
nodeThread.interrupt();
// Set cancel flag to terminate long-running node tasks.
cancelled.set(true);
try {
boolean terminated = shutdownMonitor.awaitTermination(3, TimeUnit.SECONDS);
Log.d(TAG, "NodeJS monitor " + (terminated ? "terminated" : "timed out"));
} catch (InterruptedException e) {
cancelled.set(false);
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if (nodeThread == null) {
Log.d(TAG, "Starting NodeJS service");
shutdownMonitor = Executors.newSingleThreadScheduledExecutor();
nodeThread = new V8Thread(new V8Runnable() {
@Override
public void run(V8 engine) {
final NodeJS nodeJs = NodeJS.createNodeJS();
// Move node application to temporary storage
AssetManager am = getAssets();
String cache = getCacheDir().getAbsolutePath();
String jsPath = cache + "/node";
copyAssets(am, "node", jsPath);
File appIndex = new File(jsPath, "index.js");
if (!appIndex.exists()) {
throw new RuntimeException("Background app index not found at " + appIndex);
}
Log.d(TAG, "Starting NodeJS monitor");
startMonitor(nodeJs);
Log.d(TAG, "Executing " + appIndex);
nodeJs.exec(appIndex);
while (nodeJs.isRunning() && !Thread.currentThread().isInterrupted()) {
nodeJs.handleMessage();
}
// ERROR Code never reaches this point. NodeJS not released
Log.d(TAG, "Releasing NodeJS " + (nodeJs.isRunning() ? "(running)" : "(finished)") +
", Thread " + (Thread.currentThread().isInterrupted() ? "(interrupted)" : "(running)"));
try {
nodeJs.release();
} catch (IllegalStateException e) {
throw new RuntimeException("Failed to release NodeJS", e);
}
}
});
nodeThread.start();
}
}
return START_STICKY;
}
private void startMonitor(final NodeJS nodeJs) {
shutdownMonitor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
if (cancelled.get()) {
if (nodeJs.isRunning()) {
Log.d(TAG, "Terminating NodeJS execution");
nodeJs.getRuntime().terminateExecution();
}
}
}
}, 1, 1, TimeUnit.SECONDS);
}
private static void copyAssets (AssetManager am, String src, String dest) {
try {
copyAssetFile(am, src, dest);
} catch (Exception e) {
try {
File dir = new File(dest);
dir.mkdir();
} catch (Exception e1) {}
try {
String[] files = am.list(src);
for (int i = 0; i < files.length; i++) {
copyAssets(am, src + "/" + files[i], dest + "/" + files[i]);
}
} catch (Exception e2) {}
}
}
private static void copyAssetFile(AssetManager am, String src, String dest) throws IOException {
InputStream in = am.open(src);
File destFile = new File(dest);
if (!destFile.exists()) destFile.createNewFile();
FileOutputStream out = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
in.close();
out.close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment