The Setup
I have a simple fragment that hosts a WebView component. During the initialization of this fragment, I register a few JavaScript interface methods to my app:
mJavaScriptHooks = new JavaScriptHooks();
...
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(mJavaScriptHooks, "Android");
My JavaScript hooks are nothing spectacular, and all are declared in the following manner:
public class JavaScriptHooks {
@JavascriptInterface
public void someMethod() {
...
}
...
}
In order to make sure that these methods are not obfuscated or optimized away into oblivion, I've added the following the my ProGuard configuration:
-keepattributes JavascriptInterface
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
The Problem
Everything works as expected. The web page successfully invokes the methods through the provided "Android" object, the methods are called and perform their corresponding task. However, it only works when I run the app using the APK that was generated through the Android Studio's Instant-Run feature.
It doesn't matter if the APK is installed through Android Studio or if I install it through the command-line using adb install
If I compile my APK through regular gradle (or if I disable Instant-Run), then the JavaScript interface stops working.
Debugging
First off, I've ensured that ProGuard is disabled. Even if ProGuard was somehow ✨ magically ✨ enabled, my ProGuard configuration should ensure that my JS methods are left alone.
I've also disassembled the APK to make sure that the methods being exposed through the JS Interface have not somehow become mangled (or removed). They're not. They seem to be present, as declared, in the DEX file.
When I run the APK that was generated with Instant-Run enabled, I can inspect (through Chrome) that the page loaded in the WebView and see that my "Android" object is available and all the methods I've exposed are there (and invokable):
> Android
Object
someMethod()
__proto__
However, when I run the APK with Instant-Run disabled (i.e. vanilla gradle), the "Android" object is available, but none of my methods are there. It's just an empty object.
> Android
Object
__proto__
The page I'm loading in WebView lives on a publicly-visible server that I control. The same page is loaded in both APK builds, and the methods are invoked in the same manner. Only the APK built with the Instant-Run feature seems to work.
I've been digging around all day, but I couldn't find anything really helpful. Most suggestions point to a ProGuard configuration problem or to old bugs (i.e Gingerbread-era) in WebView.