Android webview, loading javascript file in assets folder

前端 未结 5 1876
我在风中等你
我在风中等你 2020-11-29 01:09

I\'ve seen this question has been asked a lot of times, but still can\'t manage to get my code working.

I want my webview to load some URL (say www.google.c

相关标签:
5条回答
  • 2020-11-29 01:42

    I tried the same thing, loading a bookmarklet (the javascript code in your loadUrl() call) into a third-party page. My bookmarklet also depends on other assets (javascript and css files) which would not load with a file:///android_asset URL.

    That's because the security context of the page is still that of, e.g., http://www.google.com, and that's not allowed access to file: URLs. You should be able to see the errors if you supply/override a WebChromeClient.onConsoleMessage().

    I ended up with a kludge where I changed the bookmarklet's asset references to a bogus URL scheme, like:

    asset:foo/bar/baz.js
    

    and added a WebViewClient.shouldInterceptRequest() override which looks for those and loads them from assets using AssetManager.open().

    One thing I don't like about this kludge is that the asset: scheme is open to any third-party HTML/Javascript on any page my view loads, giving them access to my app's assets.

    One alternative, which I didn't try, would be to embed the sub-assets in the bookmarklet using data: URLs, but that can get unwieldy.

    I'd much prefer it if there was a way to manipulate the security context of just the JS bookmarklet I'm loading in loadUrl(), but I can't find anything like that.

    Here's a snippet:

    import android.webkit.WebResourceResponse;
    ...
        private final class FooViewClient extends WebViewClient
        {
        private final String bookmarklet;
        private final String scheme;
    
        private FooViewClient(String bookmarklet, String scheme)
            {
            this.bookmarklet = bookmarklet;
            this.scheme = scheme;
            }
    
        @Override
        public void onPageFinished(WebView view, String url)
            {
            view.loadUrl(bookmarklet);
            }
    
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url)
            {
            if (url.startsWith(scheme))
                try
                    {
                    return new WebResourceResponse(url.endsWith("js") ? "text/javascript" : "text/css", "utf-8",
                            Foo.this.getAssets().open(url.substring(scheme.length())));
                    }
                catch (IOException e)
                    {
                    Log.e(getClass().getSimpleName(), e.getMessage(), e);
                    }
    
            return null;
            }
        }
    
    0 讨论(0)
  • 2020-11-29 01:45

    I think the iceam cream webview client of cordova does the very thing you want to do.

    It would be nice if this was documented somewhere but, as far as I can see, it is not.

    Take a look at cordova's android github: https://github.com/apache/incubator-cordova-android/blob/master/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java

    0 讨论(0)
  • 2020-11-29 01:56

    With the following two conditions given:

    • minSdkVersion 21
    • targetSdkVersion 28

    I am able to successfully load any local asset (js, png, css) via the following Java code

    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        Uri uri = request.getUrl();
        if (uri.getHost().equals("assets")) {
            try {
                return new WebResourceResponse(
                    URLConnection.guessContentTypeFromName(uri.getPath()),
                    "utf-8",
                    MainActivity.this.getAssets().open(uri.toString().substring(15)));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    

    And in the HTML code I can use

    <link rel="stylesheet" href="https://assets/material.min.css">
    <script src="https://assets/material.min.js"></script>
    <script src="https://assets/moment-with-locales.min.js"></script> 
    <img src="https://assets/stackoverflow.png">
    

    In Java the following then also works (you'd also need to add a favicon.ico to the assets)

    webView.loadUrl("https://assets/example.html");
    

    Using https:// as the scheme allows me to load local assets from a page served via HTTPS without security issues due to mixed-content.

    None of these require to be set:

    webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
    webSettings.setDomStorageEnabled(true);
    webSettings.setAllowContentAccess(true);
    webSettings.setAllowFileAccess(true);
    webSettings.setAllowFileAccessFromFileURLs(true);
    webSettings.setAllowUniversalAccessFromFileURLs(true);    
    
    0 讨论(0)
  • 2020-11-29 01:58

    Here is how i ended up doing it. I used the Content:// protocol and set up a contentprovider to handle returning a file descriptor to the system

    Here is my fileContentProvider:

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.commons.io.IOUtils;
    
    
    import android.content.ContentProvider;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.ParcelFileDescriptor;
    import android.util.Log;
    
    public class FileContentProvider extends ContentProvider {
        @Override
        public ParcelFileDescriptor openFile(Uri uri, String mode) {
    
            Log.d("FileContentProvider","fetching: " + uri);
    
            ParcelFileDescriptor parcel = null;
    
            String fileNameRequested = uri.getLastPathSegment();
            String[] name=fileNameRequested.split("\\.");
            String prefix=name[0];
            String suffix=name[1];
           // String path = getContext().getFilesDir().getAbsolutePath() + "/" + uri.getPath();
            //String path=file:///android_asset/"+Consts.FILE_JAVASCRIPT+"
    
    /*check if this is a javascript file*/
    
            if(suffix.equalsIgnoreCase("js")){
            InputStream is = null;
            try {
                is = getContext().getAssets().open("www/"+Consts.FILE_JAVASCRIPT);
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
    
    
            File file = stream2file(is,prefix,suffix);
            try {
                parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
            } catch (FileNotFoundException e) {
                Log.e("FileContentProvider", "uri " + uri.toString(), e);
            }
            }
            return parcel;
        }
    
        /*converts an inputstream to a temp file*/
    
        public File stream2file (InputStream in,String prefix,String suffix) {
            File tempFile = null;
            try {
                tempFile = File.createTempFile(prefix, suffix);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            tempFile.deleteOnExit();
    
                FileOutputStream out = null;
                try {
                    out = new FileOutputStream(tempFile);
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } 
    
                try {
                    IOUtils.copy(in, out);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
    
            return tempFile;
        }
    
    
        @Override
        public boolean onCreate() {
            return true;
        }
    
        @Override
        public int delete(Uri uri, String s, String[] as) {
            throw new UnsupportedOperationException("Not supported by this provider");
        }
    
        @Override
        public String getType(Uri uri) {
            throw new UnsupportedOperationException("Not supported by this provider");
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues contentvalues) {
            throw new UnsupportedOperationException("Not supported by this provider");
        }
    
        @Override
        public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
            throw new UnsupportedOperationException("Not supported by this provider");
        }
    
        @Override
        public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
            throw new UnsupportedOperationException("Not supported by this provider");
        }
    }
    

    in the manifest i defined the provider:

    <provider android:name="com.example.mypackage.FileContentProvider"
              android:authorities="com.example.fileprovider"
            />
    

    Here is the javascript o inject into the webview:

    webView.loadUrl("javascript:(function() { "
    
               + "var script=document.createElement('script'); "
               + " script.setAttribute('type','text/javascript'); "
               + " script.setAttribute('src', 'content://com.example.fileprovider/myjavascriptfile.js'); "
          /*      + " script.onload = function(){ "
               + "     test(); "
               + " }; "
          */     + "document.body.appendChild(script); "
               + "})();");
    

    and here is the myjavascriptfile.js (as an example):

       function changeBackground(color) {
           document.body.style.backgroundColor = color;
       }
    
    0 讨论(0)
  • 2020-11-29 01:59

    Maybe you could have assets as 'html/javascript templates'. You could combine different of these text sources and string logic to compose your desired html to be loaded into the WebViewer. Then, you use .loadData instead of .loadUrl

    I'm using it on my own and it seems to work pretty well.

    Hope it helps!

    0 讨论(0)
提交回复
热议问题