如何在Android后台服务中运行cordova插件?

Posted

技术标签:

【中文标题】如何在Android后台服务中运行cordova插件?【英文标题】:How to run cordova plugin in Android background service? 【发布时间】:2014-07-05 09:23:00 【问题描述】:

我正在开发在cordova 上开发的移动应用程序。我想实现一个后台服务,它可以做一些工作,比如打开套接字连接将本地数据库与远程数据库同步,并在新的远程推送时通知用户等。关键是我在 javascript 中实现了这段代码,但我想在后台执行它。

我在互联网上搜索了一个 cordova 后台服务插件。

katzer/cordova-plugin-background-mode

jocull/phonegap-backgroundjs

red-folder

我认为最好的是red-folder,但它只适用于android,它不允许我编写要在后台执行的javascript。但只是在 java 和 javascript 之间交换 json。

我已经阅读了一些关于 android 后台服务的主题,这些都是我发现的有用的主题:

create-a-service-on-android-with-phonegap-application simple-android-service-example-code-description-start-stop-service android-using-webview-outside-an-activity-context

所以我开始编写 cordova 插件(主要在 android 上)来在后台执行 javascript 代码。我从后台服务创建了一个 webview 以从中执行 javascript。这在我执行普通 javascript 时工作正常,但是当涉及到 cordova 插件 js 时它会失败,例如设备 device.uuid 提供 null

这是java服务代码:

      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);
  
     



     

更新 logcat中没有错误。 所以我试图在屏幕上写设备对象,这就是我得到的:

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

这就是结果:

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

我尝试将标准的webView 替换为cordovaWebView,但给出了相同的结果。

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

  

对这个问题有什么帮助吗?

【问题讨论】:

有帮助吗:***.com/questions/21505369/… 在我的情况下,javascript 被执行并继续运行,但问题是在后台执行 cordova plugin js 能不能发个错误或者日志 @kirchberger 我更新了我的问题看上面我没有错误但设备对象为空 您是否按照我在回答中的建议考虑使用 Cordova WebView?在我看来,设备插件的 .js 只是初始化了一个空的设备对象,然后它调用了本机部分,但这会失败(默默地),因为标准的 WebView 没有处理程序。 【参考方案1】:

您应该使用嵌入式 Cordova WebView,而不是标准 WebView。没有设置标准 WebView 来处理 Cordova 插件,并且设备信息是一个插件。

请参阅Cordova docs on embedding webviews。

【讨论】:

给出相同的结果我将编辑我的问题以查看我如何使用cordovaWebView 您是否按照文档中的所有步骤进行操作?您是否为插件设置了线程池以在其中运行? 从您在回答中所写的内容来看,您似乎只使用 CordovaWebView 类,但在 Cordova 文档中(我的回答中的链接)很明显,必须采取更多步骤才能配置CordovaWeb 视图。正是这个配置连接了插件(例如,现在不工作的设备插件)。【参考方案2】:

WebViews 无法从后台服务执行 javascript。

我建议改用本机代码。但如果你必须使用 javascript,我会试试这个库

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();
   

首先将脚本的内容加载到字符串中,然后运行engine.eval() 方法。

示例(从资产运行“test.js”):

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();
  

注意! eval 函数要求一次只执行一个 javascript 函数并返回该函数的值。

【讨论】:

我正在使用服务中的 webview 来执行我的 js 并且它正在工作,但是在使用 cordova 插件的 javascript 时失败 对这个问题有任何想法吗? @mehsen 你不能在没有初始化的情况下使用 Cordova 插件。当 Cordova 引擎初始化时,它会经过一个复杂的过程来设置插件。您需要在后台服务中重复整个过程。 Webview 可以从后台服务执行js。我在 github.com/mauron85/background-geolocation-android/tree/master/… 中使用 evgenii/jsevaluator。【参考方案3】:

为了将 WebView 中的 Cordova 插件用作后台服务,我创建了实现 CordovaInterface 的类。 这是一个例子

 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();
    


为了防止在 cordova 初始化时出错,我已经覆盖了 onJsAlert 方法。如果你有时间,你可能会找到更好的方法。

 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;
    


使用方法:

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);

【讨论】:

以上是关于如何在Android后台服务中运行cordova插件?的主要内容,如果未能解决你的问题,请参考以下文章

cordova-ionic 应用程序在后台进行地理定位 - android 和 ios

如何在android studio中cordova的混合开发

在后台获取 Ionic/Cordova 应用程序的位置

在 cordova/phonegap Android 应用程序上关闭屏幕后媒体停止播放

apache cordova 应用程序中的后台服务

Ionic/Cordova 应用程序不会在后台收到推送通知