问题
I am developing and app in Android which has a piece written in javascript and python using Transcrypt (it'd almost be the same as saying that I am developing that part straight in javascript, but I do not have full control on how things are made). Android displays that section in an Activity with a WebView. The relevant java code to start it is simply
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
mWebView.loadUrl("file:///android_asset/AndroidWebView.html");
and the relevant part of AndroidWebView.html
is similar to Transcript's demo for iOS
<script type="module">
import * as my_app from "./target/my_app.js"; window.my_app = my_app;
</script>
The app works just fine on the emulator, but on real devices WebView refuses to load the javascript and spits out the following error:
Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.
If I force the MIME type by changing the HTML to
<script type="text/javascript">
import * as my_app from "./target/my_app.js"; window.my_app = my_app;
</script>
which seems to work for other parts of my page that are plain javascript (not modules), it obviously fail with Uncaught SyntaxError: Cannot use import statement outside a module
.
Is there a way to either force the latter HTML to be a module, or the former HTML to not check the MIME types so strongly?
Looking at the options for WebView I could not see anything that seems relevant. Looking around, this issue seems to be caused exactly by this bug.
On the other hand, it seems to me that the problem is the syntax of the HTML script
tag of accepting type to be either "text/javascript"
or "module"
but not both at the same time (I tried and it seems to use only the first), whereas semantically there are reasons to have both. Is there are workaround, or even a hack to have both? I know one hack that would work for loading the my_app.js
, but that one would fail to further load other modules (which my_app.js
does). So that won't work unless there is a transpiler or something able to automatically merge all my modules into one. FWIW, those modules are generated from python from Transcrypt, so it must be an automated, not manual process.
PS: I am not HTML or javascript developer, these are my first steps in this realm, so please be kind against my speculations
PPS: I understand the reason for not doing any of this on a computer, but rather start up a server. And that is what I do for my development environment. But for one single Activity in an Android App, an HTTP server seems an overkill and a big headache (tracking ports, potential more failure modes, potentially visible in the browser or in other apps, additional permissions, additional resources and battery usage, and probably more troubles that I have not thought about).
回答1:
Some things you could try.
1.
Make sure it is html 5 by adding <!DOCTYPE html>
to your index.html
2. Instead of loading the file directly from the system using file://... you can use the WebViewAssetLoader this will return the mimetype https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader
3. If that does not work inside the webViewClient you could intercept the request and add the text/javascript mimetype manually:
val webViewAssetLoader = WebViewAssetLoader.Builder()
.addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(context!!))
.build()
val webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse? {
val interceptedWebRequest = webViewAssetLoader.shouldInterceptRequest(request.url)
interceptedWebRequest?.let {
if (request.url.toString().endsWith("js", true)) {
it.mimeType = "text/javascript"
}
}
return interceptedWebRequest
}
}
回答2:
I am just restating the previous answer in an easier to read way (and with Java instead of Kotlin)
Not sure it matters, but it's best practice to use html 5 by adding
<!DOCTYPE html>
import androidx.webkit.WebViewAssetLoader;
as described in this other questionforce the correct MIME type with something like
mWebView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
WebResourceResponse intercepted = assetLoader.shouldInterceptRequest(request.getUrl());
if (request.getUrl().toString().endsWith("js")) {
if (intercepted != null) {
intercepted.setMimeType("text/javascript");
}
}
return intercepted;
}
});
- load the HTML with something like
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.loadUrl("https://appassets.androidplatform.net/assets/AndroidWebView.html");
Note the HTTPS (not file) protocol, and the absence of AllowFileAccessFromFileURLs
来源:https://stackoverflow.com/questions/60588170/relaxing-mime-type-checking-in-androids-webview-or-forcing-module-type-for-reg