99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

CML SDK

2020-05-14 14:20 更新

Native SDK 目標(biāo)讓客戶(hù)端上趨近于各類(lèi)小程序引擎,使同一套代碼平滑在客戶(hù)端上運(yùn)行。

使用 CML 的渲染能力,需要集成對(duì)應(yīng)平臺(tái)的 SDK。請(qǐng)?jiān)谧髠?cè)目錄中根據(jù)你需要接入的平臺(tái)來(lái)查看文檔。

iOS SDK

CML iOS SDK 使用 Weex、React Native 與 WebView 作為基礎(chǔ)渲染引擎,提供了基礎(chǔ)的組件功能之外,還支持用戶(hù)擴(kuò)展自己的功能組件。

環(huán)境要求

CML 最低支持的 iOS deployment target 為:iOS 9.0 CML 使用Cocoapods進(jìn)行管理,使用npm管理react_native。

組件名依賴(lài)版本備注
Cocoapods1.3.1-
npm最新版本即可-
Weex SDK0.19.0.2-
React Native0.57.6-
React16.6.1-

詳細(xì)集成

當(dāng) SDK 下載下來(lái)后,首先進(jìn)入/chameleon-sdk-iOS/Chameleon/react_native,并運(yùn)行npm install進(jìn)行更新。(這也是 React Native 的更新辦法。)

接下來(lái)我們以 Demo 工程為例(要注意工程路徑位置,在工程實(shí)際配置中需要注意 :path 的內(nèi)容)。

在 Podfile 中,寫(xiě)入:

platform :ios, '9.0'

target 'Chameleon_Example' do

    ##CML        pod 'Chameleon', :path => '../Chameleon/'

    ## 如果需要Weex,則寫(xiě)入weex依賴(lài)。
    pod 'WeexSDK', '~> 0.19.0.2'

    ## 如果需要react_native,則寫(xiě)入react_native依賴(lài)。
    pod 'React', :path => '../Chameleon/react_native/node_modules/react-native', :subspecs => [
    'Core',
    'CxxBridge', ##  如果RN版本 >= 0.45則加入此行
    'DevSupport', ##  如果RN版本 >= 0.43,則需要加入此行才能開(kāi)啟開(kāi)發(fā)者菜單
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', ##  這個(gè)模塊是用于調(diào)試功能的
    ]

    pod 'yoga', :path => '../Chameleon/react_native/node_modules/react-native/ReactCommon/yoga'
    pod 'DoubleConversion', :podspec => '../Chameleon/react_native/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => '../Chameleon/react_native/node_modules/react-native/third-party-podspecs/GLog.podspec'
    pod 'Folly', :podspec => '../Chameleon/react_native/node_modules/react-native/third-party-podspecs/Folly.podspec'

end

將 podfile 保存,并運(yùn)行 pod install。

常見(jiàn)問(wèn)題

  • Multiple commands produce Showing All Messages :-1: Multiple commands produce 'XXXXX': 1) Target 'Chameleon_Example' has create directory command with output 'XXXXp' 2) That command depends on command in Target 'Chameleon_Example': script phase “[CP] Copy Pods Resources”

解決辦法:刪除 工程->Build Phrases->[CP] Copy Pods Resources->Output files下的 copy 路徑。

項(xiàng)目目錄

描述作用
CMLSDK 源碼與依賴(lài)文件夾
Examplereact_native 依賴(lài)

####CMLsdk_src

目錄功能描述
CMLSDKEngine 類(lèi)初始化 SDK、注冊(cè)自定義的 Module 等功能
CMLCommonCML 抽象層。抽象了基礎(chǔ)的渲染頁(yè)面、緩存、配置、預(yù)加載等功能。
CMLReactNative針對(duì) ReactNative 額外配置的部分
CMLWeex針對(duì) Weex 額外配置的部分
CMLWeb針對(duì) WebView 額外配置的部分

提供功能

Bundle 預(yù)加載

  1. 先設(shè)置預(yù)加載地址[CMLEnvironmentManage chameleon].weexService.config.prefetchContents = @[@"http%3A%2F%2F172.22.139.32%3A8000%2Fweex%2Fchameleon-bridge.js%3Ft%3D1546502643623"];
  2. 開(kāi)始預(yù)加載 [[CMLEnvironmentManage chameleon].weexService setupPrefetch];

自動(dòng)降級(jí)

當(dāng) Bundle 下載失敗、渲染出現(xiàn)嚴(yán)重錯(cuò)誤時(shí),會(huì)自動(dòng)降級(jí)至 H5。  降級(jí)設(shè)計(jì)圖

本地 bundle 降級(jí)

當(dāng) H5 渲染失敗時(shí),倘若設(shè)置了默認(rèn)的本地 bundle,會(huì)使用本地 bundle 進(jìn)行降級(jí)。

主動(dòng)降級(jí)

當(dāng)渲染出現(xiàn)錯(cuò)誤時(shí),F(xiàn)E 可以通過(guò) JSBridge 通知客戶(hù)端觸發(fā)降級(jí)。

功能擴(kuò)展

使用+ (void)registerModule:(NSString *)moduleName className:(NSString *)className;注冊(cè)自己的擴(kuò)展。

使用說(shuō)明

  1. 初始化 SDK[CMLSDKEngine initSDKEnvironment];
  2. 設(shè)置渲染引擎類(lèi)型[CMLEnvironmentManage chameleon].serviceType = CMLServiceTypeWeex;
  3. 設(shè)置預(yù)加載環(huán)境[CMLEnvironmentManage chameleon].weexService.config.prefetchContents = @[@"http%3A%2F%2F172.22.139.32%3A8000%2Fweex%2Fchameleon-bridge.js%3Ft%3D1546502643623"];
  4. 設(shè)置預(yù)加載[[CMLEnvironmentManage chameleon].weexService setupPrefetch];

Module 的使用

什么是 Module

module 是 Native 提供給前端頁(yè)面調(diào)用的,完成一組操作的方法集合,用于擴(kuò)展 Native 的能力。在 CML 頁(yè)面中,開(kāi)發(fā)者引入相關(guān) js 庫(kù)后即可調(diào)用 Module 中的方法。

import bridge from 'chameleon-bridge';

// 主動(dòng)調(diào)用客戶(hù)端方法
export function sayHello() {
    bridge.callNative(
        'moduleDemo', // 模塊名
        'sayHello', // 方法名
        {}, // 參數(shù)
        res => {} // 回調(diào)方法
    );
}

使用 Module

Module 的使用分兩種情況,一種是使用 CML SDK 內(nèi)置的 Module,一種是用戶(hù)自定義實(shí)現(xiàn)自己的 Module。

使用內(nèi)置的 Module

內(nèi)置的 Module 在 js 前端代碼里直接使用即可,目前內(nèi)置的 Module 有:

  • clipboard
  • cml
  • modal
  • storage
  • stream
  • webSocket

API 文檔里描述的能力,部分實(shí)現(xiàn)就是由上述 Module 支撐的。

自定義實(shí)現(xiàn)自己的 Module

示例可參看CMLStorageModule 示例

注冊(cè)自己的 Module 關(guān)聯(lián)文件:

  • CMLSDKEngine
  • CMLConstants
  • CMLUtility
  • CMLModuleProtocol 非必須(該協(xié)議可獲取 CMLInstance)

詳細(xì)說(shuō)明

  • 功能:通過(guò)注冊(cè) Module 提供原生能力的擴(kuò)展
  • 原理:依賴(lài) bridge 進(jìn)行協(xié)議通信,根據(jù)不同 Module 進(jìn)行協(xié)議處理分發(fā)
  • Module,擴(kuò)展原生能力Module 注冊(cè)[CMLSDKEngine registerModule:@"module名" className:@"類(lèi)名"]; Module 名:兩端及前端同學(xué)定義的一個(gè)名字Module 方法實(shí)現(xiàn) #import "CMLConstants.h" #import "CMLUtility.h" CML_EXPORT_METHOD(@selector(xxx:callBack:)) - (void)xxx:(NSDictionary *)param callBack:(CMLMoudleCallBack)callback { } xxx:方法名,協(xié)商定義,需要跟前端一致param:所帶參數(shù),字典類(lèi)型callback:回調(diào) block (非必須)遵循 CMLModuleProtocol 的作用CMLModuleProtocol 協(xié)議可獲取到 CMLInstance通過(guò) CMLInstance 可獲取當(dāng)前運(yùn)行環(huán)境、viewController

預(yù)加載和緩存

預(yù)加載和緩存概念

預(yù)加載和緩存都是為了節(jié)省 JSBundle 下載的時(shí)間,加快 UI 的渲染。

#預(yù)加載

預(yù)加載是將下載 JSBundle 的動(dòng)作提前完成,在需要用到的時(shí)候直接從本地讀取并渲染。實(shí)際項(xiàng)目使用中,可以將需要預(yù)加載的 url 配置到預(yù)加載地址列表里,在 app 啟動(dòng)時(shí)提前從服務(wù)端獲取,通過(guò) CML SDK 提供的預(yù)加載能力提前下載下來(lái)。

#緩存

對(duì)于沒(méi)有預(yù)加載的 JSBundle 在渲染前需要先下載,下載完成后 CML SDK 會(huì)緩存此 JSBundle,下次渲染同一個(gè) JSBundle 時(shí),如果此 JSBundle 沒(méi)有更新則不會(huì)下載新的,達(dá)到節(jié)省時(shí)間和流量提升渲染速度的目的。

CMLCache

CMLCache 是一個(gè)對(duì) js 進(jìn)行下載、緩存的一個(gè)模塊,根據(jù)協(xié)議來(lái)實(shí)現(xiàn) js 增量更新功能。主要有以下內(nèi)容

 |
 |——CMLWeexCache   緩存模塊接口類(lèi)
 |——CMLCacheInfo     JsBundle文件緩存實(shí)現(xiàn)邏輯
 |——CMLCacheItem    jsBundle文件內(nèi)存對(duì)象,描述每一個(gè)jsBundle文件的緩存狀態(tài)
 |——CMLConfig    配置類(lèi),業(yè)務(wù)方可通過(guò)這個(gè)類(lèi)設(shè)置是否開(kāi)啟緩存功能、預(yù)加載js路徑、緩存大小限制、默認(rèn)兜底頁(yè)鏈接等
CMLCache

對(duì) jsBundle 進(jìn)行預(yù)加載、獲取、緩存的處理對(duì)外接口。

在該文件里,我們將拿到的 URL 解析出此頁(yè)面需要加載的 jsbundle 標(biāo)識(shí),然后根據(jù) jsBundle 標(biāo)識(shí)來(lái)檢測(cè)是否在本地已預(yù)加載,如果此 jsBundle 已預(yù)加載成功,則直接讀本地緩存渲染;否則先從網(wǎng)絡(luò)下載 jsBundle,然后渲染并緩存本地。

后續(xù)我們將支持在一個(gè) URL 中下發(fā)多段 jsbundle 標(biāo)識(shí),每段 jsbundle 標(biāo)識(shí)代表這個(gè)頁(yè)面的一部分,然后在根據(jù)每段 jsbundle 標(biāo)識(shí),分別從本地緩存里獲取去尋找對(duì)應(yīng)的 js 代碼,如果不存在則從網(wǎng)絡(luò)去下載這一段 jsBundle 并保存在本地,然后 SDK 中會(huì)將最終得到的多段 jsBundle,組合成一個(gè)完整頁(yè)面的 jsBundle 加載出來(lái)。

CMLCacheInfo

jsBundle 的內(nèi)存管理器,是加載、獲取、緩存等處理的實(shí)際操作者。 對(duì)本地緩存 jsBundle 的 maxSize 加以限制,如果超過(guò) maxSize,則優(yōu)先清除老的 jsBundle 緩存

CMLCacheItem

對(duì) jsBundle 的封裝,包括 jsBundle 文件在本地存儲(chǔ)的路徑、內(nèi)存索引;CMLCacheInfo 就是通過(guò) CMLCacheItem 來(lái)對(duì) jsBundle 進(jìn)行操作。

使用(以 Weex 為例)

緩存相關(guān)配置

緩存相關(guān)配置定義位于 CMLWeexConfig (CMLConfig)

//設(shè)置服務(wù)類(lèi)型為weex
[CMLEnvironmentManage chameleon].serviceType = CMLServiceTypeWeex;
//設(shè)置默認(rèn)錯(cuò)誤web鏈接
[CMLEnvironmentManage chameleon].weexService.config.defaultErrUrl = @“defaultErrUrl”

Chameleon 功能和緩存功能都是默認(rèn)開(kāi)啟的,如果有特殊需要,可以手動(dòng)關(guān)閉;另外在這里還有緩存限制 maxSize、緩存目錄等配置

[CMLEnvironmentManage chameleon].weexService.config.isFeatureAvailable = NO;
[CMLEnvironmentManage chameleon].weexService.config.isEnableCacheFeature = NO;

預(yù)加載

//設(shè)置預(yù)加載 URL 列表

 [CMLEnvironmentManage chameleon].weexService.config.prefetchContents = @[@"http%3A%2F%2F172.22.139.32%3A8000%2Fweex%2Fchameleon-bridge.js%3Ft%3D1546502643623"];
 //開(kāi)啟預(yù)加載
 [[CMLEnvironmentManage chameleon].weexService setupPrefetch];

獲取加載 jsBundle 的 URL

CMLWeexCache *cache = (CMLWeexCache *)[CMLEnvironmentManage chameleon].weexService.cache;
  //在緩存中獲取JSBundle的URL(本地有緩存則獲取到本地緩存的URL,本地?zé)o緩存則獲取到遠(yuǎn)端的URL)
        [cache getBundleCacheOfJSBundleUrl:self.bundleUrl completion:^(NSString *url, NSDictionary *parameter) {
  //加載jsBundle
  [self.render renderWithURL:[NSURL URLWithString:url] options:@{@"query" : [param copy]} data:nil];
}

Android SDK

Github 地址

CML Android SDK 是 CML 整體框架的一部分,主要任務(wù)是完成 CML JsBundle 在 Android 端的本地渲染。SDK 底層采用 Weex 作為渲染引擎,同時(shí)擴(kuò)展一些一般工程通用的基礎(chǔ)能力,如緩存能力、降級(jí)能力等。

項(xiàng)目結(jié)構(gòu)

項(xiàng)目一級(jí)目錄結(jié)構(gòu)如下:

|+ app SDK使用示例
|+ cmlsdk SDK接入層,抽象 CML 引擎能力、實(shí)現(xiàn)通用擴(kuò)展能力
|+ cmlweex 包裝 Weex 渲染引擎
|+ cmlweb 包裝 Web 渲染引擎
|+ js-bundle-mgr 實(shí)現(xiàn) js bundle 預(yù)加載、緩存
|+ rich-text-component 富文本組件
|+ sdk-image 圖片選擇、圖片拍攝組件
|+ sdk-location 位置組件

cmlsdk 模塊單獨(dú)拿出來(lái)看下目錄結(jié)構(gòu):

|- cmlsdk
    |+ adapter 定義了擴(kuò)展能力的接口以及默認(rèn)實(shí)現(xiàn),無(wú)默認(rèn)實(shí)現(xiàn)的能力需要第三方項(xiàng)目根據(jù)自己的實(shí)際業(yè)務(wù)需求去實(shí)現(xiàn)
    |+ bridge 定義了 js 和 Native 通信的接口,實(shí)現(xiàn)協(xié)議相關(guān)的處理能力,以及實(shí)現(xiàn)了協(xié)議層使用入口
    |+ bundle js bundle 相關(guān)定義,目前只有一個(gè)類(lèi)用來(lái)描述 js bundle 相關(guān)信息
    |+ common 通用能力的基礎(chǔ)封裝類(lèi)
    |+ container 渲染容器的抽象能力定義
    |+ extend CML 提供的一些能力
    |+ Module 擴(kuò)展能力管理,收集 sdk 默認(rèn)提供的以及第三方用戶(hù)自己實(shí)現(xiàn)的 Module,根據(jù) bridge 層指令執(zhí)行具體某個(gè) Module 的某個(gè) method
    |+ utils 工具類(lèi)集合
    |+ widget 自定義的widget,目前只有一個(gè) title bar,用做 webview 渲染容器的action bar
    |- CmlBaseLifecycle 生命周期的接口定義
    |- CmlConstant 常量定義
    |- CmlEngine CML SDK 使用入口
    |- CmlEnvironment 運(yùn)行環(huán)境和運(yùn)行參數(shù)配置入口、擴(kuò)展能力設(shè)置入口
    |- CmlInstanceManage 頁(yè)面運(yùn)行實(shí)例的管理類(lèi),每一個(gè)容器實(shí)例運(yùn)行時(shí),其對(duì)應(yīng)的Instance會(huì)注冊(cè)到這里
    |- ICmlEngine 引擎的抽象接口
    |- ICmlInstance 容器實(shí)例抽象接口
    |- ICmlActivityInstance 全屏容器實(shí)例抽象接口
    |- ICmlViewInstance 視圖容器實(shí)例抽象接口

CML 使用

CML Android SDK 的使用步驟如下:

  • 引用 CML 及工程需要的相關(guān)類(lèi)庫(kù)
  • 在項(xiàng)目中初始化 CML SDK
  • 加載 JS Bundle

基礎(chǔ)類(lèi)說(shuō)明

CmlEngine

此類(lèi)是 Chameleon/k??mi?l??n/ SDK 的入口類(lèi),提供基本的初始化入口和 Chameleon容器的調(diào)起能力。具體包含以下能力

  • SDK 初始化入口
  • 調(diào)起渲染容器
  • 初始化預(yù)加載列表
  • 注冊(cè)擴(kuò)展 Module

CmlEnvironment

CmlEnvironment 主要提供了開(kāi)發(fā)期間需要的一些能力,如

  • 調(diào)試開(kāi)關(guān)
  • 降級(jí)開(kāi)關(guān)
  • 緩存開(kāi)關(guān)

以及一些常量的定義,如

  • 預(yù)加載的最大緩存
  • 運(yùn)行時(shí)的最大緩存
  • 各種能力的自定義實(shí)現(xiàn)配置入口

富文本組件

富文本是 CML 里唯一一個(gè)默認(rèn)注冊(cè)的組件,主要有以下內(nèi)容

 |
 |-richinfo     主要是富文本需要定義的協(xié)議、點(diǎn)擊事件的回調(diào)等
 |-utils    工具類(lèi),主要是加載assets下默認(rèn)的字體
 |-CmlRichTextComponent     繼承與與CmlComponent的富文本組件
 |-CmlRichTextEngine     富文本入口類(lèi),

richinfo

  • CmlClickSpanListener:富文本點(diǎn)擊事件回調(diào)
  • CmlCustomTypefaceSpan:加載自定義字體
  • CmlRichInfo:富文本協(xié)議及實(shí)現(xiàn)
  • CmlRichInfoSpan:配合 CmlRichInfo 顯示富文本文字,可直接設(shè)置給 TextView

utils

  • CmlFontUtil:主要就是加載自定義字體,如 assets 下 fonts 包下的 Barlow-Medium.ttf 字體

Module 的使用

github 地址點(diǎn)這里

根目錄 assets 目錄下的 cml-demo-say.zip 是個(gè)簡(jiǎn)單的示例工程,用來(lái)演示 Native 和 Weex 容器或 Web 容器的雙向通信

什么是 Module

module 是 Native 提供給前端頁(yè)面調(diào)用的,完成一組操作的方法集合,用于擴(kuò)展 Native 的能力。在 CML 頁(yè)面中,開(kāi)發(fā)者引入相關(guān) js 庫(kù)后即可調(diào)用 Module 中的方法。

import bridge from 'chameleon-bridge';

// 主動(dòng)調(diào)用客戶(hù)端方法
export function sayHello() {
    bridge.callNative(
        'moduleDemo', // 模塊名
        'sayHello', // 方法名
        {}, // 參數(shù)
        res => {} // 回調(diào)方法
    );
}

使用 Module

Module 的使用分兩種情況,一種是使用 CML SDK 內(nèi)置的 Module,一種是用戶(hù)自定義實(shí)現(xiàn)自己的 Module。

使用內(nèi)置的 Module

內(nèi)置的 Module 在 js 前端代碼里直接使用即可,目前內(nèi)置的 Module 有:

  • clipboard
  • cml
  • modal
  • storage
  • stream
  • webSocket

API 里描述的能力,部分實(shí)現(xiàn)就是由上述 Module 支撐的。

自定義實(shí)現(xiàn)自己的 Module

module 擴(kuò)展 3 個(gè)重要的注解

  • @CmlModule 標(biāo)注這個(gè)類(lèi)是擴(kuò)展模塊
  • @CmlMethod 標(biāo)注可供 JS 側(cè)調(diào)用的方法
  • @CmlParam 標(biāo)注調(diào)用的參數(shù)

詳細(xì)說(shuō)明

  • 功能:通過(guò)注冊(cè) Module 提供原生能力的擴(kuò)展
  • 原理:依賴(lài) bridge 進(jìn)行協(xié)議通信,根據(jù)不同 Module 進(jìn)行協(xié)議處理分發(fā)
  • Module,擴(kuò)展原生能力Module 注冊(cè)必須注冊(cè) CmlEngine.registerModule(Class<?> moduleClass)不強(qiáng)制要求添加@CmlModule,未添加時(shí)會(huì)使用默認(rèn)設(shè)置不建議在運(yùn)行中動(dòng)態(tài)注冊(cè) ModuleModule 名稱(chēng)默認(rèn)使用 Module 的類(lèi)名配置 Module 名稱(chēng),添加注解@CmlModule(alias = "name")Module 實(shí)例默認(rèn)為實(shí)例全局唯一,即無(wú)論有多少 instance 都會(huì)使用同一個(gè) Module 實(shí)例配置全局性,添加注解@CmlModule(global = false)Module 組合針對(duì)極特殊情況,允許多個(gè) class 共用一個(gè) Module 名稱(chēng)必須有且只有一個(gè) class 作為 Module,所有相關(guān) class 均會(huì)使用該 moduel 配置其余 class 必須使用@CmlJoin(name = "name"),指定需要關(guān)聯(lián)的 moduel 名稱(chēng)每個(gè) class 實(shí)例之間無(wú)關(guān)聯(lián),僅會(huì)在使用時(shí)再創(chuàng)建實(shí)例
  • method,提供原生能力方法method 注冊(cè)自動(dòng)注冊(cè) Module 類(lèi)中所有的 public 方法不強(qiáng)制要求添加@CmlMethod,未添加時(shí)會(huì)使用默認(rèn)設(shè)置如果不希望方法被誤添加,需要在方法上添加@CmlIgnoremethod 名稱(chēng)默認(rèn)使用 method 方法名配置 method 名稱(chēng),添加注解@CmlMethod(alias = "name")method 線程默認(rèn)運(yùn)行在主線程配置 method 線程,添加注解@CmlMethod(uiThread = false)
  • param,原生能力方法所需要的參數(shù)param 類(lèi)型針對(duì) Context、ICmlInstance 等上下文類(lèi)型,會(huì)根據(jù)調(diào)用環(huán)境進(jìn)行查找替換對(duì)于 CmlCallback 的類(lèi)型,會(huì)構(gòu)建對(duì)應(yīng)的回調(diào),需要自行處理回調(diào)其余類(lèi)型會(huì)根據(jù) bridge 傳遞的參數(shù)進(jìn)行處理param 參數(shù)根據(jù) birdge 傳遞的數(shù)據(jù),根據(jù)參數(shù)類(lèi)型進(jìn)行轉(zhuǎn)化目前可轉(zhuǎn)化的類(lèi)型為 JSONObject、String如果要直接轉(zhuǎn)為對(duì)象,需要設(shè)置 CmlJsonAdapter 或接入相應(yīng) json 庫(kù)param 字段只想獲取傳遞數(shù)據(jù)中的某一個(gè)對(duì)象時(shí),可以使用@CmlParam添加@CmlParam(name = "name"),設(shè)置該參數(shù)獲取的字段添加@CmlParam(admin = "admin"),設(shè)置該參數(shù)默認(rèn)值

Adapter 的使用

初步認(rèn)識(shí) Adapter

先看個(gè)例子,對(duì) Adapter 有個(gè)直觀印象和基本概念。Chameleon SDK 里打印日志使用的是默認(rèn)的 android.util.Log, 如果想替換它可以按照如下步驟實(shí)行:

替換和注冊(cè)

如果用戶(hù)想替換 SDK 默認(rèn)提供的日志打印,可以實(shí)現(xiàn) CmlLoggerAdapter 接口,并按如下方式注冊(cè)進(jìn) SDK:

// 接口實(shí)現(xiàn)
public class MyLoggerDefault implements CmlLoggerAdapter {
	@Override
    public void d(String tag, String msg) {
        // 這里實(shí)現(xiàn)自己的日志打印
    }
	...
}

// 接口注冊(cè)
public class MyApplication extends Application implements ICmlConfig {
    @Override
    public void onCreate() {
        super.onCreate();
        CmlEngine.getInstance().init(this, this);
    }

    @Override
    public void configAdapter() {
        CmlEnvironment.setLoggerAdapter(new MyLoggerDefault()); // 注冊(cè)自己的Adapter
        ...
    }
	...
}

以上就完成了日志打印能力的替換。

原理說(shuō)明

SDK Adapter 定義和默認(rèn)實(shí)現(xiàn)如下:

// 日志接口定義
public interface CmlLoggerAdapter {
	void d(String tag, String msg);
	...
}

// 日志接口默認(rèn)實(shí)現(xiàn)
public class CmlLoggerDefault implements CmlLoggerAdapter {
	@Override
    public void d(String tag, String msg) {
        Log.d(tag, msg);
    }
	...
}

如果用戶(hù)注冊(cè)了自己的 Log Adapter 實(shí)現(xiàn)則優(yōu)先使用,否則使用 SDK 默認(rèn)提供的實(shí)現(xiàn)。

日志打印的使用

日志打印通過(guò) CmlLogUtil 類(lèi)調(diào)用,注冊(cè)自己的 Logger Adapter 后,打印日志的相關(guān)方法就會(huì)回調(diào)到自定義的方法實(shí)現(xiàn)里,使用示例:

    // 日志打印
    public void launchPage(@NonNull Activity activity, String url, HashMap<String, Object> options) {
        if (TextUtils.isEmpty(url)) {
            CmlLogUtil.e(TAG, "CmlEngine launchPage, url is empty.");
            return;
        }
        ...
    }

Adapter 基本概念

Adapter 的目的是定義一系列能力接口來(lái)隔離具體的實(shí)現(xiàn),方便 SDK 使用者在需要時(shí)靈活替換成自己的實(shí)現(xiàn)。Chameleon SDK 框架層在使用 Adapter 相關(guān)能力時(shí)都是面向接口的,使用者只需要實(shí)現(xiàn)相關(guān)能力的 Adapter 接口并通過(guò) SDK 注冊(cè)接口進(jìn)行注冊(cè),即可輕松替換成自己的實(shí)現(xiàn)并進(jìn)行能力擴(kuò)展。

Chameleon SDK 并沒(méi)有完整的實(shí)現(xiàn)所有 Adapter 接口,也就是說(shuō)一部分有默認(rèn)實(shí)現(xiàn)的 Adapter 可以直接使用,未提供默認(rèn)實(shí)現(xiàn)的需要使用者自己實(shí)現(xiàn),否則框架將無(wú)法使用對(duì)應(yīng)的接口能力。

Chameleon SDK 定義了如下的 Adapter 接口

接口功能默認(rèn)實(shí)現(xiàn)
ICmlDegradeAdapter降級(jí)無(wú)
ICmlImgLoaderAdapter圖片加載
CmlLoggerAdapter日志
ICmlNavigatorAdapterurl 跳轉(zhuǎn)
ICmlStatisticsAdapter統(tǒng)計(jì)信息輸出無(wú)
ICmlWebSocketAdapterWebSocket
CmlHttpAdapterHttp 請(qǐng)求
CmlJsonAdapterjson 解析
CmlDialogAdapter對(duì)話(huà)框
CmlToastAdapter提示浮層
CmlStorageAdapterkey->value 存儲(chǔ)
CmlThreadAdapter線程
重點(diǎn) Adapter 說(shuō)明

降級(jí)、對(duì)話(huà)框、提示浮層 Adapter 在 SDK 實(shí)際使用時(shí)替換可能性較大,分別說(shuō)明。

降級(jí)

ICmlDegradeAdapter 降級(jí)接口沒(méi)有提供默認(rèn)實(shí)現(xiàn) CmlDegradeDefault 默認(rèn)會(huì)關(guān)閉 Native 渲染容器,并打開(kāi) Web 容器加載降級(jí) url。

public class CmlDegradeDefault implements ICmlDegradeAdapter {

    @Override
    public DegradeViewWrapper getDegradeView(int degradeCode) {
        return new DegradeViewWrapper() {
            CmlWebView webView;

            @Override
            public View getView(@NonNull Context context) {
                webView = new CmlWebView(context);
                webView.onCreate();
                return webView;
            }

            @Override
            public void onDestroy() {
                if (null != webView) {
                    webView.onDestroy();
                }
            }

            @Override
            public void loadURL(@NonNull Context context, @NonNull String url, @Nullable HashMap<String, Object> options) {
                if (null != webView) {
                    webView.render(url, null);
                }
            }
        };
    }

    @Override
    public void degradeActivity(@NonNull Activity activity, @NonNull String url, @Nullable HashMap<String, Object> options, int degradeCode) {
        if (url.contains("?")) {
            url = url.substring(0, url.indexOf("?"));
        }
        CmlEngine.getInstance().launchPage(activity, url, null);
    }
}

degradeActivity 會(huì)在如下降級(jí)場(chǎng)景發(fā)生時(shí)回調(diào)

  • 下載 JSBundle 失敗
  • 解析 JSBundle 發(fā)生異常
  • 降級(jí)調(diào)試開(kāi)關(guān)打開(kāi)(在 CmlEnvironment 里設(shè)置)
  • 前端代碼手動(dòng)降級(jí)

對(duì)話(huà)框

此接口定義以下兩種對(duì)話(huà)框能力

  • showAlert
  • showConfirm

CmlModalTip 實(shí)現(xiàn)了此接口,通過(guò) CmlModalModule 類(lèi)暴露給 JS 側(cè)調(diào)用,前端用法參考 API交互反饋

提示浮層

此接口定義以下浮層提示能力

  • showToast

CmlModalTip 實(shí)現(xiàn)了此接口,通過(guò) CmlModalModule 類(lèi)暴露給 JS 側(cè)調(diào)用,前端用法參考 API交互反饋

#其他 Adapter 說(shuō)明

圖片加載

CmlDefaultImgLoaderAdapter ,默認(rèn)使用 Glide,需要用戶(hù)手動(dòng)集成 Glide

日志打印

CmlLoggerDefault,默認(rèn)使用系統(tǒng) log 輸出

跳轉(zhuǎn)

默認(rèn)使用 Intent.ACTION_VIEW 處理

統(tǒng)計(jì)信息輸出

沒(méi)有默認(rèn)實(shí)現(xiàn),不關(guān)心可以不用實(shí)現(xiàn)

WebSocket

CmlDefaultWebSocketAdapter,默認(rèn)使用 OkHttp3,需要用戶(hù)手動(dòng)集成 OkHttp3

Http 請(qǐng)求

執(zhí)行 http 請(qǐng)求,并監(jiān)聽(tīng) http 響應(yīng)

json 解析

轉(zhuǎn)換成 json 字符串和反解成 json 對(duì)象

key->value 存儲(chǔ)

線程

定義工作線程和 ui 線程

預(yù)加載和緩存

預(yù)加載和緩存概念

預(yù)加載和緩存都是為了節(jié)省 JSBundle 下載的時(shí)間,加快 UI 的渲染。

預(yù)加載

預(yù)加載是將下載 JSBundle 的動(dòng)作提前完成,在需要用到的時(shí)候直接從本地讀取并渲染。實(shí)際項(xiàng)目使用中,可以將需要預(yù)加載的 url 地址列表在 app 啟動(dòng)時(shí)提前從服務(wù)端獲取,通過(guò) CML SDK 提供的預(yù)加載能力提前下載下來(lái)。

緩存

對(duì)于沒(méi)有預(yù)加載的 JSBundle 在渲染前需要先下載,下載完成后 CML SDK 會(huì)緩存此 JSBundle,下次渲染同一個(gè) JSBundle 時(shí),如果此 JSBundle 沒(méi)有更新則不會(huì)下載新的,達(dá)到節(jié)省時(shí)間和流量提升渲染速度的目的。

sBundleMgr

JsBundleMgr 是一個(gè)對(duì) js 進(jìn)行下載、緩存的一個(gè)模塊,根據(jù)協(xié)議來(lái)實(shí)現(xiàn) js 增量更新功能。主要有以下內(nèi)容

 |
 |——cache    基于DiskLrucache來(lái)實(shí)現(xiàn)緩存功能
 |——code     js代碼的獲取及管理
 |——net      采用httpUrlConnect實(shí)現(xiàn)下載功能
 |——utils    工具包
 |——CmlJsBundleConstant  常量的管理
 |——CmlJsBundleEngine    實(shí)現(xiàn)了CmlJsBundleManager接口,入口類(lèi)
 |——CmlJsBundleEnvironment   當(dāng)前環(huán)境的設(shè)置,如debug環(huán)境等
 |——CmlJsBundleManager   實(shí)現(xiàn)此接口可自己定義JsBundle的管理
 |——CmlJsBundleMgrConfig    配置類(lèi),設(shè)置預(yù)加載js路徑、緩存大小等,默認(rèn)預(yù)加載及運(yùn)行時(shí)緩存大小是4M,可自行設(shè)置
code

對(duì) js 代碼進(jìn)行預(yù)加載、獲取、緩存的管理。在該包里,我們將拿到的 url 根據(jù)協(xié)議來(lái)拆分成多個(gè) url1、url2 等,然后在根據(jù) url1、url2 等來(lái)獲取對(duì)應(yīng)的 js 代碼,首先從本地緩存里獲取去尋找對(duì)應(yīng)的 js 代碼,如果不存在則從網(wǎng)絡(luò)去下載并保存在本地

utils

一些文件管理、拆分 url、網(wǎng)絡(luò)判斷的工具類(lèi)

  • CmlCodeUtils:獲取到的 url、code 的拆解及合并
  • CmlFileUtils:sd 卡及緩存目錄的判斷
  • CmlLogUtils:Log 的實(shí)現(xiàn)
  • CmlNetworkUtils:當(dāng)前網(wǎng)絡(luò)狀態(tài)的判斷,如 Wi-Fi、4g 等
  • CmlUtils:Md5 的生成、主線程判斷等等
CmlJsBundleConstant

緩存文件名、預(yù)加載優(yōu)先級(jí)的管理,預(yù)加載優(yōu)先級(jí)有以下三種類(lèi)型

  • 普通(PRIORITY_COMMON):非 Wi-Fi 情況不預(yù)加載
  • 強(qiáng)預(yù)加載(PRIORITY_FORCE):無(wú)論什么網(wǎng)絡(luò)情況都預(yù)加載
  • 強(qiáng)預(yù)加載+預(yù)解析(PRIORITY_FORCE_MAX):目前未用到
CmlJsBundleEngine

實(shí)現(xiàn)了 CmlJsBundleManager 接口,主要有以下三個(gè)方法

  • initConfig(Context,CmlJsBundleMgrConfig):初始化 config,主要是設(shè)置預(yù)加載 url、預(yù)加載緩存、運(yùn)行時(shí)緩存的設(shè)置,預(yù)加載及運(yùn)行時(shí)緩存默認(rèn)為 4M
  • startPreload():開(kāi)始預(yù)加載,目前預(yù)加載成功或者失敗并沒(méi)有任何信息返回,只能查看 log 進(jìn)行分析
  • getWXTemplate(String,CmlGetCodeStringCallback):獲取 js 代碼
CmlJsBundleManager

實(shí)現(xiàn)此接口可以自己定義 JsBundleMgr 的實(shí)現(xiàn)

使用

添加依賴(lài)

compile 'com.didiglobal.chameleon:js-bundle-mgr:latest.version'

預(yù)加載

        CmlJsBundleEnvironment.DEBUG = true;
        List<CmlModel> cmlModels = new ArrayList<>();
        CmlModel model = new CmlModel();
        model.bundle = CmlUtils.parseWeexUrl(url1);
        model.priority = 2;
        cmlModels.add(model);
        model = new CmlModel();
        model.priority = 2;
        model.bundle = CmlUtils.parseWeexUrl(url2);
        cmlModels.add(model);
        CmlJsBundleMgrConfig config = new CmlJsBundleMgrConfig.Builder().setPreloadList(cmlModels).build();
        CmlJsBundleEngine.getInstance().initConfig(this, config);
        CmlJsBundleEngine.getInstance().startPreload();

獲取 Js 代碼

        CmlJsBundleEngine.getInstance().initConfig(this, new CmlJsBundleMgrConfig.Builder().build());
        String url = CmlUtils.parseWeexUrl(url);
        CmlJsBundleEngine.getInstance().getWXTemplate(url, new CmlGetCodeStringCallback() {
            @Override
            public void onSuccess(String codes) {
                Log.i(TAG, "onSuccess: " + codes);
            }

            @Override
            public void onFailed(String errMsg) {
                Log.i(TAG, "onFailed: " + errMsg);
            }
        });

SDK 獨(dú)有方法

getSDKInfo

獲得 SDK 信息

參數(shù)

無(wú)

返回值

返回 promise

返回值類(lèi)型說(shuō)明
versionString版本號(hào)

inSDK

同步方法,判斷 webview 或 Native 頁(yè)面是否在 sdk 環(huán)境中,目前只用于內(nèi)部封裝方法使用。

參數(shù)

無(wú)

返回值

返回值類(lèi)型說(shuō)明
valueBooleantrue:在 sdk 環(huán)境中;false:不在 sdk 環(huán)境中
import bridge from 'chameleon-bridge';
const inSDK = bridge.inSDK(); // true/false

rollbackWeb

降級(jí)到 cmlUrl 對(duì)應(yīng)的 h5 地址。

callNative(module:String, method:String, args:Object, callback:Function)

js 調(diào)用 Native sdk

import bridge from 'chameleon-bridge';

// 主動(dòng)調(diào)用客戶(hù)端方法
export function sayHello() {
    bridge.callNative(
        'moduleDemo', // 模塊名
        'sayHello', // 方法名
        {}, // 參數(shù)
        res => {} // 回調(diào)方法
    );
}

listenNative(module:String, method:String, callback:Function)

監(jiān)聽(tīng)客戶(hù)端調(diào)用 js

import bridge from 'chameleon-bridge';

// 監(jiān)聽(tīng)客戶(hù)端調(diào)用js
export function listenTell() {
    bridge.listenNative(
        'moduleDemo', // 模塊名
        'NaTellJS', // 方法名
        res => {
         // 回調(diào)方法中處理返回的數(shù)據(jù)
        }
    );
}

端 JS 包緩存、更新、預(yù)加載

緩存策略

瀏覽器在加載靜態(tài)資源的時(shí)候一般的話(huà)會(huì)使用兩種 HTTP 緩存管理機(jī)制:

  • 強(qiáng)制緩存(Cache-Control、Expires)
  • 協(xié)商式緩存(ETag、Last-Modified)

類(lèi)似的,在使用chameleon sdk加載 JS 包的時(shí)候也會(huì)提供兩種緩存管理機(jī)制:

  • 基于 LRU 的強(qiáng)制緩存
  • 類(lèi) HTTP 的協(xié)商式緩存(后續(xù)發(fā)布)

基于 LRU 的強(qiáng)制緩存

基于LRU的緩存策略,簡(jiǎn)單來(lái)說(shuō)就是實(shí)現(xiàn)了一個(gè)緩存池,每次請(qǐng)求先從緩存池中搜索一下,如果有就直接使用緩存池中的 JS 包,如果沒(méi)有,就從網(wǎng)絡(luò)上請(qǐng)求 JS 包并將其緩存在緩存池中,每一份 JS 包緩存按照最后使用時(shí)間排序,當(dāng)緩存池滿(mǎn)了以后,將最早使用過(guò)的緩存從緩存池中清理出去,保證客戶(hù)端上的資源占用可控。

如何配置
  • iOS
  • Android

類(lèi) HTTP 的協(xié)商式緩存(后續(xù)發(fā)布)

與普通瀏覽器實(shí)現(xiàn)的協(xié)商式緩存類(lèi)似,用戶(hù)只需在靜態(tài)資源服務(wù)端配置好靜態(tài)資源的協(xié)商式緩存頭部,即可實(shí)現(xiàn)與普通瀏覽器一致的方式使用協(xié)商式緩存。

更新策略

當(dāng) JS 包升級(jí)迭代需要在客戶(hù)端內(nèi)使用最新的包時(shí),在使用兩種緩存方式下,相應(yīng)的存在兩周更新方式,下面會(huì)詳細(xì)介紹

#強(qiáng)制緩存下的更新:

由于強(qiáng)制緩存,客戶(hù)端不會(huì)主動(dòng)的去向服務(wù)器請(qǐng)求最新的更新包,會(huì)導(dǎo)致客戶(hù)端一直使用老的版本。

為了能夠打到規(guī)避這種情況,我們提供了一種可供參考的解決方案:

  1. 配置文件指紋

設(shè)置chameleon.config.js中的hash: true,具體可參考工程化配置之文件指紋, CML 項(xiàng)目構(gòu)建出的 JS 包文件名會(huì)類(lèi)似如下

test_project_c6bdf9074a821f01e70f.js
  1. 上線打包出來(lái)的 JS 包

得到以下可以訪問(wèn)的資源地址

https://www.static.com/test_project_c6bdf9074a821f01e70f.js
  1. 替換入口資源地址

將入口頁(yè)面中的 cmlUrl cml_addr 替換成 encodeURIComponent 后的最新資源地址即可,比如

原 cmlUrl:

https://www.static.com/test_project.html?cml_addr=https%3A%2F%2Fwww.static.com%2Ftest_project_21f01e70fc6bdf9074a8.js

新的 cmlUrl

https://www.static.com/test_project.html?cml_addr=https%3A%2F%2Fwww.static.com%2Ftest_project_c6bdf9074a821f01e70f.js

最佳實(shí)踐

由于強(qiáng)制緩存下每次修改都需要修改入口頁(yè)面的 cmlUrl 中的 cml_addr 參數(shù),可能會(huì)導(dǎo)致修改頻繁影響效率,所以建議通過(guò)后端讀取 map.json 的方式下發(fā)跳轉(zhuǎn) cmlUrl 進(jìn)行統(tǒng)一管理。

具體請(qǐng)求過(guò)程如下圖所示:

示意圖

  1. JS 包修改上線后,同時(shí)將map.json上線到服務(wù)器上
  2. 入口頁(yè)面加載數(shù)據(jù)時(shí),服務(wù)器從map.json文件中查詢(xún)到要跳轉(zhuǎn)的 cmlUrl,并與初始化數(shù)據(jù)合并和下發(fā)給入口頁(yè)面。
  3. 入口頁(yè)面邏輯將接受到的 cmlUrl 作為需要跳轉(zhuǎn)的鏈接進(jìn)行使用。

這樣每次 bundle 修改后可以自動(dòng)完成更新

協(xié)商式緩存下的更新:

可以直接使用 http 的緩存更新策略,不需額外的配置。

預(yù)加載

在某些場(chǎng)景下,為了能夠讓頁(yè)面更快的呈現(xiàn)在用戶(hù)面前,需要讓客戶(hù)端提前下載一些 js 包,這時(shí)就需要用到預(yù)加載。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)