chariotsolutions / phonegap-nfc

PhoneGap NFC Plugin
MIT License
706 stars 557 forks source link

Plugin initialization error in older devices when using Cordova 5 #226

Closed homer-jay closed 6 years ago

homer-jay commented 8 years ago

CordovaWebView.sendJavascript method is deprecated. It has been deprecated from some time ago, but it worked. Now with Cordova 5 in a 4.2 device this exception is thrown during Cordova initialization:

02-26 10:06:30.048: W/dalvikvm(7979): DexOpt: method is in an interface
02-26 10:06:30.048: I/dalvikvm(7979): Could not find method org.apache.cordova.CordovaWebView.sendJavascript, referenced from method com.chariotsolutions.nfc.plugin.NfcPlugin.fireNdefEvent
02-26 10:06:30.048: W/dalvikvm(7979): VFY:  rejected Lcom/chariotsolutions/nfc/plugin/NfcPlugin;.fireNdefEvent (Ljava/lang/String;Landroid/nfc/tech/Ndef;[Landroid/os/Parcelable;)V
02-26 10:06:30.048: W/dalvikvm(7979): Verifier rejected class Lcom/chariotsolutions/nfc/plugin/NfcPlugin;
02-26 10:06:30.048: W/System.err(7979): java.lang.ClassNotFoundException: com.chariotsolutions.nfc.plugin.NfcPlugin
02-26 10:06:30.048: W/System.err(7979):     at java.lang.Class.classForName(Native Method)
02-26 10:06:30.048: W/System.err(7979):     at java.lang.Class.forName(Class.java:217)
02-26 10:06:30.048: W/System.err(7979):     at java.lang.Class.forName(Class.java:172)
02-26 10:06:30.048: W/System.err(7979):     at org.apache.cordova.PluginManager.instantiatePlugin(PluginManager.java:490)
02-26 10:06:30.048: W/System.err(7979):     at org.apache.cordova.PluginManager.getPlugin(PluginManager.java:170)
02-26 10:06:30.048: W/System.err(7979):     at org.apache.cordova.PluginManager.exec(PluginManager.java:123)
02-26 10:06:30.058: W/System.err(7979):     at org.apache.cordova.CordovaBridge.jsExec(CordovaBridge.java:59)
02-26 10:06:30.058: W/System.err(7979):     at org.apache.cordova.CordovaBridge.promptOnJsPrompt(CordovaBridge.java:135)
02-26 10:06:30.058: W/System.err(7979):     at org.apache.cordova.engine.SystemWebChromeClient.onJsPrompt(SystemWebChromeClient.java:124)
02-26 10:06:30.058: W/System.err(7979):     at android.webkit.CallbackProxy.handleMessage(CallbackProxy.java:886)
02-26 10:06:30.058: W/System.err(7979):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-26 10:06:30.058: W/System.err(7979):     at android.os.Looper.loop(Looper.java:137)
02-26 10:06:30.058: W/System.err(7979):     at android.app.ActivityThread.main(ActivityThread.java:4867)
02-26 10:06:30.058: W/System.err(7979):     at java.lang.reflect.Method.invokeNative(Native Method)
02-26 10:06:30.058: W/System.err(7979):     at java.lang.reflect.Method.invoke(Method.java:511)
02-26 10:06:30.058: W/System.err(7979):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
02-26 10:06:30.058: W/System.err(7979):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
02-26 10:06:30.058: W/System.err(7979):     at dalvik.system.NativeStart.main(Native Method)
02-26 10:06:30.058: W/System.err(7979): Caused by: java.lang.VerifyError: com/chariotsolutions/nfc/plugin/NfcPlugin
02-26 10:06:30.058: W/System.err(7979):     ... 18 more
02-26 10:06:30.058: I/System.out(7979): Error adding plugin com.chariotsolutions.nfc.plugin.NfcPlugin.
02-26 10:06:30.058: W/System.err(7979): java.lang.NullPointerException
02-26 10:06:30.068: W/System.err(7979):     at org.apache.cordova.PluginManager.getPlugin(PluginManager.java:172)
02-26 10:06:30.068: W/System.err(7979):     at org.apache.cordova.PluginManager.exec(PluginManager.java:123)
02-26 10:06:30.068: W/System.err(7979):     at org.apache.cordova.CordovaBridge.jsExec(CordovaBridge.java:59)
02-26 10:06:30.068: W/System.err(7979):     at org.apache.cordova.CordovaBridge.promptOnJsPrompt(CordovaBridge.java:135)
02-26 10:06:30.068: W/System.err(7979):     at org.apache.cordova.engine.SystemWebChromeClient.onJsPrompt(SystemWebChromeClient.java:124)
02-26 10:06:30.068: W/System.err(7979):     at android.webkit.CallbackProxy.handleMessage(CallbackProxy.java:886)
02-26 10:06:30.068: W/System.err(7979):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-26 10:06:30.068: W/System.err(7979):     at android.os.Looper.loop(Looper.java:137)
02-26 10:06:30.068: W/System.err(7979):     at android.app.ActivityThread.main(ActivityThread.java:4867)
02-26 10:06:30.068: W/System.err(7979):     at java.lang.reflect.Method.invokeNative(Native Method)
02-26 10:06:30.068: W/System.err(7979):     at java.lang.reflect.Method.invoke(Method.java:511)
02-26 10:06:30.068: W/System.err(7979):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
02-26 10:06:30.068: W/System.err(7979):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
02-26 10:06:30.078: W/System.err(7979):     at dalvik.system.NativeStart.main(Native Method)

As I said it was working fine in Cordova 3.7, and the method still exists in the CordovaWebView class, but for some unknown reason the exception is thrown only in older devices. And in my case I only got the exception when using NDEF-related functionality. If you only need to read a tag's ID you probably won't see it.

So I looked for alternatives to sendJavascript. Many other plugins stopped using it to get rid of the deprecation warning. In some repos (links at the end) I've seen they replaced the calls to webView.sendJavascript with calls to a private sendJavascript method that executes JavaScript in the WebView using alternative methods:

@TargetApi(Build.VERSION_CODES.KITKAT)
private void sendJavascript(final String javascript) {
    webView.post(new Runnable() {
        @Override
        public void run() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                webView.evaluateJavascript(javascript, null);
            } else {
                webView.loadUrl("javascript:"  +  javascript);
            }
        }
    });
}

This probably worked for Cordova 4, but then the guys at Cordova changed everything again in their new release (as they do always) and broke such code. There are two issues here that will prevent compilation in Cordova 5:

So the proposed fix for Cordova 5 now looks like this:

@TargetApi(Build.VERSION_CODES.KITKAT)
private void sendJavascript(final String javascript) {
    webView.getView().post(new Runnable() {
        @Override           
        public void run() {             
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                webView.sendJavascript(javascript);
            } else {                    
                webView.loadUrl("javascript:" + javascript);
            }
        }
    });
}

It falls back again to sendJavascript but only for KitKat and newer versions. I've tested this code in 4.2 and 5.1 devices and it works well.

But there are a number of issues with this code:

So my proposed fix is simpler and can be seen in this commit. I've tested it in 4.2 and 5.1 devices and it works, so I've created a pull request.

LINKS TO OTHER REPOS WITH RELATED PROBLEMS: https://github.com/katzer/cordova-plugin-local-notifications/issues/535 https://github.com/driftyco/ionic-plugin-keyboard/pull/40/commits

don commented 6 years ago

Closing old issue. Hopefully new Cordova and Android fixes all this.