简体   繁体   English

Android webview,在assets文件夹中加载javascript文件

[英]Android webview, loading javascript file in assets folder

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.com) and then apply some javascript stored in assets/jstest.js , which contains the following: 我希望我的webview加载一些URL(例如www.google.com),然后应用存储在assets/jstest.js一些javascript,其中包含以下内容:

function test(){
document.bgColor="#00FF00"; //turns to green the background color
}

And here's where I try to load the JS: 这是我尝试加载JS的地方:

@Override  
public void onPageFinished(WebView view, String url){
    view.loadUrl("javascript:(function() { "
                + " document.bgColor='#FF0000';" //turns to red the background color
                + " var script=document.createElement('script'); "
                + " script.setAttribute('type','text/javascript'); "
                + " script.setAttribute('src', 'file:///android_asset/jstest.js'); "
                + " script.onload = function(){ "
                + "     test(); "
                + " }; "
                + " document.getElementsByTagName('head')[0].appendChild(script); "
                + "})()"); 
} 

I know the javascript here works because the background color actually turns to red, but for some reason it won't load jstest.js . 我知道这里的javascript有效,因为背景颜色实际上变为红色,但由于某种原因它不会加载jstest.js I think the problem might be in file path (I'm certain every other line of the javascript code is correct), but it looks correct to me. 我认为问题可能在文件路径中(我确定javascript代码的每一行都是正确的),但它看起来对我来说是正确的。 And the file is in the right folder. 该文件位于正确的文件夹中。

What am I missing? 我错过了什么?

EDIT : 编辑

Since WebResourceResponse class is available only with API Level 11, here's what I've figured out in the end. 由于WebResourceResponse类仅适用于API Level 11,所以这是我最终想到的。

public void onPageFinished(WebView view, String url){
        String jscontent = "";
        try{
            InputStream is = am.open("jstest.js"); //am = Activity.getAssets()
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);

            String line;
            while (( line = br.readLine()) != null) {
                jscontent += line;
            }
            is.close(); 
        }
        catch(Exception e){}
        view.loadUrl("javascript:(" + jscontent + ")()"); 
    } 

with the jstest.js simply containing: jstest.js只包含:

function() {
    document.bgColor="#00FF00";
}

I tried the same thing, loading a bookmarklet (the javascript code in your loadUrl() call) into a third-party page. 我尝试了同样的事情,将bookmarklet(你的loadUrl()调用中的javascript代码)加载到第三方页面。 My bookmarklet also depends on other assets (javascript and css files) which would not load with a file:///android_asset URL. 我的bookmarklet还依赖于其他资源(javascript和css文件),这些资源不会加载文件:/// android_asset URL。

That's because the security context of the page is still that of, eg, http://www.google.com , and that's not allowed access to file: URLs. 这是因为页面的安全上下文仍然是例如http://www.google.com的安全上下文,并且不允许访问文件:URL。 You should be able to see the errors if you supply/override a WebChromeClient.onConsoleMessage(). 如果提供/覆盖WebChromeClient.onConsoleMessage(),您应该能够看到错误。

I ended up with a kludge where I changed the bookmarklet's asset references to a bogus URL scheme, like: 我最终得到了一个kludge,我将bookmarklet的资产引用更改为伪造的URL方案,如:

asset:foo/bar/baz.js

and added a WebViewClient.shouldInterceptRequest() override which looks for those and loads them from assets using AssetManager.open(). 并添加了一个WebViewClient.shouldInterceptRequest()覆盖,它会查找这些内容并使用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. 我不喜欢这个问题的一件事是,资产:方案对我的视图加载的任何页面上的任何第三方HTML / Javascript开放,使他们可以访问我的应用程序的资产。

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. 我没有尝试的另一种方法是使用数据:URL来将子资产嵌入书签中,但这可能会变得难以处理。

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. 我更喜欢它,如果有操纵只是 JS书签我加载在使用loadURL()的安全上下文的方式,但我找不到这样的事情。

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;
        }
    }

I think the iceam cream webview client of cordova does the very thing you want to do. 我认为cordova的iceam cream webview客户端可以做你想做的事情。

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 看看cordova的android github: https//github.com/apache/incubator-cordova-android/blob/master/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java

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 我使用Content://协议并设置contentprovider来处理将文件描述符返回给系统

Here is my fileContentProvider: 这是我的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的javascript o:

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): 这是myjavascriptfile.js(作为示例):

   function changeBackground(color) {
       document.body.style.backgroundColor = color;
   }

With the following two conditions given: 给出以下两个条件:

  • minSdkVersion 21 minSdkVersion 21
  • targetSdkVersion 28 targetSdkVersion 28

I am able to successfully load any local asset (js, png, css) via the following Java code 我可以通过以下Java代码成功加载任何本地资产(js,png,css)

@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 在我可以使用的HTML代码中

<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) 在Java中,以下也适用(您还需要向资产添加favicon.ico

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. 使用https://作为方案允许我从通过HTTPS提供的页面加载本地资产,而不会因混合内容而导致安全问题。

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);    

Maybe you could have assets as 'html/javascript templates'. 也许你可以把资产作为'html / javascript模板'。 You could combine different of these text sources and string logic to compose your desired html to be loaded into the WebViewer. 您可以将这些文本源和字符串逻辑中的不同组合在一起,以组成要加载到WebViewer中的所需html。 Then, you use .loadData instead of .loadUrl 然后,使用.loadData而不是.loadUrl

I'm using it on my own and it seems to work pretty well. 我自己使用它似乎工作得很好。

Hope it helps! 希望能帮助到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将 HTML 文件中的外部 javascript 文件从 android 资产文件夹加载到 WebView - Load external javascript file inside HTML file from android assets folder into WebView Android WebView:使用脚本标签从我的资产文件夹加载外部javascript文件 - Android WebView: Using Script Tags to Load an external javascript file from my assets folder Android WebView:从资产加载javascript时使用相对路径 - Android WebView: Using relative paths when loading javascript from assets 使用javascript文件访问android资产文件夹 - Access android assets folder using javascript file 来自资产的 Android WebView JavaScript - Android WebView JavaScript from assets 从Internet访问时JavaScript工作正常,但从WebView Android上的文件夹资产访问页面时无响应 - JavaScript Working When Access From Internet But Not Responding When Access Page From Folder Assets On WebView Android Android WebView加载带有本地javascript文件的远程html文件 - android webview loading remote html file with local javascript file 加载html后,将资源中的js文件加载到webview中 - Loading a js file from assets into webview after loading html Webview无法加载html或javascript Android - Webview not loading html or javascript Android 从Android WebView中的Assets文件夹加载CSS和JS文件 - Load CSS and JS files from assets folder in android webview
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM