web-dev-qa-db-de.com

Android-Webview, Laden der Javascript-Datei im Ordner Assets

Ich habe gesehen, dass diese Frage schon oft gestellt wurde, aber ich kann meinen Code immer noch nicht zum Laufen bringen.

Ich möchte, dass mein webview eine URL lädt (z. B. www.google.com) und dann ein in assets/jstest.js gespeichertes Javascript anwendet, das Folgendes enthält:

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

Und hier versuche ich, die JS zu laden:

@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); "
                + "})()"); 
} 

Ich weiß, dass das Javascript hier funktioniert, weil die Hintergrundfarbe tatsächlich rot wird, aber aus irgendeinem Grund wird jstest.js nicht geladen. Ich denke, das Problem liegt möglicherweise im Dateipfad (ich bin sicher, dass jede andere Zeile des Javascript-Codes korrekt ist), aber es sieht für mich richtig aus. Und die Datei befindet sich im richtigen Ordner.

Was vermisse ich?

EDIT:

Da die WebResourceResponse-Klasse nur mit API Level 11 verfügbar ist, habe ich Folgendes herausgefunden.

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 + ")()"); 
    } 

mit dem jstest.js einfach enthalten:

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

Ich habe das gleiche versucht, indem ich ein Bookmarklet (den Javascript-Code in Ihrem loadUrl () - Aufruf) in eine Drittanbieterseite geladen habe. Mein Bookmarklet hängt auch von anderen Assets (Javascript- und CSS-Dateien) ab, die nicht mit einer file: /// Android_asset-URL geladen werden könnten.

Dies liegt daran, dass der Sicherheitskontext der Seite immer noch der von http://www.google.com ist und der Zugriff auf file: URLs nicht zulässig ist. Sie sollten die Fehler sehen können, wenn Sie einen WebChromeClient.onConsoleMessage () angeben/überschreiben.

Am Ende erhielt ich einen Kludge, bei dem ich die Asset-Referenzen des Bookmarklets in ein falsches URL-Schema umwandelte, beispielsweise:

asset:foo/bar/baz.js

und fügte eine Überschreibung WebViewClient.shouldInterceptRequest () hinzu, die nach diesen sucht und sie mit AssetManager.open () aus Assets lädt.

Was mir an diesem Kludge nicht gefällt, ist, dass das Asset: Schema für HTML/Javascript von Drittanbietern auf jeder Seite geöffnet ist, auf die meine Ansicht geladen wird, wodurch sie Zugriff auf die Assets meiner App haben.

Eine Alternative, die ich nicht ausprobiert habe, wäre das Einbetten der Sub-Assets mithilfe von data: URLs in das Bookmarklet. Dies kann jedoch unhandlich werden.

Ich würde es sehr vorziehen, wenn es eine Möglichkeit gibt, den Sicherheitskontext von just des JS-Bookmarklets zu ändern, das ich in loadUrl () lade, aber ich kann nichts dergleichen finden.

Hier ist ein Ausschnitt:

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

So habe ich es gemacht. Ich habe das Content: // -Protokoll verwendet und einen Contentprovider für die Rückgabe eines Dateideskriptors an das System eingerichtet

Hier ist mein 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");
    }
}

im Manifest habe ich den Anbieter definiert:

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

Hier ist das Javascript, das in die Webansicht eingefügt wird:

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); "
           + "})();");

und hier ist die myjavascriptfile.js (als Beispiel):

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

Ich glaube, dass der iceam cream Webview-Client von Cordova genau das tut, was Sie tun möchten. 

Es wäre schön, wenn dies irgendwo dokumentiert wäre, aber soweit ich sehen kann, ist es nicht so.

Sehen Sie sich Cordovas Android-Github an: https://github.com/Apache/incubator-cordova-Android/blob/master/framework/src/org/Apache/cordova/IceCreamCordovaWebViewClient.Java

1
nurieta

Mit den folgenden zwei Bedingungen gegeben:

  • minSdkVersion 21
  • targetSdkVersion 28

Ich kann jedes lokale Asset (js, png, css) über den folgenden Java Code erfolgreich laden

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

Und im HTML-Code kann ich verwenden

<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 funktioniert dann auch folgendes (man müsste noch ein favicon.ico zu den Vermögenswerten)

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

Mit https://, da das Schema es mir ermöglicht, lokale Assets von einer Seite zu laden, die über HTTPS ohne Sicherheitsprobleme aufgrund von gemischtem Inhalt bereitgestellt wird.

Keine davon müssen eingestellt werden:

webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
webSettings.setDomStorageEnabled(true);
webSettings.setAllowContentAccess(true);
webSettings.setAllowFileAccess(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);    
0
Daniel F

Vielleicht könnten Sie Assets als "HTML/Javascript-Vorlagen" haben. Sie können verschiedene dieser Textquellen und String-Logik kombinieren, um die gewünschte HTML-Datei zum Laden in den WebViewer zusammenzustellen. Dann verwenden Sie .loadData anstelle von .loadUrl

Ich benutze es alleine und es scheint ziemlich gut zu funktionieren.

Ich hoffe es hilft!

0
Dani bISHOP