J'ai vu cette question posée à maintes reprises, mais je n'arrive toujours pas à faire fonctionner mon code.
Je souhaite que mon webview charge une URL (par exemple, www.google.com), puis applique du code JavaScript stocké dans assets/jstest.js
, qui contient les éléments suivants:
function test(){
document.bgColor="#00FF00"; //turns to green the background color
}
Et voici où j'essaye de charger le 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); "
+ "})()");
}
Je sais que le javascript fonctionne ici parce que la couleur de fond devient rouge, mais pour une raison quelconque, il ne chargera pas jstest.js
. Je pense que le problème pourrait être dans le chemin du fichier (je suis sûr que chaque ligne du code javascript est correcte), mais cela me semble correct. Et le fichier est dans le bon dossier.
Qu'est-ce que je rate?
MODIFIER:
Puisque la classe WebResourceResponse
est disponible uniquement avec l'API de niveau 11, voici ce que j'ai découvert à la fin.
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 + ")()");
}
avec le jstest.js
contenant simplement:
function() {
document.bgColor="#00FF00";
}
J'ai essayé la même chose, en chargeant un bookmarklet (le code javascript de votre appel loadUrl ()) dans une page tierce. Mon bookmarklet dépend également d'autres ressources (fichiers javascript et css) qui ne seraient pas chargées avec une URL file: /// Android_asset.
En effet, le contexte de sécurité de la page reste celui, par exemple, http://www.google.com , et l'accès au fichier: URL n'est pas autorisé. Vous devriez pouvoir voir les erreurs si vous fournissez/substituez un WebChromeClient.onConsoleMessage ().
Je me suis retrouvé avec un kludge où j'ai changé les références d'actif du bookmarklet à un schéma d'URL bidon, tel que:
asset:foo/bar/baz.js
et ajouté un paramètre WebViewClient.shouldInterceptRequest () qui recherche ces éléments et les charge à partir des actifs à l'aide de AssetManager.open ().
Une chose que je n'aime pas dans ce kludge, c'est que l'actif: le schéma est ouvert à tout code HTML/Javascript tiers sur n'importe quelle page que ma vue charge, ce qui leur donne accès aux actifs de mon application.
Une solution, que je n’ai pas essayée, serait d’incorporer les sous-actifs dans le bookmarklet à l’aide de data: URLs, mais cela peut devenir difficile à manier.
Je le préfèrerais de beaucoup s'il existe un moyen de manipuler le contexte de sécurité de just le bookmarklet JS que je charge dans loadUrl (), mais je ne trouve rien de tel.
Voici un extrait:
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;
}
}
Voici comment j'ai fini par le faire. J'ai utilisé le protocole Content: // et mis en place un fournisseur de contenu pour gérer le renvoi d'un descripteur de fichier au système
Voici mon 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");
}
}
dans le manifeste, j'ai défini le fournisseur:
<provider Android:name="com.example.mypackage.FileContentProvider"
Android:authorities="com.example.fileprovider"
/>
Voici le javascript o injecter dans la 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); "
+ "})();");
et voici le myjavascriptfile.js (à titre d'exemple):
function changeBackground(color) {
document.body.style.backgroundColor = color;
}
Je pense que le client Webview iceam cream de Cordova fait exactement ce que vous voulez.
Ce serait bien si cela était documenté quelque part mais, autant que je sache, ce n'est pas le cas.
Jetez un coup d'œil au github Android de Cordova: https://github.com/Apache/incubator-cordova-Android/blob/master/framework/src/org/Apache/cordova/IceCreamCordovaWebViewClient.Java
Vous pourriez peut-être avoir des ressources en tant que "modèles HTML/JavaScript". Vous pouvez combiner différentes sources de texte et une logique de chaîne pour composer le code HTML que vous souhaitez charger dans WebViewer. Ensuite, vous utilisez .loadData au lieu de .loadUrl
Je l'utilise seul et cela semble fonctionner assez bien.
J'espère que ça aide!