从Webview发送参数给本地代码

在Webview里跑的网页,用户操作触发事件,要将参数传递给Webview外的本地代码(比如Android或者iOS)。

具体场景,比如我在网页中有一个图片,点击这个图片,需要让Android/iOS本地代码获取到一组图片URL,并产生一个组件翻页显示这些图片。

那么问题是,我需要用什么方式将这些URL从HTML中传递出来,传给Android/iOS。

下面写了一组简单的原型,使用统一的方式,解决了这个问题:

HTML的写法

写了个简单的HTML代码,供测试使用:

1
2
3
4
5
<html>
<body>
<a href='local://localhost/?url=http%3A%2F%2Fexample.com%2Findex.php%3Ffoo%3Dbar%26test%3Done&url=http%3A%2F%2Fexample.com%2Findex.php%3Ffoo%3Dbar%26test%3Done'><img src="images/test.jpg"></a>
</body>
</html>

我是写在一个nodejs/express项目中,public目录下,静态网页。

这里url参数有2个,后面Android和iOS是可以解析出来的。

Android如何收到URL参数

Android有两种方式从Webview/JavaScript获取参数:

因为iOS有类似shouldOverrideUrlLoading的方式,所以这里使用这种方式。

源代码见:https://github.com/MarshalW/AndroidWebviewSendParamsToNative

主要代码MyActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);

this.webview = (WebView)findViewById(R.id.webview);
WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSupportZoom(false);
webview.setWebViewClient(new MyCustomWebViewClient());
webview.loadUrl("http://10.0.2.2:3000/test.html");
}

用到的内部类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private class MyCustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.d("webviewTest",">>>>>>>>>>>>>"+url);
Uri uri= Uri.parse(url);
if (uri.getScheme().equals("local")){
List<String> values=uri.getQueryParameters("url");
for (String v:values){
Log.d("webvewTest",">>>>>>>>>>>>>"+v);
}
return true;
}

view.loadUrl(url);
return true;
}
}

这里使用10.0.2.2,是因为我使用模拟器(X86模拟器4.4.2,性能还可以),访问开发机器的IP地址是Android规定的这个。

想获取到参数数组还是很容易的。

点击Webview上的图片,可在Android日志中看到:

1
2
3
10-27 03:53:40.982    3324-3324/com.example.marshal.webviewtest D/webviewTest﹕ >>>>>>>>>>>>>local://localhost/?url=http%3A%2F%2Fexample.com%2Findex.php%3Ffoo%3Dbar%26test%3Done&url=http%3A%2F%2Fexample.com%2Findex.php%3Ffoo%3Dbar%26test%3Done
10-27 03:53:40.982 3324-3324/com.example.marshal.webviewtest D/webvewTest﹕ >>>>>>>>>>>>>http://example.com/index.php?foo=bar&test=one
10-27 03:53:40.982 3324-3324/com.example.marshal.webviewtest D/webvewTest﹕ >>>>>>>>>>>>>http://example.com/index.php?foo=bar&test=one

iOS接收URL参数

iOS只有通过拦截URL请求获取Webview的参数的方法:webView:shouldStartLoadWithRequest:navigationType:

而且,比Android麻烦在,包含在URL中的参数需要自己来解析(iOS 8提供了queryItems,不过要等到iOS 7淘汰了才可使用)。

找到一个项目NSURL-Parameters可以通过Category解决参数解析问题,不过它不支持解析参数数组,就是类似这样的:

1
http://localhost/?id=3&id=4

我希望能得到一个类似这样的解析结果:

1
2
3
4
5
{
id:[
3,4
]

}

改造了一下这个项目,让它支持我想要的方式:MarshalW/NSURL-Parameters。没有改动人家的功能,增加了获取参数列表的方法。

主要代码ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#import "NSURL+Parameters.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIWebView *webview;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.webview.delegate=self;
NSURL *url =[NSURL URLWithString:@"http://localhost:3000/test.html"];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
[self.webview loadRequest:request];
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(@"should load with request, %@",request.URL);

if ([request.URL.scheme isEqualToString:@"local"]) {
NSLog(@"==>>should load with request, %@",[request.URL parametersForKey:@"url"]);
return NO;
}

return YES;
```

点击Webview中图片可在Xcode看到如下日志:

2014-10-27 17:14:18.247 TestWebView[4933:2874857] should load with request, local://localhost/?url=http%3A%2F%2Fexample.com%2Findex.php%3Ffoo%3Dbar%26test%3Done&url=http%3A%2F%2Fexample.com%2Findex.php%3Ffoo%3Dbar%26test%3Done
2014-10-27 17:14:18.248 TestWebView[4933:2874857] ==>>should load with request, (
http://example.com/index.php?foo=bar&test=one“,
http://example.com/index.php?foo=bar&test=one
)
```