1

I'm getting a NullPointerException on the following call in Android 2.3.4:

java.lang.NullPointerException
    at android.webkit.WebView.addPackageNames(WebView.java:4063)
    at com.my.company.MyClass$MyInnerClass.myMethod(MyClass.java:283)
    at android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(Native Method)
    at android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(Native Method)
    at android.webkit.BrowserFrame.loadUrl(BrowserFrame.java:246)
    at android.webkit.WebViewCore.loadUrl(WebViewCore.java:1981)
    at android.webkit.WebViewCore.access$1400(WebViewCore.java:53)
    at android.webkit.WebViewCore$EventHub$1.handleMessage(WebViewCore.java:1122)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:130)
    at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:674)
    at java.lang.Thread.run(Thread.java:1019)

MyClass$MyInnerClass is added to the JavaScript interface as

class MyClass {
    // ...
        myWebView.addJavascriptInterface(new MyInnerClass(), "MyInnerClass");
    // ...

    public void myOuterMethod(int param1, int param2) {
        // Notify a listener that myOuterMethod was called
    }

    private class MyInnerClass {
        public void myMethod(int param1, int param2) {
            myOuterMethod(param1, param2);
        }
    }
}

So, the JavaScript call MyInnerClass.myMethod(-1, -1) seems to come over the Java-JavaScript bridge fine, but fails in the addPackageNames call, which isn't my code.

I've looked at the android.webkit.WebView class in GrepCode, but I can't figure out how I could have caused this. The only line in addPackageNames is

public void addPackageNames(Set<String> packageNames) { 
    mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, packageNames);
}

So, I've come to the conclusion that either mWebViewCore or EventHub is null.

Can any Android experts shed some light on this? Is this a known bug? Did I cause this? If so, how? If not, how might I prevent this?

boo-urns
  • 10,136
  • 26
  • 71
  • 107
  • Does your javascript interface require invoking a method on a inner class? – Morrison Chang Sep 21 '12 at 06:16
  • I'm not sure I understand your question. The JavaScript call is `MyInnerClass.myMethod(-1, -1)`. So, yes? – boo-urns Sep 21 '12 at 16:23
  • The javascript interfaces I've seen all use a regular class rather than inner class. I'm wondering if there was a specific reason you picked inner rather than regular class as you are only supposed to use the inner with the outer, and you are handing it 'bare' to the addJavascriptInterface method. http://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class – Morrison Chang Sep 21 '12 at 16:34
  • I'm confused. Why would that matter? Doesn't the bridge just "bind" a Java object to a JavaScript object? Outer or inner class, so long as the object still exists when the JavaScript call is made, I don't understand why the distinction matters in this case. – boo-urns Sep 21 '12 at 17:27
  • You need to provide the implementation for your myMethod(). From the stack trace one can deduce that WebView.addPackageNames is being called from within your method so seeind what's happening there would be helpful. Btw. EventHub.ADD_PACKAGE_NAMES is a static reference so EventHub cannot be null (it isn't an instance). mWebViewCore must be null in this case. – gardarh Oct 03 '12 at 22:48
  • @gardarh myMethod() doesn't explicitly call WebView.addPackageNames. The subsequent code from myMethod() doesn't explicitly call WebView.addPackageNames. In fact the string "addPackageNames" doesn't appear anywhere in my project. – boo-urns Oct 04 '12 at 04:18
  • Note that WebView.addPackageNames is not a public API (that is, it says that in the comments for that method). The Android documentation on the WebView class doesn't even mention addPackageNames anywhere. So, I'm trying to find out what, in the Android source code, calls addPackageNames. Something I'm doing is implicitly causing this even though I don't do anything explicitly with the WebView subsequently. – boo-urns Oct 04 '12 at 04:28
  • I've updated the question with what happens in myMethod. It has one line, and it calls a method in the outer class. – boo-urns Oct 04 '12 at 04:33

2 Answers2

0

This is definitelly related to the mWebViewCore being set to null.

mWebViewCore is instantiated inside of the ViewView's constructor. And the only place where it is set to null is a public method destroy().

Do you call webView.destroy() anywhere in the code?

Vit Khudenko
  • 28,288
  • 10
  • 63
  • 91
  • No, I don't call webView.destroy() anywhere. Thanks for the suggestion, though! – boo-urns Oct 04 '12 at 04:53
  • Could myWebView be getting garbage collected at an unexpected time? – boo-urns Oct 04 '12 at 04:53
  • Well, I don't believe that. My guess is that maybe Android itself tries to release resources when activity is being destroyed and probably it is too clever to call `destroy()` on any found `WebView`s. But since web content loading happens on a background thread (it should to, as any networking activity in order not to block the main UI thread) it could call your JS interface AFTER the activity has passed `onDestroy()`. If this guess is true, then you should only reproduce the issue when open-close the activity quickly, so webview loading starts, but has no time to finish. – Vit Khudenko Oct 05 '12 at 08:38
0

It seems to me that your problem is that the outer class has not been instantiated when your myMethod is called. What's a bit interesting here is that the internal code that seems to be breaking in your case is no longer there in the Android 4.0 source code - so your issue might not occur on that version of Android. To me this also indicates that there were some issues with the previous version of Android Webview and perhaps you are encountering an issue with Android. But that's just speculation, my experience usually is that I'm doing something wrong, not the Android team :)

What could be a solution would be to wrap your code in an if statement:

if(MyClass.this != null) {
    myOuterMethod(param1, param2);
}

This is not a beautiful solution but if it works I think it won't break anything.

gardarh
  • 3,084
  • 2
  • 28
  • 31
  • One thought: I had some livecycle issues with the Android WebView the other day... sounded similar to your issue, the view seemed to be null when it wasn't. I ended up using a WebViewFragment ( https://github.com/android/platform_frameworks_base/blob/master/core/java/android/webkit/WebViewFragment.java ) - you just need to copy that class for backwards compatibility (if you are using the compat library there should be no other dependencies). The key to your problem might be the stuff that's being done in the onDestroy() function there. – gardarh Oct 04 '12 at 12:53
  • On other thought - is your js command by any chance being executed before the parent Activity's onCreate command? – gardarh Oct 04 '12 at 12:53
  • Where I wrote "when it wasn't" I meant "when it wasn't supposed to". If it is null it is null, no argument there :) – gardarh Oct 04 '12 at 13:29
  • The JS command is definitely getting executed after the parent Activity's onCreate. Thanks for the suggestions, though! Maybe I've run into an odd race condition. – boo-urns Oct 05 '12 at 07:12
  • Ok, good luck with it, please post your solution if you resolve this. – gardarh Oct 05 '12 at 09:42