Last active
May 3, 2018 03:59
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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