How to run Javascript code in a background service on Android

Ori Harel
4 min readMay 26, 2017

--

If you’re a web shop, like my employer Capriza, you are probably having a blast in the last decade. Javascript have sprung to be the go to language by most developers. With stronger than ever client-side devices, and ports to the server side, it’s pretty a no-brainer on what technology you should invest in. I really like Jeff Atwood formulating this into a law:

Any application that can be written in JavaScript, will eventually be written in JavaScript

With its cross-platform nature, we are also seeing it taking on mobile native as well with the growing success of React Native. However, React Native strongest point is allowing developers to write Javascript in order to render native widgets while mobile development is a lot more than UI.

When building a mobile app in 2017, one must think beyond the online experience with the user. Context aware software must be taking into consideration in order to compete with the congested app stores. By “Context aware” I refer to software running in the background to ever poll the user’s context (location, time of day, new stuff that are fetched from the backend) which dismisses the user from checking in to the app. instead, the app is checking in with the user.

But, what if all your software is already written in Javascript and you want to run it on Android in the background? On Android SDK, Javascript runs in a WebView. WebViews runs on the UI thread and a background service is forbidden of running in a UI thread. You could include a Javascript runtime engine with your app but that will explode your app size and will put huge amount of effort on your R&D.

Entering the SDK’s WindowManager. This component is a ViewManager that is in charge on the phone’s display, which is always alive. even when your app is not active. So when running a Service, getting a pointer to it is as simple as calling Context.getSystemService(Context.WINDOW_SERVICE).

Adding/removing views to the WindowManager is possible from all threads, and doesn’t enforce the runOnUiThread requirement. With the permission of android.permission.SYSTEM_ALERT_WINDOW, you’re app is now allowed to add views to the WindowManager, WebView included. All you need to do is call your URL and vualá! you have JavaScript running in the background.

Here’s a code snippet:

...

public class BackgroundService extends Service {

...

@Override
public int onStartCommand(Intent intent, int flags, int startId{


final WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT
);

params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 0;
params.width = 0;
params.height = 0;

final WebView wv = new WebView(this);

wv.setWebViewClient(new WebViewClient() {

@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
Log.d("Error","loading web view: request: "+request+" error: "+error);
}

@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {


if (request.getUrl().toString().contains("/endProcess")) {

windowManager.removeView(wv);

wv.post(new Runnable() {
@Override
public void run() {
wv.destroy();
}
});
stopSelf();
return new WebResourceResponse("bgsType", "someEncoding", null);
}
else {
return null;
}
}
});
wv.loadUrl(bgsUrl);
windowManager.addView(wv, params);
}

...
}

This code runs in Android’s Service component. After getting a pointer for the WindowManager from the system service, we declare it’s layout params. In our case, there is no visible UI — we’re just want to run our Javascript in the background — so we declare it not to be focusable or touchable as well as give it a zero width and height.

Then we create the WebView. We also can add the WebViewClient to intercept URL calling — this is a nice way of communicating between Javascript and native (that can also be done by applying a Javascript interface and calling WebView’s addJavascriptInterface method).

We then call loadUrl and WindowManager’s addView method. now the URL can point to some HTML that loads Javascript and performs whatever logic. It’s important that once the Javascript side is finished, it will communicate this to the WebViewClient (e.g. invoking some special URL — in our case /endProcess) which in turn will end the Android’s Service (by calling stopSelf).

Few important notes, however:

As mentioned, this flow uses a very special permission, android.permission.SYSTEM_ALERT_WINDOW which Google is very careful with. This permission allows your app to draw on other app. You can see this in the special permissions page on the setting (see screenshot below)

A look inside Android’s special access settings page. The “Draw over other apps” permission has been the topic of some disputes among Android bloggers

This permission can be abused by ad networks and malicious apps for click jacking and overlay display. So you need to stay up to date with the latest updates from Android (for instance, in Android O you’ll need to add TYPE_APPLICATION_PANEL instead of TYPE_PHONE in the WindowManager’s layout params).

You also need to be careful not to drain the user’s battery and make sure that the Javascript is performing it’s task very quickly and calls the stopSelf in a quick manner.

--

--

Ori Harel

Engineering Manager, love startups, love NBA Basketball but mostly procrastinate. Work @ Taranis