欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS原生App与H5页面交互 离线缓存 笔记

程序员文章站 2022-07-03 21:01:55
//webview每次加载之前都会调用这个方法,利用该代理方法截取JS的href来调用原生的方法 - (BOOL)webView:(UIWebView*)webView...
//webview每次加载之前都会调用这个方法,利用该代理方法截取JS的href来调用原生的方法

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType

       然而这次的交互要求是进行双向通信,即JS调用原生App的方法之后,原生App要讲相关参数信息返回给H5页面,H5页面接受到参数信息后做其他处理。

例:H5页面的发布信息按钮,在点击按钮后要在原生端判断用户是否登录,若没有登录则弹出原生登录页面,登录成功后将用户信息返回给H5页面,继续发布流程。

重点来了!
在这里推荐一个比较好的第三方库即:WebViewJavascriptBridge 

地址:https://github.com/marcuswestin/WebViewJavascriptBridge

通过使用该库可以轻松实现JS与原生交互。

//初始化WebViewJavascriptBridge方法

_bridge= [WebViewJavascriptBridge bridgeForWebView:self.BookWebView webViewDelegate:self handler:^(id data,WVJBResponseCallback responseCallback) {

}];

//原生与JS约定接口名为“testObjcCallback”,data是JS传递过来的信息,responseCallback来将信息传递给JS

[_bridge registerHandler:@"testObjcCallback" handler:^(id  data,WVJBResponseCallback responseCallback) {

responseCallback("postInfomationToJS")

}];
WebViewJavascriptBridge使用说明(IOS)

由于现在很多产品都是有安卓版跟ios版,就意味着同一样东西要出两套,由两组人去完成,不仅增加了开发成本,也大大加剧了维护成本。聪明的coder想出了跨平台的思路,用html写页面,分别用webview(ios),(安卓)来加载,对某些html无法调用的硬件,通过双方的交互来实现方法的互调和传值。这个过程就是跨平台。
下面来说一下WebViewJavascriptBridge在ios端怎么样使用。
首先确保一份已经配好功能的html文件。(html还在学习阶段,暂时就不卖弄了。。。)

1.初始化一个webview(viewdidload)

UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds];  
[self.view addSubview:webView]; 

2.将此webview与WebViewJavascriptBridge关联(viewdidload)

if (_bridge) { return; }  

[WebViewJavascriptBridge enableLogging];  

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {  
    NSLog(@"ObjC received message from JS: %@", data);  

    responseCallback(@"Response for message from ObjC");  
}]; 

ps:此时你的webview就与js搭上桥了。下面就是方法的互调和参数的互传。

(1) js调oc方法(可以通过data给oc方法传值,使用responseCallback将值再返回给js)

[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {  
     NSLog(@"testObjcCallback called: %@", data);  
     responseCallback(@"Response from testObjcCallback");  
 }]; 

这里注意testObjcCallback这个方法的标示。html那边的命名要跟ios这边相同,才能调到这个方法。当然这个名字可以两边商量着自定义。简单明确即可。

(2)oc调js方法(通过data可以传值,通过response可以接受js那边的返回值)

id data = @{ @"greetingFromObjC": @"Hi there, JS!" };  
 [_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {  
     NSLog(@"testJavascriptHandler responded: %@", response);  
 }];  

注意这里的testJavascriptHandler也是个方法标示。
(3)oc给js传值(通过response接受返回值)

[_bridge send:@”A string sent from ObjC to JS” responseCallback:^(id response) {
NSLog(@”sendMessage got response: %@”, response);
}];

(4)oc给js传值(无返回值)

[_bridge send:@”A string sent from ObjC after Webview has loaded.”];

##2 UIWebView页面信息的离线缓存

推荐一个比较好的第三方库RNCachingURLProtocol ,只需要在AppDelegate中加入下面方法即可。
[NSURLProtocolregisterClass:[RNCachingURLProtocolclass]];
地址:https://github.com/rnapier/RNCachingURLProtocol


##3  NSURLConnection小解

NSURLConnection提供对网络异步加载请求的支持,并且将获取的数据返回给代理。提供了简单的接口去创建和取消连接,同时使用delegate方法去支持连接过程的反馈和控制 。在实际开发中直接用的不多,但是有的第三方库却是用它来封装的。

举例一: 
1、先创建一个NSURL 
2、在通过NSURL创建NSURLRequest,可以指定缓存规则和超时时间 
3、创建NSURLConnection实例,指定NSURLRequest和一个delegate对象 
   如果创建失败,则会返回nil,如果创建成功则创建一个NSMutalbeData的实例用来存储数据 

代码: 

NSURL *url = [NSURL URLWithString:@”http://192.168.2.128:9090/ssonew/login/UserLogin.action?name=mql&psd=123456“];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
self.theConncetion = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];

if (_theConncetion) {
    self.receiveData = [[NSMutableData alloc]init];
}
NSURLConnection有3个初始化函数,只有第一个初始化函数可以做到创建连接但是并 
不马上开始下载,而是通过start:开始,调用该初始化函数时,startImmediately传NO
(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately NS_AVAILABLE(10_5, 2_0); (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate; (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;

当收到initWithRequest: delegate:或收到connectionWithRequest: delegate: 消息时,下载会立即开始,在代理(delegate)
收到connectionDidFinishLoading:或者connection:didFailWithError:消息之前 
可以通过给连接发送一个cancel:消息来中断下载 

connection: willSendRequest: redirectResponse:这个方法在请求将要被发送出去之前会调用
返回值是一个NSURLRequest,就是那个真正将要被发送的请求

第二个参数request就是被重定向处理过后的请求
第三个参数response是触发重定向请求的响应包.默认是支持跳转的。

(NSURLRequest )connection:(NSURLConnection )connection willSendRequest:(NSURLRequest )request redirectResponse:(NSURLResponse )response
{
NSURLRequest *newRequest = request;
if (response) {
NSLog(@”redirect!!!”); //如,请求google的网站时会有跳转发生
newRequest = nil; //拒绝跳转
}

return newRequest;
}


当服务器提供了足够客户程序创建NSURLResponse对象的信息时,代理对象会收到 
一个connection:didReceiveResponse:消息,在消息内可以检查NSURLResponse 
对象和确定数据的预期长途,mime类型,文件名以及其他服务器提供的元信息 

要注意,一个简单的连接也可能会收到多个connection:didReceiveResponse:消息 
当服务器连接重置或者一些罕见的原因(比如多组mime文档),代理都会收到该消息 
这时候应该重置进度指示,丢弃之前接收的数据 

(void)connection:(NSURLConnection )connection didReceiveResponse:(NSURLResponse )response
{
//注意这里将NSURLResponse对象转换成NSHTTPURLResponse对象才能使用
NSHTTPURLResponse httpResponse = (NSHTTPURLResponse)response;
if ([response respondsToSelector:@selector(allHeaderFields)]) {
NSDictionary *dictionary = [httpResponse allHeaderFields];
NSLog(@”%@”, dictionary);
}

[self.receiveData setLength:0];

}


当下载开始的时候,每当有数据接收,代理会定期收到connection:didReceiveData:消息 
代理应当在实现中储存新接收的数据,下面的例子既是如此 

-(void) connection:(NSURLConnection*)connection didReceiveData:
(NSData *) data

[receiveData appendData:data];

-(void) connection:(NSURLConnection*)connection didReceiveData: 
            (NSData *) data 
{ 
   [receiveData appendData:data]; 

 } 

在上面的方法实现中,可以加入一个进度指示器,提示用户下载进度 

当下载的过程中有错误发生的时候,代理会收到一个connection:didFailWithError消息 
消息参数里面的NSError对象提供了具体的错误细节,它也能提供在用户信息字典里面失败的 
url请求(使用NSErrorFailingURLStringKey) 

当代理接收到连接的connection:didFailWithError消息后,对于该连接不会再收到任何消息 

举例 
(void)connection:(NSURLConnection )connection didFailWithError:(NSError )error
{

}


最后,如果连接请求成功的下载,代理会接收connectionDidFinishLoading:消息,代理不会收到其他的消息

举例: 
(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@”总共请求的数据长度 = %d”, [self.data length]);
}

注:从ios5.0开始,苹果为开发者引入block概念,所以使用NSURLConnection发送一个请求可以如下:

NSURL *url = [NSURL URLWithString:@”http://www.google.com“];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
NSOperationQueue *queue=[[NSOperationQueue alloc]init];

[ NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    if (error == nil){

        NSLog(@"download success");
        NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
        NSString *str = [[NSString alloc]initWithData:data encoding:enc];
        NSLog(@"%@", str);

    }else if(error!=nil){

        NSLog(@"error");

    }

}];

“`

以上演示的请求都是GET请求,如果没使用NSMutableURLRequest的实例方法setHTTPMethod进行设置,请求都是GET请求。
如要走POST请求,仅需:
1、将上面的NSURLRequest替换成NSMutableURLRequest
2、[request setHTTPMethod:@“POST”];
3、[request setHTTPBody:postData];//postData一般是JSON格式的字符串转换过来的。
4、使用NSURLConnection启动请求。