先加载网页后加载js 请问怎么使用ioswebview加载网页

WKWebView的新特性与使用 | // TODO:
【iOS】WKWebView的新特性与使用 | // TODO:
& 2016 // TODO:您所在的位置: &
在WebView中如何让JS与Java安全地互相调用
在WebView中如何让JS与Java安全地互相调用
书呆子精神院
在现在安卓应用原生开发中,为了追求开发的效率以及移植的便利性,使用WebView作为业务内容展示与交互的主要载体是个不错的折中方案。那么在 这种Hybrid(混合式) App中,难免就会遇到页面JS需要与Java相互调用,调用Java方法去做那部分网页JS不能完成的功能。
在现在安卓应用原生开发中,为了追求开发的效率以及移植的便利性,使用WebView作为业务内容展示与交互的主要载体是个不错的折中方案。那么在 这种Hybrid(混合式) App中,难免就会遇到页面JS需要与Java相互调用,调用Java方法去做那部分网页JS不能完成的功能。
网上的方法可以告诉我们这个时候我们可以使用addjavascriptInterface来注入原生接口到 JS中,但是在安卓4.2以下的系统中,这种方案却我们的应用带来了很大的安全风险。攻击者如果在页面执行一些非法的JS(诱导用户打开一些钓鱼网站以进 入风险页面),极有可能反弹拿到用户手机的shell权限。接下来攻击者就可以在后台默默安装木马,完全洞穿用户的手机。详细的攻击过程可以见乌云平台的 这份报告:。
安卓4.2及以上版本(API &=
17),在注入类中为可调用的方法添加@JavascriptInterface注解,无注解的方法不能被调用,这种方式可以防范注入漏洞。那么有没一种 安全的方式,可以完全兼顾安卓4.2以下版本呢?答案就是使用prompt,即WebChromeClient 输入框弹出模式。
这篇文章给出的解决方案, 但它JS下的方法有点笨拙, 动态生成JS文件过程也并没有清晰,且加载JS文件的时机也没有准确把握。那么如何改造才能便利地在JS代码中调用Java方法,并且安全可靠呢?
下面提到的源码及项目可以在这找到。
一、动态地生成将注入的JS代码
JsCallJava在构造时,将要注入类的public且static方法拿出来,逐个生成方法的签名,依据方法签名先将方法缓存起来,同时结合方法名称与静态的HostApp-JS代码动态生成一段将要注入到webview中的字符串。
public&JsCallJava&(String&injectedName,&Class&injectedCls)&{&&&&&try&{&&&&&&&&&mMethodsMap&=&new&HashMap&String,&Method&();&&&&&&&&&&&&&&&&&&Method[]&methods&=&injectedCls.getDeclaredMethods();&&&&&&&&&StringBuilder&sb&=&new&StringBuilder(&javascript:(function(b){console.log(\&HostApp&initialization&begin\&);var&a={queue:[],callback:function(){var&d=Array.prototype.slice.call(arguments,0);var&c=d.shift();var&e=d.shift();this.queue[c].apply(this,d);if(!e){delete&this.queue[c]}}};&);&&&&&&&&&&for&(Method&method&:&methods)&{&&&&&&&&&&&&&String&&&&&&&&&&&&&&if&(method.getModifiers()&!=&(Modifier.PUBLIC&|&Modifier.STATIC)&||&(sign&=&genJavaMethodSign(method))&==&null)&{&&&&&&&&&&&&&&&&&continue;&&&&&&&&&&&&&}&&&&&&&&&&&&&mMethodsMap.put(sign,&method);&&&&&&&&&&&&&sb.append(String.format(&a.%s=&,&method.getName()));&&&&&&&&&}&&&&&&&&&&sb.append(&function(){var&f=Array.prototype.slice.call(arguments,0);if(f.length&1){throw\&HostApp&call&error,&message:miss&method&name\&}var&e=[];for(var&h=1;h&f.h++){var&c=f[h];var&j=typeof&c;e[e.length]=j;if(j==\&function\&){var&d=a.queue.a.queue[d]=c;f[h]=d}}var&g=JSON.parse(prompt(JSON.stringify({method:f.shift(),types:e,args:f})));if(g.code!=200){throw\&HostApp&call&error,&code:\&+g.code+\&,&message:\&+g.result}return&g.result};Object.getOwnPropertyNames(a).forEach(function(d){var&c=a[d];if(typeof&c===\&function\&&&d!==\&callback\&){a[d]=function(){return&c.apply(a,[d].concat(Array.prototype.slice.call(arguments,0)))}}});b.&&+&injectedName&+&&=a;console.log(\&HostApp&initialization&end\&)})(window);&);&&&&&&&&&mPreloadInterfaceJS&=&sb.toString();&&&&&}&catch(Exception&e){&&&&&&&&&Log.e(TAG,&&init&js&error:&&+&e.getMessage());&&&&&}&}&&private&String&genJavaMethodSign&(Method&method)&{&&&&&String&sign&=&method.getName();&&&&&Class[]&argsTypes&=&method.getParameterTypes();&&&&&int&len&=&argsTypes.&&&&&if&(len&&&1&||&argsTypes[0]&!=&WebView.class)&{&&&&&&&&&Log.w(TAG,&&method(&&+&sign&+&&)&must&use&webview&to&be&first&parameter,&will&be&pass&);&&&&&&&&&return&null;&&&&&}&&&&&for&(int&k&=&1;&k&&&&k++)&{&&&&&&&&&Class&cls&=&argsTypes[k];&&&&&&&&&if&(cls&==&String.class)&{&&&&&&&&&&&&&sign&+=&&_S&;&&&&&&&&&}&else&if&(cls&==&int.class&||&&&&&&&&&&&&&cls&==&long.class&||&&&&&&&&&&&&&cls&==&float.class&||&&&&&&&&&&&&&cls&==&double.class)&{&&&&&&&&&&&&&sign&+=&&_N&;&&&&&&&&&}&else&if&(cls&==&boolean.class)&{&&&&&&&&&&&&&sign&+=&&_B&;&&&&&&&&&}&else&if&(cls&==&JSONObject.class)&{&&&&&&&&&&&&&sign&+=&&_O&;&&&&&&&&&}&else&if&(cls&==&JsCallback.class)&{&&&&&&&&&&&&&sign&+=&&_F&;&&&&&&&&&}&else&{&&&&&&&&&&&&&sign&+=&&_P&;&&&&&&&&&}&&&&&}&&&&&return&&}&
从上面可以看出,类的各个方法名称被拼接到前后两段静态压缩的JS代码当中,那么这样生成的完整清晰的HostApp-JS片段是怎样的呢?
我们假设HostJsScope类中目前只定义了toast、alert、getIMSI这三个公开静态方法,那么完整的片段就是下面这样:
(function(global){&&&&&console.log(&HostApp&initialization&begin&);&&&&&var&hostApp&=&{&&&&&&&&&queue:&[],&&&&&&&&&callback:&function&()&{&&&&&&&&&&&&&var&args&=&Array.prototype.slice.call(arguments,&0);&&&&&&&&&&&&&var&index&=&args.shift();&&&&&&&&&&&&&var&isPermanent&=&args.shift();&&&&&&&&&&&&&this.queue[index].apply(this,&args);&&&&&&&&&&&&&if&(!isPermanent)&{&&&&&&&&&&&&&&&&&delete&this.queue[index];&&&&&&&&&&&&&}&&&&&&&&&}&&&&&};&&&&&hostApp.toast&=&hostApp.alert&=&hostApp.getIMSI&=&function&()&{&&&&&&&&&var&args&=&Array.prototype.slice.call(arguments,&0);&&&&&&&&&if&(args.length&&&1)&{&&&&&&&&&&&&&throw&&HostApp&call&error,&message:miss&method&name&;&&&&&&&&&}&&&&&&&&&var&aTypes&=&[];&&&&&&&&&for&(var&i&=&1;i&&&args.i++)&{&&&&&&&&&&&&&var&arg&=&args[i];&&&&&&&&&&&&&var&type&=&typeof&&&&&&&&&&&&&&aTypes[aTypes.length]&=&&&&&&&&&&&&&&if&(type&==&&function&)&{&&&&&&&&&&&&&&&&&var&index&=&hostApp.queue.&&&&&&&&&&&&&&&&&hostApp.queue[index]&=&&&&&&&&&&&&&&&&&&args[i]&=&&&&&&&&&&&&&&}&&&&&&&&&}&&&&&&&&&var&res&=&JSON.parse(prompt(JSON.stringify({&&&&&&&&&&&&&method:&args.shift(),&&&&&&&&&&&&&types:&aTypes,&&&&&&&&&&&&&args:&args&&&&&&&&&})));&&&&&&&&&&if&(res.code&!=&200)&{&&&&&&&&&&&&&throw&&HostApp&call&error,&code:&&+&res.code&+&&,&message:&&+&res.&&&&&&&&&}&&&&&&&&&return&res.&&&&&};&&&&&&&&&&&&&&&&&&&&&&&&&&&&Object.getOwnPropertyNames(hostApp).forEach(function&(property)&{&&&&&&&&&var&original&=&hostApp[property];&&&&&&&&&&if&(typeof&original&===&'function'&&property!==&callback&)&{&&&&&&&&&&&&&hostApp[property]&=&function&()&{&&&&&&&&&&&&&&&&&return&original.apply(hostApp,&&[property].concat(Array.prototype.slice.call(arguments,&0)));&&&&&&&&&&&&&};&&&&&&&&&}&&&&&});&&&&&global.HostApp&=&hostA&&&&&console.log(&HostApp&initialization&end&);&})(window);&
其实在JsCallJava初始化时我们拼接的只是上面第15行 hostApp.toast = hostApp.alert = hostApp.getIMSI = function ()
这段。目的是将所有JS层调用函数嫁接到一个匿名函数1中,而后利用拦截技术,遍历hostApp下所有的函数,拿出对应的函数名,然后将hostApp 下所有的函数调用嫁接到另一个匿名函数2,这样做的目的是hostApp下函数调用时首先执行匿名函数2,匿名函数2将对应的函数名作为第一个参数然后再 调用匿名函数1,这样匿名函数1中就能区分执行时调用来源。实现了JS层调用入口统一,返回出口统一的结构体系。
二、HostApp JS片段注入时机
步骤一说明了HostApp-JS片段的拼接方法,同时JS片段拼接是在JsCallJava初始化完成的,而JsCallJava初始化是在实例化InjectedChromeClient对象时发起的。
public&InjectedChromeClient&(String&injectedName,&Class&injectedCls)&{&&&&&mJsCallJava&=&new&JsCallJava(injectedName,&injectedCls);&}&
从步骤一的代码,我们知道JsCallJava拼接出来的JS代码暂时被存到mPreloadInterfaceJS字段中。那么我们何时把这段代码串注入到Webview的页面空间内呢?答案是页面加载进度变化的过程中。
@Override&public&void&onProgressChanged&(WebView&view,&int&newProgress)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if&(newProgress&&=&25)&{&&&&&&&&&mIsInjectedJS&=&false;&&&&&}&else&if&(!mIsInjectedJS)&{&&&&&&&&&view.loadUrl(mJsCallJava.getPreloadInterfaceJS());&&&&&&&&&mIsInjectedJS&=&true;&&&&&&&&&Log.d(TAG,&&&inject&js&interface&completely&on&progress&&&+&newProgress);&&&&&&}&&&&&super.onProgressChanged(view,&newProgress);&}&
从上面我们可以看出,注入的时机是准确把握在进度大于25%时。如果在OnPageFinished注入,页面document.ready的初始回调会等待时间过长,详细的原因我们会在后面讲到。
三、页面调用Java方法执行的过程
OK,上面两步解决了动态生成与成功注入的两大问题,接下来就要处理JS具体的调用过程。上面,我们知道页面调用Java方法时,匿名js函数在拼 接好参数后prompt json数据。prompt消息被Java层的WebChromeClient.onJsPrompt拦截到。
@Override&public&boolean&onJsPrompt(WebView&view,&String&url,&String&message,&String&defaultValue,&JsPromptResult&result)&{&&&&&result.confirm(mJsCallJava.call(view,&message));&&&&&return&true;&}&
而JsCallJava.call的具体实现如下。
public&String&call(WebView&webView,&String&jsonStr)&{&&&&&if&(!TextUtils.isEmpty(jsonStr))&{&&&&&&&&&try&{&&&&&&&&&&&&&JSONObject&callJson&=&new&JSONObject(jsonStr);&&&&&&&&&&&&&String&methodName&=&callJson.getString(&method&);&&&&&&&&&&&&&JSONArray&argsTypes&=&callJson.getJSONArray(&types&);&&&&&&&&&&&&&JSONArray&argsVals&=&callJson.getJSONArray(&args&);&&&&&&&&&&&&&String&sign&=&methodN&&&&&&&&&&&&&int&len&=&argsTypes.length();&&&&&&&&&&&&&Object[]&values&=&new&Object[len&+&1];&&&&&&&&&&&&&int&numIndex&=&0;&&&&&&&&&&&&&String&currT&&&&&&&&&&&&&&values[0]&=&webV&&&&&&&&&&&&&&for&(int&k&=&0;&k&&&&k++)&{&&&&&&&&&&&&&&&&&currType&=&argsTypes.optString(k);&&&&&&&&&&&&&&&&&if&(&string&.equals(currType))&{&&&&&&&&&&&&&&&&&&&&&sign&+=&&_S&;&&&&&&&&&&&&&&&&&&&&&values[k&+&1]&=&argsVals.isNull(k)&?&null&:&argsVals.getString(k);&&&&&&&&&&&&&&&&&}&else&if&(&number&.equals(currType))&{&&&&&&&&&&&&&&&&&&&&&sign&+=&&_N&;&&&&&&&&&&&&&&&&&&&&&numIndex&=&numIndex&*&10&+&k&+&1;&&&&&&&&&&&&&&&&&}&else&if&(&boolean&.equals(currType))&{&&&&&&&&&&&&&&&&&&&&&sign&+=&&_B&;&&&&&&&&&&&&&&&&&&&&&values[k&+&1]&=&argsVals.getBoolean(k);&&&&&&&&&&&&&&&&&}&else&if&(&object&.equals(currType))&{&&&&&&&&&&&&&&&&&&&&&sign&+=&&_O&;&&&&&&&&&&&&&&&&&&&&&values[k&+&1]&=&argsVals.isNull(k)&?&null&:&argsVals.getJSONObject(k);&&&&&&&&&&&&&&&&&}&else&if&(&function&.equals(currType))&{&&&&&&&&&&&&&&&&&&&&&sign&+=&&_F&;&&&&&&&&&&&&&&&&&&&&&values[k&+&1]&=&new&JsCallback(webView,&argsVals.getInt(k));&&&&&&&&&&&&&&&&&}&else&{&&&&&&&&&&&&&&&&&&&&&sign&+=&&_P&;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&Method&currMethod&=&mMethodsMap.get(sign);&&&&&&&&&&&&&&&&&&&&&&&&&&&if&(currMethod&==&null)&{&&&&&&&&&&&&&&&&&return&getReturn(jsonStr,&500,&&not&found&method(&&+&methodName&+&&)&with&valid&parameters&);&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&if&(numIndex&&&0)&{&&&&&&&&&&&&&&&&&Class[]&methodTypes&=&currMethod.getParameterTypes();&&&&&&&&&&&&&&&&&int&currI&&&&&&&&&&&&&&&&&Class&currC&&&&&&&&&&&&&&&&&while&(numIndex&&&0)&{&&&&&&&&&&&&&&&&&&&&&currIndex&=&numIndex&-&numIndex&/&10&*&10;&&&&&&&&&&&&&&&&&&&&&currCls&=&methodTypes[currIndex];&&&&&&&&&&&&&&&&&&&&&if&(currCls&==&int.class)&{&&&&&&&&&&&&&&&&&&&&&&&&&values[currIndex]&=&argsVals.getInt(currIndex&-&1);&&&&&&&&&&&&&&&&&&&&&}&else&if&(currCls&==&long.class)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&values[currIndex]&=&Long.parseLong(argsVals.getString(currIndex&-&1));&&&&&&&&&&&&&&&&&&&&&}&else&{&&&&&&&&&&&&&&&&&&&&&&&&&values[currIndex]&=&argsVals.getDouble(currIndex&-&1);&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&numIndex&/=&10;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&return&getReturn(jsonStr,&200,&currMethod.invoke(null,&values));&&&&&&&&&}&catch&(Exception&e)&{&&&&&&&&&&&&&&&&&&&&&&&&&&if&(e.getCause()&!=&null)&{&&&&&&&&&&&&&&&&&return&getReturn(jsonStr,&500,&&method&execute&error:&&+&e.getCause().getMessage());&&&&&&&&&&&&&}&&&&&&&&&&&&&return&getReturn(jsonStr,&500,&&method&execute&error:&&+&e.getMessage());&&&&&&&&&}&&&&&}&else&{&&&&&&&&&return&getReturn(jsonStr,&500,&&call&data&empty&);&&&&&}&}&
这是一个完整的解析匹配过程,会依据js层传入的方法名、参数类型列表再次生成方法签名,与之前初始化构造好的缓存对象中的方法匹配。匹配成功后则 判断js调用参数类型中是否有number类型,如果有依据Java层方法的定义决定是取int、long还是double类型的值。最后使用调用值列表 和方法对象反射执行,返回函数执行的结果。这里有几点需要注意:
方法反射执行时会将当前WebView的实例放到第一个参数,方便在HostJsScope静态方法依据Context拿到一些相关上下文信息;
注入类(如HostJsScope)静态方法的参数定义可使用的类型有int/long/double、String、boolean、 JSONObject、JsCallback,对应于js层传入的类型为number、string、boolean、object、function, 注意number数字过大时(如时间戳),可能需要先转为string类型(Java方法中参数也须定义为String),避免精度丢失;
Java方法的返回值可以是void 或 能转为字符串的类型(如int、long、String、double、float等)或 可序列化的自定义类型;
如果执行失败或找不到调用方法时,Java层会将异常信息传递到JS层, JS匿名函数中会throw抛出错误;
四、HostApp在页面的使用
有了上面的准备工作,现在我们在页面中就可以很方便地使用HostApp了,而不需要加载任何依赖文件。如li标签的点击:
&class=&entry&&&&&&&onclick=&HostApp.alert('HostApp.alert');&HostApp.alert&&&&&&onclick=&HostApp.toast('HostApp.toast');&HostApp.toast&&&&&&onclick=&HostApp.testLossTime(new&Date().getTime()&+&'');&HostApp.testLossTime&&&&&&&onclick=&HostApp.toast(HostApp.getIMSI());&HostApp.getIMSI&&
但同时有一种业务情景时,页面初始加载完备时就应立即触发的调用,如果我们这样写:
document.addEventListener('DOMContentLoaded',&function()&{&&&&&HostApp.toast('document&ready&now');;&},&false);&
那么HostApp的调用极有可能不成功,因为端注入HostApp-JS片段的时机可能在document.ready前也可能在其后。那么如何解决这个矛盾的问题呢?
如果document.ready的时候HostApp
JS已经注入成功,这种情况OK没有问题。当document.ready的时候HostApp
JS还未开始注入,这种情景下我们的js脚本层就需要做出变动,即轮询状态,直到端注入成功或者超时(1.5s),再发生回调。具体实现如下(下面的是以 zepto.js的$.ready()函数改造为例)。
&&&$.fn&=&{&&&&&&&&&&ready:&function(callback,&jumpHostAppInject)&{&&&&&&&&&var&originCb&=&&&&&&&&&&var&mcounter&=&0;&&&&&&&&&&&&&&&&&&callback&=&function&()&{&&&&&&&&&&&&&if(!window.HostApp&&&&mcounter++&&&150)setTimeout(callback,&10);else&originCb($);&&&&&&&&&};&&&&&&&&&&&&&&&&&&if&(jumpHostAppInject)&{&&&&&&&&&&&&&callback&=&originCb;&&&&&&&&&}&&&&&&&&&if&(readyRE.test(document.readyState))&callback($);&else&document.addEventListener('DOMContentLoaded',&function()&{&&&&&&&&&&&&&&&&&callback($)&&&&&&&&&&&&&},&false);&&&&&&&&&return&this&&&&&},&&&&&...&&&&&...&};&
这样的机制也就解释了为什么不把Java层的JS注入放在OnPageFinish了,如果那样页面轮询的次数就会上升,等待的时间就会变长,而且有可能会超时。好了,有了上面的改动,页面初始加载完备时需要立即触发HostApp的调用,如下:
&type=&text/javascript&&&&&&$(function&()&{&&&&&&&&&HostApp.alert(&HostApp&ready&now&);&&&&&});&&
更多使用说明及完整源代码见:
【编辑推荐】
【责任编辑: TEL:(010)】
关于&&&&&&的更多文章
Java 8版本最大的改进就是Lambda表达式,其目的是使Java更易于为
既然强大的Android Studio来了,有什么理由不去用呢?
讲师: 144人学习过讲师: 56人学习过讲师: 22人学习过
国际消费类电子产品展览会(International Consumer E
你心爱的那些移动游戏为什么会如此吸引你?就是那些你
本专题意在帮助想要了解Android的人能快速上手Android
20多年以来,《软件工程:实践者的研究方法》一书是最受学生和行业专业人员欢迎的软件工程指南。它在全面而系统、概括而清晰地介
Windows Phone专家
Android开发专家
51CTO旗下网站webview html页面加载本地js及img src(二)
以content://的形式有javascript跨域的问题,所以就算加载js文件成功,也不能执行,或者说还需要解决跨域的问题,太麻烦,所以还是放弃了。我们现在也没有用这种方案。但是加载图片是一点问题都没有的。
希望对同样使用webview开发的兄弟们有所帮助。大家有什么更好的关于webview的开发技术,欢迎分享。 =======
↑↑↑↑↑↑↑↑↑先看上面,可能看完上面的,你就觉得这种方案没必要了
↑↑↑↑↑↑↑↑↑=================================
继上一文章
最近又有了新的进展。
首先我们要实现web页加载手机本地的资源(以图片为例)
如content://com.andych008.demo.webview/girl_sd.gif这样的link,我们要在html上显示手机本地的girl_sd.gif这张图片,
至于girl_sd.gif这张图片的位置,依次从以下位置查找,如果找到就打开。
/sdcard/andych008/
/data/data/com.andych008.demo.webview/andych008/
apk:assets/
当然也可以指定只从某个位置打开。
下面是代码实现
public class LocalFileContentProvider extends ContentProvider {
private static final String URI_PREFIX = &content://com.andych008.demo.webview&;
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
Log.e(&path1:&, uri.getPath());
File file = new File(uri.getPath());
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
public AssetFileDescriptor openAssetFile (Uri uri, String mode) throws FileNotFoundException{
AssetManager am = getContext().getAssets();
String path = uri.getPath().substring(1);
Log.e(&path:&, path);
//sdcard里有没有
String tpath = &/sdcard/andych008/&+
File file = new File(tpath);
if (file.exists()) {
Log.e(&path2:&, tpath);
Uri turi = Uri.parse(URI_PREFIX+tpath);
return super.openAssetFile(turi, mode);
//C盘有没有
tpath = &/data/data/com.andych008.demo.webview/andych008/&+
file = new File(tpath);
if (file.exists()) {
Log.e(&path2:&, tpath);
Uri turi = Uri.parse(URI_PREFIX+tpath);
return super.openAssetFile(turi, mode);
AssetFileDescriptor afd = am.openFd(path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return super.openAssetFile(uri, mode);
public boolean onCreate() {
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException(&Not supported by this provider&);
public String getType(Uri uri) {
throw new UnsupportedOperationException(&Not supported by this provider&);
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException(&Not supported by this provider&);
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
throw new UnsupportedOperationException(&Not supported by this provider&);
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
throw new UnsupportedOperationException(&Not supported by this provider&);
&!DOCTYPE html PUBLIC &-//W3C//DTD XHTML 1.0 Transitional//EN&
&http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&&
&html xmlns=&http://www.w3.org/1999/xhtml& xml:lang=&en& lang=&en&&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&meta name=&viewport& content=&width=device-width,minimum-scale=1.0,maximum-scale=1.0& /&
&title&Insert title here&/title&
&link type=&text/css& rel=&stylesheet& href=&1.css&&
&script type=&text/javascript& charset=&utf-8& src=&1.js&&&/script&
&body&sd这是一个html页面
&button onclick=&showNote('andy')&&showNote&/button&
&img src=&girl_sd.gif& alt=&相对路径&&
&img src=&content://com.andych008.demo.webview/girl_sd.gif& alt=&sd图片&&
&img src=&content://com.andych008.demo.webview/girl_c.gif& alt=&c图片&&
&img src=&content://com.andych008.demo.webview/1/girl.gif& alt=&a图片&&
&img src=&content://com.andych008.demo.webview/sdcard/andych008/girl_sd.gif& alt=&a图片&&
要求 存在以下文件
/sdcard/andych008/girl_sd.gif
/data/data/com.andych008.demo.webview/andych008/girl_c.gif
apk:assets/1/girl.gif
/data/data下面的文件可以这样cp进去(要求手机是Root过的),或者自己通过程序写文件进去
adb push girl_c.gif &/data/data/com.andych008.demo.webview/andych008/girl_c.gif&
大侠,你都什么时候在线,我的跨域问题迟迟无解,都耗了几天了。。。
[reply]genty_bj[/reply]我们不去解决了。你如果要解决,网上面都有,总的来说两种方法1、代理。2、javascript标签。如果不明白,你可以找你们公司里的前端工程师问问。
楼主的表达能力。。。。。。。。。。。。。。。。看了半天头疼
[reply]xxbkustc[/reply]现在还疼吗?
[reply]andych008[/reply]在下愚钝,早就已经不疼了
本分类共有文章43篇,更多信息详见
& 2012 - 2014 &
&All Rights Reserved. &
/*爱悠闲图+*/
var cpro_id = "u1888441";
/*爱悠闲底部960*75*/
var cpro_id = "u1888128";404 Not Found!!!

我要回帖

更多关于 webview加载网页慢 的文章

 

随机推荐