How to run cordova plugin in Android background service?

34,412

Solution 1

You should use an embedded Cordova WebView, not a standard WebView. A standard WebView is not set up to handle Cordova plugins, and the device info is a plugin.

See the Cordova docs on embedding webviews.

Solution 2

WebViews can not execute javascript from a background service.

I would recommend using native code instead. But if you must use javascript, i would try this library

https://code.google.com/p/jav8/

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("jav8");

  try {
    engine.eval("print('Hello, world!')");
  } catch (ScriptException ex) {
      ex.printStackTrace();
  } 

First load the contens of your script into a string and then run engine.eval() method.

Example (Run "test.js" from assets):

AssetManager am = context.getAssets();
InputStream is = am.open("test.js");
BufferedReader r = new BufferedReader(new InputStreamReader(is));
StringBuilder total = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
    total.append(line);
}

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("jav8");

  try {
    engine.eval(total.toString());
  } catch (ScriptException ex) {
      ex.printStackTrace();
  }

Notice! The eval function expects only a javascript function to be executed at a time and returns the value of this function.

Solution 3

To work with Cordova plugins in WebView as background service, i've created class that implements CordovaInterface. Here is an example

 private class CordovaBackground extends Activity implements CordovaInterface {
    private ArrayList pluginEntries = new ArrayList();
    private CordovaPreferences preferences;
    private Context context;
    private Whitelist internalWhitelist;
    private Whitelist externalWhitelist;
    private CordovaWebViewBackground webView;
    protected LinearLayout root;
    private WindowManager serviceWindowManager;
    private final ExecutorService threadPool = Executors.newCachedThreadPool();

    public CordovaBackground(Context context, WindowManager windowManager) {
        attachBaseContext(context);
        this.context = context;
        this.serviceWindowManager = windowManager;
    }

    private void loadConfig() {
        ConfigXmlParser parser = new ConfigXmlParser();
        parser.parse(this);
        preferences = parser.getPreferences();
        internalWhitelist = parser.getInternalWhitelist();
        externalWhitelist = parser.getExternalWhitelist();;
        ArrayList<PluginEntry> allPluginEntries = parser.getPluginEntries();
        String[] backgroundPluginNames = {"File"};//not all plugins you need in service, here is the list of them
        ArrayList<String> backgroundPlugins = new ArrayList<String>(
            Arrays.asList(backgroundPluginNames));
        for (PluginEntry pluginEntry : allPluginEntries) {
            if (backgroundPlugins.contains(pluginEntry.service)) {
                pluginEntries.add(pluginEntry);
            }
        }
    }

    public void loadUrl(String url) {
        init();
        webView.loadUrl(url);
    }

    public void init() {
        loadConfig();
        webView = new CordovaWebViewBackground(context);
        if (webView.pluginManager == null) {
            CordovaWebViewClient webClient = webView.makeWebViewClient(this);
            CordovaChromeClientBackground webChromeClient = webView.makeWebChromeClient(this);
            webView.init(this, webClient, webChromeClient,
                    pluginEntries, internalWhitelist, externalWhitelist, preferences);
        }
    }

    public WindowManager getWindowManager() {
        return serviceWindowManager;
    }

    @Override
    public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
    }

    @Override
    public void setActivityResultCallback(CordovaPlugin plugin) {
    }

    @Override
    public Activity getActivity() {
        return this;
    }

    @Override
    public Object onMessage(String id, Object data) {
        return null;
    }

    @Override
    public ExecutorService getThreadPool() {
        return threadPool;
    }

    @Override
    public Intent registerReceiver(android.content.BroadcastReceiver receiver, android.content.IntentFilter filter) {
        return  getIntent();
    }

    @Override
    public String getPackageName() {
        return context.getPackageName();
    }

}

To prevent errors while cordova initializing, i've overrode onJsAlert method. If you have a time, you may have found better way.

 private class CordovaWebViewBackground extends CordovaWebView {

    public CordovaWebViewBackground(Context context) {
        super(context);
    }

    public CordovaChromeClientBackground makeWebChromeClient(CordovaInterface cordova) {
        return new CordovaChromeClientBackground(cordova, this);
    }

}

private class CordovaChromeClientBackground extends CordovaChromeClient {

    public CordovaChromeClientBackground(CordovaInterface ctx, CordovaWebView app) {
        super(ctx, app);
    }

    public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
        //super.onJsAlert(view, url, message, result);
        return true;
    }

}

How to use:

WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
CordovaBackground cordovaBackground = new CordovaBackground(this, wm);
cordovaBackground.setIntent(intent);
String url = "file:///android_asset/www/test.html";
cordovaBackground.loadUrl(url);
Share:
34,412
mehsen
Author by

mehsen

Updated on October 14, 2020

Comments

  • mehsen
    mehsen over 3 years

    I am working on mobile application developed on cordova . I want to implement a background service that do some work like open socket connection syncronise local database with remote one and notify the users on new remote pushes etc . The point is I have this code implemented in javascript but I want execute it i background.

    I searched internet for a cordova background service plugin.

    I have read some topics about background service in android these are useful ones I found:

    So I started writing cordova plugin (primarily on android) to execute the javascript code in background. I created a webview from the background service to execute the javascript from it. This works fine when I execute normal javascript but when it comes to cordova plugins js it fails for example the device device.uuid gives null.

    This is the java service code:

          public void onStart(Intent intent, int startId) {
          Toast.makeText(this, "My Happy Service Started", Toast.LENGTH_LONG).show();
           
               createBackGroundView();
               super.onStart(intent,startId);
        }
    
    
          public void createBackGroundView(){
    
             
            WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
           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_TOUCHABLE,
                       PixelFormat.TRANSLUCENT
               );
             
            params.gravity = Gravity.TOP | Gravity.LEFT;
            params.x = 0;
            params.y = 0;
            params.width = 200;
            params.height = 200;
           
            LinearLayout view = new LinearLayout(this);
               
            view.setLayoutParams(new RelativeLayout.LayoutParams(
                        android.view.ViewGroup.LayoutParams.MATCH_PARENT, 
                        android.view.ViewGroup.LayoutParams.MATCH_PARENT
                ));
               
            WebView wv = new WebView(this);
            wv.setLayoutParams(new LinearLayout.LayoutParams(
                        android.view.ViewGroup.LayoutParams.MATCH_PARENT,
                        android.view.ViewGroup.LayoutParams.MATCH_PARENT
                ));     
            view.addView(wv);
            wv.getSettings().setJavaScriptEnabled(true);
            wv.setWebChromeClient(new WebChromeClient());
                wv.loadUrl("file:///android_asset/www/background.html");
            wv.setWebViewClient(new WebViewClient() {
    
                @Override
                public void onReceivedError(final WebView view, int errorCode,
                        String description, final String failingUrl) {
                    Log.d("Error","loading web view");
                    super.onReceivedError(view, errorCode, description, failingUrl);
                }
            });
    
            windowManager.addView(view, params);
      
         }
    
    
    
         
    

    Update There is no error in the logcat. So I tried to write the device object on the screen and thats what I get :

      document.write(JSON.stringify(window.device))
    

    And this is the result :

      { available : false, 
        plaform : null , 
        version : null , 
        uuid : null ,  
        cordova : null ,
        model : null 
       }
    

    I tried to replace the standard webView with cordovaWebView But the same result is given.

           //WebView wv = new WebView(this);  Commented out
           CordovaWebView wv = new CordovaWebView(this);
    
      
    

    Any help about this problem ?