日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

iOS端接入

更新時(shí)間:

EMAS提供WindVane供您集成應(yīng)用,以實(shí)現(xiàn)通過(guò)WindVane集成H5應(yīng)用。本文介紹如何在iOS端集成WindVane。

接入SDK

  1. 在podfile文件中指定倉(cāng)庫(kù),添加依賴(lài),并執(zhí)行pod install或者pod update命令,獲取SDK到項(xiàng)目中。

    source 'https://github.com/CocoaPods/Specs.git'
    source 'https://github.com/aliyun/aliyun-specs.git'
    pod 'WindVane', '11.2.2.1'
    pod 'ZipArchive', '1.4.0'
    pod 'NetworkSDK', '10.0.4.6'
  2. 添加系統(tǒng)庫(kù)依賴(lài)

    在工程項(xiàng)目中(Build Phases -> Link Binary With Libraries)添加以下庫(kù)依賴(lài)。

    AssetsLibrary.framework
    AVFoundation.framework
    MobileCoreServices.framework
    AudioToolbox.framework
    Photos.framework
    WebKit.framework
  3. 按照如下配置,初始化SDK。

    #import <WindVane/WindVane.h>
    
    @interface EMASWindVaneConfig : NSObject
    + (void)setUpWindVanePlugin;
    @end
    
    @implementation EMASWindVaneConfig
    
    + (void)setUpWindVanePlugin {
        // 設(shè)置APPKey, 如果使用了安全黑匣子, 就會(huì)使用安全黑匣子的key
        // [WVUserConfig setAppKey:@"Your appKey"secrect:@"Your secretKey"];
        [WVUserConfig setAppKey:@"Your appKey"];
        // 設(shè)置環(huán)境
        // [WVUserConfigsetEnvironment:WVEnvironmentDaily];
        [WVUserConfig setEnvironment:WVEnvironmentRelease];
        // 設(shè)置TTID
        // [WVUserConfig setTTid:@"windvane@****"];
        // 設(shè)置UA
        [WVUserConfig setAppUA:[NSString stringWithFormat:@"TBIOS"]];
        // 設(shè)置 App 名稱(chēng),會(huì)在 UserAgent 中帶上,請(qǐng)務(wù)必正確設(shè)置。
        [WVUserConfig setAppName:@"EMASDemo"];
        // 設(shè)置App版本
        NSDictionary *infoDictionary =[[NSBundle mainBundle] infoDictionary];
        [WVUserConfig setAppVersion:[infoDictionary objectForKey:@"CFBundleShortVersionString"]];
        // WKWebView 支持 NSURLProtocol
        [WVURLProtocolService setSupportWKURLProtocol:YES];
    
        #ifdef DEBUG
        [WVUserConfig setDebugMode:YES];
        // 打開(kāi) WindVane 的 Log
        [WVUserConfig openWindVaneLog];
        [WVUserConfig setLogLevel:WVLogLevelVerbose];
        [WVBasic setJSLogLevel:WVLogLevelVerbose];
        #endif
    
        // 初始化WindVane各模塊
        [WVBasic setup];
        [WVAPI setup];
        [WVMonitor startMonitoring];
    
    }
    
    @end

    AppDelegate.m文件的application:didFinishLaunchingWithOptions方法中,調(diào)用以下代碼,初始化SDK。

    - (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        [EMASWindVaneConfig setUpWindVanePlugin];
    }

使用WVWKWebView

說(shuō)明

建議在iOS 9或更高版本再使用WKWebView,iOS 8及之前版本存在較多缺陷。

WVWKWebView使用的是iOS 9.0新加入的WKWebView,使用WebKit內(nèi)核進(jìn)行渲染,具有更高的渲染效率和JS執(zhí)行效率,也支持更多的HTML5特性。有需要的Native業(yè)務(wù)方可以根據(jù)自己的需要,使用WVWKWebView來(lái)展示頁(yè)面。

WindVane iOS版本中,全面優(yōu)化了對(duì)WVWKWebView的支持,通過(guò) [WVURLProtocolServicesetSupportWKURLProtocol:YES]主動(dòng)開(kāi)啟NSURLProtocol的攔截功能后,可以正常使用預(yù)加載功能,持久化Cookie(設(shè)置有expires或max-age)也可以正常同步,但POST請(qǐng)求會(huì)丟失body,非持久化 Cookie仍是不能同步的。目前僅有XMLHTTPRequest同步POST請(qǐng)求Blob和FormData不支持。

監(jiān)聽(tīng)頁(yè)面信息

WindVane提供了頁(yè)面的基礎(chǔ)信息。通過(guò)KVO監(jiān)聽(tīng)WebView title屬性和estimatedProgress屬性的變化,就可以及時(shí)獲取頁(yè)面標(biāo)題和加載進(jìn)度的改變,然后客戶(hù)端自行決定如何顯示在界面中。

// 添加 KVO 監(jiān)聽(tīng),務(wù)必在 WebView 銷(xiāo)毀前移除監(jiān)聽(tīng)。
[_webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNewcontext:nil];

[_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNewcontext:nil];

// 處理 KVO 監(jiān)聽(tīng)。
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {
    [super observeValueForKeyPath:keyPathofObject:object change:change context:context];
    if ([keyPathisEqual:@"title"]) {
        // change[NSKeyValueChangeNewKey] 就是新的頁(yè)面標(biāo)題。
    } else if ([keyPath isEqual:@"estimatedProgress"]){
        // [change[NSKeyValueChangeNewKey]doubleValue] 就是新的加載進(jìn)度,范圍是 0.0~1.0。
        // 注意加載進(jìn)度可能在非主線(xiàn)程觸發(fā),如果有 UI 操作注意切換線(xiàn)程。
    }
}

- (void)dealloc {
    // 移除KVO監(jiān)聽(tīng),這里只是一個(gè)示例,根據(jù)實(shí)際情況不一定要在dealloc中做。
    [_webView removeObserver:selfforKeyPath:@"title"];
    [_webView removeObserver:selfforKeyPath:@"estimatedProgress"];
}

接入灰度SDK

  1. 接入灰度SDK。

        pod 'DynamicConfiguration', '~> 11.0.0'
        pod 'DynamicConfigurationAdaptor', '~> 1.0.0'
  2. 初始化灰度SDK。

    // 灰度初始化
    - (void)initDyConfig
    {
        NSString *appKey = @"xxxxx";  
        NSString *logicIdentifier = [NSString stringWithFormat:@"%@@%@",appKey, [self isDeviceIphone] ? @"iphoneos" : @"iPad"];
        [[DynamicConfigurationAdaptorManager sharedInstance] setUpWithMaxUpateTimesPerDay:10 AndIdentifier:logicIdentifier AndAppkey:appKey];
    }

接入ZCache SDK

1.接入ZCache SDK。

   pod 'ZCache', '~> 11.1.0.0'

2.初始化ZCache SDK。

- (void)initZCache
{
    NSString *appKey = @"xxx";
    NSString *appSecret = @"xxx";
    NSString *version = @"xxx";
    
    [ZCache setupWithAppKey:appKey appSecret:appSecret appVersion:version];
}

(可選)使用JSBridge

JSBridge的Native方法提供方,要像提供普通方法一樣提供JSBridge,要求必須滿(mǎn)足以下基本約束:

  • 方法調(diào)用后,必須返回,任何情況下都要調(diào)用callback以將結(jié)果返回給JS;可以根據(jù)需要返回不同的數(shù)據(jù)。

  • 方法只能有一個(gè)返回值,也就是說(shuō)callback只允許調(diào)用一次,如果有多個(gè)數(shù)據(jù)需要返回給H5,Android請(qǐng)使用標(biāo)準(zhǔn)事件機(jī)制,iOS請(qǐng)使用JSBridge context的dispatchEvent方法。

  • 注意做好資源的釋放工作,尤其iOS不允許對(duì)View或ViewController有強(qiáng)引用。

  1. 注冊(cè)JSBridge

    EMAS提供了兩種注冊(cè)方案:動(dòng)態(tài)注冊(cè)和靜態(tài)注冊(cè)。

    • 動(dòng)態(tài)注冊(cè):無(wú)需主動(dòng)注冊(cè)給WindVane,使用簡(jiǎn)單方便。但是要求類(lèi)名與JSBridge調(diào)用時(shí)的ClassName相同,且目前僅限WindVane iOS。

    • 靜態(tài)注冊(cè):需要調(diào)用WindVane的注冊(cè)方法,使用操作復(fù)雜。不要求特定的類(lèi)名,會(huì)更加靈活。

      總之,只要JSBridge調(diào)用的ClassName并沒(méi)有被占用,那么總是建議使用動(dòng)態(tài)注冊(cè)的方式。

  2. iOS動(dòng)態(tài)注冊(cè)JSBridge

    WindVane提供了新版本的用法,在WVBridgeCallbackContext中統(tǒng)一提供了JSBridge需要的所有功能,而且為Weex等非WebView場(chǎng)景也提供了支持。詳情請(qǐng)參見(jiàn)下文的新接口介紹

    假設(shè)需要實(shí)現(xiàn)兩個(gè)JSBridge:MyJSBridge.firstAPIMyJSBridge.secondAPI,那么JSBridge的ClassName是MyJSBridge,HandlerName分別是firstAPI和secondAPI。

    • 創(chuàng)建一個(gè)MyJSBridge類(lèi)(動(dòng)態(tài)注冊(cè)要求與JSBridge調(diào)用時(shí)的ClassName相同),需要確保不會(huì)有同名類(lèi)。

    • 令MyJSBridge類(lèi)繼承自WVDynamicHandler(引入頭文件WVDynamicHandler.h)。

    • 實(shí)現(xiàn)firstAPI和secondAPI方法,要求方法的簽名必須為-/+(void)handlerName:(NSDictionary *)paramswithWVBridgeContext:(id<WVBridgeCallbackContext>)context,其中:

      • 方法可以是靜態(tài)方法,也可以是動(dòng)態(tài)方法。實(shí)例方法會(huì)直接調(diào)用,實(shí)例方法會(huì)先創(chuàng)建一個(gè)實(shí)例,然后在實(shí)例上調(diào)用。

      • 方法名與JSBridge調(diào)用的HandlerName相同,這里為firstAPI和secondAPI。

      • 第一個(gè)參數(shù)為NSDictionary *,是JS傳入的參數(shù)對(duì)象。

      • 第二個(gè)參數(shù)為 id<WVBridgeCallbackContext>,是JSBridge的調(diào)用上下文。

    • 在方法中實(shí)現(xiàn)JSBridge的邏輯,使用[contextcallbackSuccess:RESULT]/[contextcallbackFailure:RET withResult:RESULT]來(lái)輸出執(zhí)行成功/失敗的結(jié)果返回給JSBridge調(diào)用方。請(qǐng)保證任何情況下都會(huì)調(diào)用、且只會(huì)調(diào)用一次[context callbackXXX]系列方法。

      說(shuō)明
      • JSBridge總是在主線(xiàn)程調(diào)用,如果有耗時(shí)操作,請(qǐng)務(wù)必自行切換線(xiàn)程。

      • 可能你提供的服務(wù)帶有多個(gè)階段輸出性,請(qǐng)使用[contextdispatchEvent:eventName withParam:param]將結(jié)果通過(guò)事件的方式輸出給JSBridge調(diào)用方。

      • 當(dāng)你的JSBridge邏輯執(zhí)行完畢,可以主動(dòng)調(diào)用[contextreleaseHandler:self]方法來(lái)主動(dòng)釋放內(nèi)存,否則就只能等到頁(yè)面銷(xiāo)毀的時(shí)候才會(huì)釋放。注意在dealloc中調(diào)用是沒(méi)有意義的,因此此時(shí)對(duì)象已經(jīng)被釋放了。

      #import <Foundation/Foundation.h>
      #import <WindVane/WindVane.h>
      @interface MyJSBridge : WVDynamicHandler
      @end
      @implementation MyJSBridge
      // 這是一個(gè)靜態(tài)方法,直接調(diào)用。
      + (void)firstAPI:(NSDictionary *)params 
      withWVBridgeContext:(id<WVBridgeCallbackContext>)context{
          // 回調(diào)中處理業(yè)務(wù)邏輯
          [context callbackSuccess:nil];
      }
      // 這是一個(gè)實(shí)例方法,會(huì)創(chuàng)建一個(gè)新實(shí)例,然后再調(diào)用。
      
      // API實(shí)例
      - (void)secondAPI:(NSDictionary *)params 
          // 回調(diào)中處理業(yè)務(wù)邏輯
          [context callbackSuccess:nil];
      }
      @end

      動(dòng)態(tài)注冊(cè)的JSBridge,要求類(lèi)名不能重復(fù)。若希望擴(kuò)展現(xiàn)有ClassName,可以使用JSBridge別名。

常見(jiàn)錯(cuò)誤

如果引入WindVane時(shí)報(bào)如下的錯(cuò)誤,那么可以在項(xiàng)目的BuildPhrase中的Link Binary With Libraries中添加 WebKit,并選擇Status為Optional。

[MISSING IMAGE:error message | left | 618x118, error message | left | 618x118 ]
[MISSING IMAGE:WebKit framework | left | 479x80, WebKit framework | left | 479x80 ]

如果引入WindVane時(shí)報(bào)如下錯(cuò):

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Completion handler passed to -[WVCommonWebView webView:decidePolicyForNavigationAction:decisionHandler:] was not called'

需要手動(dòng)實(shí)現(xiàn)WKNavigationDelegate的代理方法并回調(diào)decisionHandler:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    decisionHandler(WKNavigationActionPolicyAllow);
}
  • 使用iOS WebView

    WindVane在iOS平臺(tái)提供了WVWebView(基于UIWebView)和WVWKWebView(基于WKWebView)的支持,并為兩種WebView做了功能和接口的兼容。因此請(qǐng)總是使用UIView*作為WebView的類(lèi)型,可以兼容WVWebView和WVWKWebView的主要功能。

    另外,請(qǐng)使用#import <WindVane/WindVane.h>來(lái)引入WindVane的頭文件,無(wú)需區(qū)分WindVane整包或者分包。您在升級(jí)WindVane版本時(shí),請(qǐng)注意檢查并修改代碼的Warning,盡早修改已廢棄的方法。

  • 使用WindVane提供的ViewController

    首先您可以選擇使用WindVane提供的含有WebView的WVUIWebViewController,它封裝了常用的WebView接口,提供了WVWebView和WVWKWebView切換、狀態(tài)欄背景兼容、工具欄、錯(cuò)誤頁(yè)、加載框和橫屏支持等功能。

    關(guān)于UI無(wú)關(guān)的基礎(chǔ)功能信息請(qǐng)參考WVViewController.h,UI相關(guān)的功能請(qǐng)參考WVUIWebViewController.h

    要在WVUIWebViewController中切換到WVWKWebView,可以設(shè)置useWKWebView屬性:

    • WVUseWKWebViewNever表示總是使用WVWebView,是useWKWebView屬性的默認(rèn)值。

    • WVUseWKWebViewAlways表示總是使用WVWKWebView

    • WVUseWKWebViewCustomdecideIsUseWKWebView方法的返回值決定是使用WVWKWebView,還是 WVWebView。由于必須在初始化WebView之前就決定是否切換到WVWKWebView,因此 WVUIWebViewController默認(rèn)會(huì)檢查loadUrl中是否包含 _wvUseWKWebView=YES參數(shù),包含的話(huà)就切換到WVWKWebView

      #import <WindVane/WindVane.h>
      
      WVUIWebViewController * controller =[[WVUIWebViewController alloc] init];
      controller.loadUrl = @"http://m.taobao.com";
      // 開(kāi)啟 JSBridge,如果需要使用 JSBridge 必須調(diào)用。
      controller.openLocalService = YES;
      // [iOS 7 適配] 隱藏導(dǎo)航欄時(shí),為 Status Bar 添加白色背景顏色,按需要設(shè)置。
      [controller supportiOS7WithoutStatusBar];
      // 禁用 WebView 的長(zhǎng)按事件,按需要設(shè)置。
      // controller.openWebKitLongPress = NO;
      [self.navigationController pushViewController:controlleranimated:YES];

WindVane功能說(shuō)明

  • WindVaneCore

    WindVane核心庫(kù),定義了模塊間的接口和約束,以及公共工具類(lèi),所有模塊都必須依賴(lài)它。

  • WindVaneBridge

    WindVane JSBridge庫(kù),提供了JSBridge的功能,可以在自己的WebView中或者直接脫離WebView使用。

  • WindVaneBasic

    WindVane基礎(chǔ)WebView庫(kù),提供了WebView相關(guān)功能優(yōu)化和擴(kuò)展,包括WebView、WKWebView、ViewController、StandardEventModal模塊。

  • WindVaneAPI

    WindVane自身提供的一些基礎(chǔ)JSBridge API。 需要添加系統(tǒng)庫(kù):CoreBluetooth.framework、AddressBookUI.framework、AddressBook.framework、MessageUI.framework、Messages.framework、ContactsUI.framework、Contacts.framework、UserNotifications.framework。

  • WindVaneMonitor

    WindVane埋點(diǎn)支持庫(kù),提供了埋點(diǎn)的UT。

  • WindVaneEMASExtension

    EMAS擴(kuò)展庫(kù),主要有URL攔截使用預(yù)加載配置和上報(bào)Crash,預(yù)加載可以讓前端在EMAS平臺(tái)將html、js、css 等資源打包成一個(gè)zip包,客戶(hù)端可以采用啟動(dòng)后下載或者直接預(yù)置的方式將zip包解壓到本地,使得用戶(hù)在瀏覽網(wǎng)頁(yè)的時(shí)候不需要再去下載資源,提升用戶(hù)體驗(yàn)。

自定義生命周期

如果實(shí)現(xiàn)的是實(shí)例方法,默認(rèn)情況下,每次調(diào)用都會(huì)創(chuàng)建一個(gè)實(shí)例。該實(shí)例會(huì)被強(qiáng)引用以保證不會(huì)被錯(cuò)誤釋放,直到WebView被銷(xiāo)毀時(shí)才會(huì)釋放。您可以通過(guò)以下方法,實(shí)現(xiàn)實(shí)例生命周期的自定義。

@implementation MyJSBridge
+ (WVBridgeScope)instanceScope {

    return WVBridgeScopeInvocation;
}
@end

WindVane支持四種生命周期:每次調(diào)用一個(gè)實(shí)例(WVBridgeScopeInvocation),每個(gè)頁(yè)面一個(gè)實(shí)例(WVBridgeScopePage),每個(gè)View一個(gè)實(shí)例(WVBridgeScopeView)和全局唯一實(shí)例(WVBridgeScopeStatic)。

對(duì)于ScopeInvocation和ScopePage,實(shí)例的引用會(huì)持續(xù)到頁(yè)面被切換(例如WebView跳轉(zhuǎn)新頁(yè)面、回退歷史記錄等)。對(duì)于ScopeView,實(shí)例的引用會(huì)持續(xù)到View被銷(xiāo)毀。因此在確保當(dāng)前實(shí)例可以被銷(xiāo)毀時(shí),建議主動(dòng)調(diào)用 [contextreleaseHandler:self]立刻釋放當(dāng)前實(shí)例,以節(jié)約內(nèi)存。ScopeStatic的實(shí)例,會(huì)一直存在,但也支持通過(guò) [contextreleaseHandler:self] 主動(dòng)釋放。

  • 與View或ViewController交互

    JSBridge中允許與View(包括WebView)或ViewController交互,以實(shí)現(xiàn)某些特殊邏輯。或者檢查ViewController是否具有特定的類(lèi)型,以實(shí)現(xiàn)只允許在某類(lèi)ViewController中調(diào)用的私有JSBridge。

    不允許對(duì)View或ViewController持有強(qiáng)引用,防止出現(xiàn)循環(huán)引用導(dǎo)致內(nèi)存泄露。context已提供view屬性和 viewController供實(shí)例方法使用,在WebView環(huán)境下提供了webViewEnv屬性,Weex場(chǎng)景下提供了weexEnv 屬性。這些屬性會(huì)自動(dòng)設(shè)置和更新,您可以直接使用。

  • 發(fā)送事件

    在JSBridge中向容器發(fā)送事件,請(qǐng)使用[contextdispatchEvent:withParam:],會(huì)對(duì)當(dāng)前環(huán)境(WebView/Weex)做適配。

    事件機(jī)制中提供的方法只能夠支持WebView環(huán)境。

  • 復(fù)雜的JSBridge生命周期管理

    對(duì)于一些持續(xù)性的JSBridge,如音頻播放、動(dòng)作感應(yīng),需要長(zhǎng)時(shí)間的發(fā)送事件。但是在用戶(hù)跳轉(zhuǎn)到其它Native 頁(yè)面、跳轉(zhuǎn)到其它H5 WindVane提供了以下生命周期回調(diào)方法,可以在JSBridge中實(shí)現(xiàn)。

    @implementation MyJSBridge
    /**
    * 重置處理器 - 該方法將會(huì)在加載新頁(yè)面時(shí)調(diào)用,可以做一些清理工作。
    *
    * @param context WVBridge 被重置時(shí)的上下文。
    * @param request 要加載的新頁(yè)面。
    */
    -(void)resetWithContext:(id<WVBridgeContext>)contextwithNextRequest:(NSURLRequest *)request {
    }
    /**
    
    * 暫停處理器 - 該方法將會(huì)在 UIViewControllerviewWillDisappear 時(shí)調(diào)用,用于降低WVBridge API的性能消耗,例如暫停播放音樂(lè)和持續(xù)性監(jiān)聽(tīng)器等。
    * 不會(huì)對(duì) WVBridgeScopeStatic 作用域的 WVBridge 調(diào)用。
    *
    * @param context WVBridge 被暫停時(shí)的上下文。
    */
    -(void)pauseWithContext:(id<WVBridgeContext>)context {
    }
    /**
    
    * 恢復(fù)處理器 - 該方法將會(huì)在 UIViewControllerviewWillAppear 時(shí)調(diào)用,用于恢復(fù) WVBridge API 的性能,例如恢復(fù)播放音樂(lè)和持續(xù)性監(jiān)聽(tīng)器等。
    * 不會(huì)對(duì) WVBridgeScopeStatic 作用域的 WVBridge 調(diào)用。
    *
    * @param context WVBridge 被恢復(fù)時(shí)的上下文。
    */
    -(void)resumeWithContext:(id<WVBridgeContext>)context {
    }
    @end

    如果JSBridge需要在切換到新頁(yè)面后自動(dòng)清理,需要實(shí)現(xiàn)resetWithContext:withNextRequest:;如果需要在當(dāng)前 UIViewController被其它Native界面覆蓋時(shí)暫停和恢復(fù),需要實(shí)現(xiàn)pauseWithContext: resumeWithContex:。一般建議與WVBridgeScopeView同時(shí)使用。

    如果需要在應(yīng)用切換前后臺(tái)時(shí)做特殊工作,可以自行注冊(cè)UIApplicationDidBecomeActiveNotificationUIApplicationWillResignActiveNotification這兩個(gè)通知。

  • 新接口介紹

    在新版本JSBridge接口,將所有功能收攏到了統(tǒng)一的id<WVBridgeCallbackContext>中,一切功能都可以通過(guò)context來(lái)實(shí)現(xiàn)。具體包括:

    • 獲取環(huán)境信息,基本信息有來(lái)源URL referrer,view和viewController,以及當(dāng)前的環(huán)境 envUIView<WVWebViewBasicProtocol> *WeexSDKInstance *

    • 發(fā)送事件,提供了dispatchEvent:withParam: 方法。

    • 釋放實(shí)例,提供了releaseHandler: 方法。

    • 回調(diào),提供了callbackSuccess: callbackFailure:withResult:方法,以及一系列失敗回調(diào)的快捷方法。

    • 所有JSBridge的方法參數(shù)全部統(tǒng)一為NSDictioanry* paramsid<WVBridgeCallbackContext> context,無(wú)論是動(dòng)態(tài)注冊(cè),還是各類(lèi)靜態(tài)注冊(cè)的方法。

支持懶加載的動(dòng)態(tài)庫(kù)

一些客戶(hù)端為了提高應(yīng)用打開(kāi)速度,對(duì)動(dòng)態(tài)庫(kù)做了懶加載處理,這樣可以將動(dòng)態(tài)庫(kù)的加載延遲到使用時(shí),避免影響應(yīng)用啟動(dòng)耗時(shí)。

如果動(dòng)態(tài)庫(kù)中提供了動(dòng)態(tài)注冊(cè)JSBridge,那么在動(dòng)態(tài)庫(kù)被加載之前,是找不到對(duì)應(yīng)的類(lèi)的,也無(wú)法正常調(diào)用。因此我們懶加載的動(dòng)態(tài)庫(kù)做了特別的支持,允許動(dòng)態(tài)庫(kù)通過(guò)plist 配置動(dòng)態(tài)JSBridge的類(lèi)名,以便WindVane在需要使用JSBridge時(shí)主動(dòng)將動(dòng)態(tài)庫(kù)加載進(jìn)來(lái)。

解決方案是在動(dòng)態(tài)庫(kù)的framework根目錄,提供一個(gè)命名為XXX_bundle.plist的文件,其中的XXX部分可以任意命名,甚至同一個(gè)framework包含多個(gè)符合此規(guī)則的plist也可以。在plist中添加名為windvane_bridge_list的數(shù)組,并在數(shù)組中添加需要配置的動(dòng)態(tài)JSBridge類(lèi)名,多個(gè)plist的配置會(huì)自動(dòng)合并。示例如下:

[MISSING IMAGE:Dynamic Framework Config | left | 600x374, Dynamic Framework Config | left |600x374 ]

請(qǐng)確保動(dòng)態(tài)庫(kù)全部位于客戶(hù)端的Frameworks目錄,如果不同動(dòng)態(tài)庫(kù)之間的映射關(guān)系存在覆蓋,動(dòng)態(tài)庫(kù)會(huì)在JSBridge被首次使用時(shí),打出日志 'A' 注冊(cè)的WVBridge 'XXX' 被 'B' 覆蓋了,此外,在WindVane的DEBUG模式下還會(huì)彈出Alert提示。

iOS靜態(tài)注冊(cè)JSBridge

WindVane iOS允許將JSBridge注冊(cè)到全局,也可以注冊(cè)到特定WebView。前者可以在任意WebView中調(diào)用,而后者只能在注冊(cè)的WebView中使用,適合需要較嚴(yán)格的權(quán)限控制的JSBridge。 但是注冊(cè)到全局的JSBridge,也可以通過(guò)對(duì)ViewController 的類(lèi)型或?qū)傩赃M(jìn)行校驗(yàn),以達(dá)到部分權(quán)限的控制,因此如無(wú)特別的必要,請(qǐng)總是將JSBridge注冊(cè)到全局,會(huì)有更少的內(nèi)存消耗。

  • 將JSBridge注冊(cè)到全局

    #import <WindVane/WindVane.h>
    // JSBridge 調(diào)用的 Block。
    WVBridgeHandler handler = ^(NSDictionary *params, id<WVBridgeCallbackContext> context) {
        [context callbackSuccess:nil];
    };
    // 注冊(cè)JSBridge到全局,特別注意這里的JSBridge 名稱(chēng)合并寫(xiě)作@"className.handlerName" 格式。
    WVBridgeRegisterHandler(@"className.handlerName",handler);
  • 將JSBridge注冊(cè)到特定WebView

    這樣注冊(cè)的JSBridge,只能在注冊(cè)到的WebView中使用className和handlerName調(diào)用。

    #import <WindVane/WindVane.h>
    // JSBridge 調(diào)用的 Block
    WVBridgeHandler handler = ^(NSDictionary *params, id<WVBridgeCallbackContext> context) {
        [context callbackSuccess:nil];
    };
    // 注冊(cè) JSBridge 到指定 WebView,特別注意這里的 JSBridge 名稱(chēng)合并寫(xiě)作
    @"className.handlerName" 格式。
    [webview registerHandler:@"className.handlerName" withBlock:handler];
    // 注冊(cè) JSBridge到指定ViewController中的WebView,特別注意這里的JSBridge名稱(chēng)合并寫(xiě)作 @"className.handlerName" 格式。
    [wvViewController registerHandler:@"className.handlerName" withBlock:handler];

    靜態(tài)注冊(cè)的JSBridge,不支持復(fù)雜的生命周期管理。如果需要實(shí)現(xiàn)復(fù)雜的JSBridge,請(qǐng)動(dòng)態(tài)注冊(cè)JSBridge。

    靜態(tài)注冊(cè)的JSBridge,若className和handlerName都相同,那么后注冊(cè)的會(huì)覆蓋先注冊(cè)的。同時(shí)也會(huì)覆蓋同名的動(dòng)態(tài)注冊(cè)方法。

JSBridge別名

WindVane提供了JSBridge的別名服務(wù),可以用來(lái)對(duì)現(xiàn)有ClassName的JSBridge進(jìn)行擴(kuò)展或覆蓋,也可以對(duì)JSBridge的升級(jí)、更新、改名和沖突提供解決方案。

允許將原始的JSBridge className.handlerName,映射到新的別名aliasClassName.aliasHandlerName,您既可以使用舊的className.handlerName來(lái)訪(fǎng)問(wèn),也可以使用新的aliasClassName.aliasHandlerName來(lái)訪(fǎng)問(wèn)。

WindVane的別名服務(wù),是首先將別名解析為原始名稱(chēng),然后使用原始名稱(chēng)來(lái)查找 JSBridge;不要將別名指向其它別名,可能導(dǎo)致無(wú)法正確找到JSBridge。

iOS注冊(cè)全局別名:全局別名必須與全局JSBridge配合使用。

#import <WindVane/WindVane.h>
// 注冊(cè) JSBridge 別名到全局。
NSDictionary * alias = @{

    @"類(lèi)別名.方法別名": @"類(lèi)名.方法名",
    @"aliasClassName.aliasHandlerName":@"className.handlerName"
};
WVBridgeRegisterAlias(alias);

JSBridge全局鑒權(quán)

WindVane提供了JSBridge的全局鑒權(quán)接口,供實(shí)現(xiàn)App級(jí)別的JSBridge API鑒權(quán)。該鑒權(quán)層切入到JSBridge的調(diào)用邏輯中,可以由客戶(hù)端統(tǒng)一自定義鑒權(quán)邏輯。

對(duì)于單個(gè)JSBridge的鑒權(quán),請(qǐng)?jiān)谠揓SBridge的實(shí)現(xiàn)中自行實(shí)現(xiàn)。

#import <WindVane/WindVane.h>
// 實(shí)現(xiàn) JSBridge 鑒權(quán)方法。
@interface MyJSBridgeCheckerHandler : NSObject<WVBridgeCheckerProtocol>
@end
@implementation MyJSBridgeCheckerHandler
/**

檢查指定 WVBridge 是否具有執(zhí)行權(quán)限。
* 總是在后臺(tái)線(xiàn)程調(diào)用。
*
* @param apiName WVBridge 的名稱(chēng),格式為 "類(lèi)名.方法名"。
* @param paramsWVBridge 的調(diào)用參數(shù)。
* @param context WVBridge 的執(zhí)行上下文。
*
* @return 權(quán)限檢查結(jié)果。
*/
- (WVBridgePermission*)checkPermission:(NSString *)apiName withParams:(NSDictionary *)paramswithContext:(id<WVBridgeContext>)context {
    return [WVBridgePermissionpermissionNotSure];

}
@end

// 注冊(cè)鑒權(quán)處理器。

[WVBridgeChecker registerChecker:[[MyJSBridgeCheckerHandler alloc]init]];

在Native中使用JSBridge

WindVane iOS中,支持在Native中調(diào)用JSBridge。現(xiàn)在提供有兩種用法:

  • 簡(jiǎn)單用法

    這里適合一些簡(jiǎn)單場(chǎng)景下,Native調(diào)用與UI無(wú)關(guān)的JSBridge。直接調(diào)用[WVBridge callHandler:withParams:withCallback:]方法,傳入以下參數(shù):

    • name: 要執(zhí)行的JSBridge名稱(chēng),格式為@"類(lèi)名.方法名"。

    • params: 要執(zhí)行的JSBridge參數(shù),與JS使用方式保持一致 ,對(duì)象用NSDictionary代替,數(shù)組用NSArray代替)。

    • callback: 此次JSBridge調(diào)用的回調(diào),可能在任意線(xiàn)程調(diào)用。

  • 復(fù)雜用法

    這里適合Native調(diào)用全功能的JSBridge,可以用于在自己的容器中完整接入JSBridge。

    • 實(shí)現(xiàn)WVBridgeDelegate協(xié)議,供JSBridge回調(diào)數(shù)據(jù)給Native。

    • 實(shí)例化自己的WVBridge實(shí)例,傳入env和上面實(shí)現(xiàn)的delegate。

    • 設(shè)置 WVBridge 實(shí)例的view和viewController屬性,為JSBridgeAPI提供環(huán)境信息。其它屬性可以根據(jù)需要設(shè)置:

      • opageId:當(dāng)前頁(yè)面的ID,變化后前一頁(yè)面未調(diào)用的JSBridge會(huì)全部失效,避免影響到當(dāng)前頁(yè)面。

      • oopenPermissionCheck:?jiǎn)⒂冒酌麊涡r?yàn),只允許符合WindVane domain 配置中的Ali域名白名單的 URL 調(diào)用JSBridge,其它URL的調(diào)用都會(huì)被拒絕。

    • 調(diào)用callHandlerWithRequest:callHandlerWithURL: callHandler:withParams:withReqId:方法之一,就會(huì)調(diào)用實(shí)際的JSBridge,JSBridge的回調(diào)、事件都會(huì)通過(guò)delegate返回回來(lái)。

  • 標(biāo)準(zhǔn)事件機(jī)制

    WindVane的標(biāo)準(zhǔn)事件機(jī)制,提供了Native和H5之間互發(fā)事件的標(biāo)準(zhǔn)通訊接口,便于各業(yè)務(wù)場(chǎng)景下的基于統(tǒng)一的事件模型來(lái)通訊。

  • Native向H5發(fā)送事件

    WindVane提供了WVStandardEventCenter 類(lèi),供Native向H5發(fā)送事件:

    在事件參數(shù)中,WebView是要發(fā)送事件的目標(biāo)WebView,eventName是事件的名稱(chēng)(字符串),eventData是事件的數(shù)據(jù)(iOS是NSDictionary*,Android是一個(gè)JSON Object格式的String)。在iOS平臺(tái)的JSBridge中,不建議使用這里的方法來(lái)發(fā)送事件,而是使用JSBridge context的dispatchEvent方法來(lái)發(fā)送事件,對(duì)多種容器(WebView/Weex)提供了兼容。

    #import <WindVane-Basic/WVStandardEventCenter.h>
    // 向指定 WebView 發(fā)送事件。
    [WVStandardEventCenter postNotificationToJS:eventName withEventData:eventData withWebView:webView];
    // 向所有 WebView 發(fā)送事件,請(qǐng)謹(jǐn)慎使用。
    [WVStandardEventCenter postNotificationToJS:eventName withEventData:eventData];

    H5用法:

    document.addEventListener('eventName', function(data) {
    // 這里要注意,Native 傳遞過(guò)來(lái)的事件參數(shù)是在data的param屬性中。
    alert(data.param);
    }, false);
  • H5向Native發(fā)送事件

    H5用法:使用JSBridge來(lái)發(fā)送事件,注意引入windvane.js,詳情可以參考如下示例。

    WVStandardEventCenter.postNotificationToNative:
    var params = {
        event: 'eventName',
        param: {
            // 事件要傳遞的數(shù)據(jù)。
        }
    };
    
    window.WindVane.call('WVStandardEventCenter','postNotificationToNative',params, 
    function(e) {
        alert('success');
    }, function(e) {
        alert('failure: ' + JSON.stringify(e));
    });
    

    iOS用法:使用標(biāo)準(zhǔn)的消息中心監(jiān)聽(tīng)事件。

    [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(myEventListener) name:eventName object:nil]
    - (void)myEventListener:(NSNotification*)notification {
    
    // 事件參數(shù)可以從 notification.userInfo 中獲取。
    }