百度小程序逆向分析1 - 初步分析 百度小程序
导读
背景
-
回顾2018.目前.小程序大概有几家比较主流的,微信,百度,支付宝,头条…..微信小程序,暂时在小程序被使用体量最大.另外,还有类似的安卓九大厂商联合的轻应用.从种种迹象表明,基于js的动态化,占用比重越来越大,app原生需求,可能会进一步下降.这里刚好有时间,就从原理上,了解一ios端上的小程序,实现逻辑.
-
这里选择逆向百度,研究一下小程序的实现.本来是打算使用微信的,奈何修改bundle id后,登录了一次就提示微信号违规,有被封可能.而头条的,ios端刚好又被下架了.
-
下文所说的 masterWebView或逻辑容器等价,slaveWebView或渲染容器等价,逻辑容器主要处理逻辑,渲染容器只做界面处理,这里可能有部分观点描述不恰当,请见谅…
需要技能
-
1.js基础,ios开发基础.
-
2.会使用Xcode,safari,有基础js编写能力.
逆向ipa
-
这里不细说了,使用monkeydev,非常简单.上网资源一搜一大堆,另外,本人之前也写了一个简单的使用说明.
-
另外,再次集成了flex,这个机器上调试神器.
从小程序角度分析
获取具体页面js,css,swan,json文件 (这里文件命名只对应,百度小程序,其他小程序估计可以类比)
- app使用felx查看路径
- 导出到mac后的汇总路径
分析文件结构
初步分析目录结构
- 初步目录分析,其实从这里看,每个详情页的js代码,都被打包到一个,pages.js文件,这里可以猜测,百度小程序的逻辑都是单独在一个wkwebview,运行的,下面还有详细说明,证实这个情况.
从page.js文件,思考情页逻辑引入机制
- 其实从这个文件可以看出,每个详情页的处理逻辑都通过这个引入,封装引入通过一个JSCore调度,实现与原生交互,window.define这部分,由于本人js只懂皮毛,初步整理一下整理一下,以后再深究 另外,我猜猜其中的n是指swan的全局对象
window.define("pages/custom/custom", function (t, e, a, o, n, s, i, d, r, c, u, l, f, h, g, m, p)
{
//省略一万字
}),
, window.define("pages/custom/custom", function (t, e, a, o, n, s, i, d, r, c, u, l, f, h, g, m, p) {
//省略一万字
})
- page.js文件,末尾是一个全局路由表,猜测这个是用来做做页面跳转时,切换逻辑处理的索引
window.__swanRoute = "pages/index/index", window.usingComponents = [], require("pages/index/index"),
window.__swanRoute = "pages/custom/custom", window.usingComponents = [], require("pages/custom/custom"),
window.__swanRoute = "pages/search/search", window.usingComponents = [], require("pages/search/search"),
window.__swanRoute = "pages/detail/detail", window.usingComponents = [], require("pages/detail/detail"),
window.__swanRoute = "pages/mycolumn/mycolumn", window.usingComponents = [], require("pages/mycolumn/mycolumn"),
window.__swanRoute = "pages/hotspotdetail/hotspotdetail", window.usingComponents = [], require("pages/hotspotdetail/hotspotdetail"),
window.__swanRoute = "pages/specialdetail/specialdetail", window.usingComponents = [], require(
"pages/specialdetail/specialdetail"),
window.__swanRoute = "pages/hotconcept/hotconcept", window.usingComponents = ["components/ec-canvas/ec-canvas"], require("pages/hotconcept/hotconcept"),
window.__swanRoute = "pages/personalcenter/personalcenter", window.usingComponents = [], require("pages/personalcenter/personalcenter"),
window.__swanRoute = "pages/download/download", window.usingComponents = [], require("pages/download/download"),
window.__swanRoute = "pages/mycoupon/mycoupon", window.usingComponents = [], require("pages/mycoupon/mycoupon"),
window.__swanRoute = "pages/comment/comment", window.usingComponents = [], require("pages/comment/comment"),
window.__swanRoute = "pages/version/version", window.usingComponents = [], require("pages/version/version"),
window.__swanRoute = "pages/webview/webview", window.usingComponents = [], require("pages/webview/webview"),
window.__swanRoute = "pages/minigramcode/minigramcode", window.usingComponents = [], require("pages/minigramcode/minigramcode"),
window.__swanRoute = "pages/miniqrcode/miniqrcode", window.usingComponents = [], require("pages/miniqrcode/miniqrcode"),
window.__swanRoute = "pages/push/push", window.usingComponents = [], require("pages/push/push");
从page.js文件,分析app请求
- 请求对比
//这是百度小程序的官网请求demo
swan.request({
url: 'https://smartprogram.baidu.com/xxx', // 仅为示例,并非真实的接口地址
method: 'GET',
dataType: 'json',
data: {
key: 'value'
},
header: {
'content-type': 'application/json' // 默认值
},
success: function (res) {
console.log(res.data);
},
fail: function (err) {
console.log('错误码:' + err.errCode);
console.log('错误信息:' + err.errMsg);
}
});
//这是逆向出来的请求
L = D.getSelectNewUrl,
n.request({
url: L,
data: {
fromversion: 530
},
method: "GET",
success: function (e) {
if (e.data.status) {
var a = e.data.info,
o = a.toplist.concat(a.subscribelist);
o.unshift({
channelId: "006688",
title: "专栏"
}), o.splice(3, 0, {
channelId: "779900",
title: "热点"
}), console.log("menuListData:", o);
for (var n = 0; n < o.length; n++) R[n] = new Array, 5 == o[n].type &&
o.splice(n, 1);
t.setData({
menuList: o,
allArticles: R
})
}
t.loadHeadLineArticles()
},
fail: function () {
n.showLoading({
title: "网络出现故障",
duration: 1500
})
}
})
从common.js文件,分析app请求
//截取其中一段细看,可以看出和讯网的app所有请求路径都封装在同一个文件中,通过这个文件,基本就可以分析出,请求的地址了
window.define("config",
function(t, e, n, i, a, r, o, s, l, u, c, h, d, f, p, g, m) {
"use strict";
var v, y, x, _, b, w, S = "nwapi.hexun.com",
M = "wapi.hexun.com",
v = "m.hexun.com",
y = "wxin.hexun.com",
x = "callvip.hexun.com",
_ = "callwx.hexun.com",
b = "redian.hexun.com",
w = "api-stockmatch.hexun.com",
I = {
upDataWx: !1,
appid: "wx25371c74b74ebb96",
mHost: v,
openToolHost: "opentool.hexun.com",
newsSearchHost: "newssearch.hexun.com",
callVipHost: y,
callWxHost: _,
regToolHost: "regtool.hexun.com",
statiSticsHost: "anautrack.hexun.com",
nwApiHost: S,
wApiHost: M,
stackRoomHost: b,
hqwiApiHost: "hqwiapi.hexun.com",
stockMatchHost: w,
commentToolHost: "commenttool.hexun.com",
statiSticsUrl: "https://anautrack.hexun.com/pubinterface/io/fileinterface.aspx",
getSelectUrl: "https://" + v + "/api/getMsiteCategoryInfoList",
getDuiBaLoginUrl: "https://" + v + "/duiba/getLoginUrl",
getSelectNewUrl: "https://" + S + "/version/selectchannel",
newsRecommendUrl: "https://opentool.hexun.com/MongodbNewsService/getDGBackupNewsListByPid.jsp",
headLinesUrl: "https://" + M + "/Head_newsJson.cc?appId=1&pid=100234721&pc=20&pn=1&num=2&fromhost=ywkf",
isStockOnOffUrl: "https://" + w + "/api/redis/isTransaction",
stockGrailUrl: "https://hqwiapi.hexun.com/a/quotelist",
getHotPointNewsUrl: "https://" + M + "/AppPopUp_getHotPointNews.cc",
futuresUrl: "https://" + M + "/Mix_newsJson.cc",
twentyFourHoursUrl: "https://" + S + "/liveNews/getNews",
getNewsBriefingUrl: "https://" + S + "/NewsInfo/getNewsBriefing",
twentyFourCountUrl: "https://" + S + "/liveNews/getNewNewsCount",
newsListUrl: "https://" + v + "/api/getMoreNews",
newsListNewUrl: "https://" + M + "/AppV5_newsJson_V5.cc",
getMoreListNewUrl: "https://" + S + "/liveNews/getMoreNews",
wxHeXunLoginUrl: "https://regtool.hexun.com/wapreg/wechat/miniapp.aspx",
heXunJsCodeLoginUrl: "https://regtool.hexun.com/wapreg/wechat/jscode_login.aspx",
checkBindMobileUrl: "https://regtool.hexun.com/wapreg/checkbindmobile.aspx",
wxGoldRewardUrl: "https://" + _ + "/share/msite_api/first_add_gold",
getAccReditUrl: "https://" + _ + "/share/platform_user/is_first_visit",
wxGiveGoldUrl: "https://" + x + "/rpsapi/service",
recordHXWShareInfoUrl: "https://" + y + "/hxwx-web/share/recordHXWShareInfo",
getOpenIdUrl: "https://" + y + "/hxwx-web/get/openId",
bindPhoneNumberUrl: "https://" + y + "/hxwx-web/decrypt/bindMoble",
onOffPushSwitchUrl: "https://" + y + "/hxwx-web/pushReceiveIntention/modify",
getPushStatusUrl: "https://" + y + "/hxwx-web//pushReceiveIntention/query",
storageInviteUrl: "https://" + _ + "/share/sapi/interact_by_user",
storageShareUrl: "https://" + _ + "/share/sapi/share_by_user",
getHXWShareInfoUrl: "https://" + y + "/hxwx-web/share/getHXWShareInfo",
getPayApiUrl: "https://" + x + "/payapi/service?service=query_balance",
detailsUrl: "https://opentool.hexun.com/MongodbNewsService/forapp/gethtml.jsp",
detailsNewsUrl: "https://" + v + "/api/getNewsContent",
detailsColumnNewsUrl: "https://" + M + "/AppV5_commonNewsDetail.cc",
detailsRecommendUrl: "https://opentool.hexun.com/MongodbNewsService/forapp/getrelativenewsJson.jsp",
newsSearchUrl: "https://newssearch.hexun.com/nocallback/newsjson",
setSwitchOnOff: "https://" + y + "/hxwx-web/comm/switch",
wxImgCodeUrl: "https://" + y + "/hxwx-tools/img/getDetailCode",
getCommentUrl: "https://commenttool.hexun.com/Comment/GetComment.do",
getGiveLikeUrl: "https://commenttool.hexun.com/Comment/praise.do",
getPostCommentUrl: "https://commenttool.hexun.com/Comment/PostComment.do",
saveFormIdUrl: "https://" + y + "/hxwx-web/tmpMsg/saveFormId",
saveReadNewsUrl: "https://" + y + "/hxwx-web/news/saveNewsClickRecord",
getReadOrgNewsUrl: "https://" + y + "/hxwx-web/news/getRNewsInfoByUsrId",
readArticleGoldUrl: "https://" + _ + "/share/msite_api/rader_add_gold",
shareArticleGoldUrl: "https://" + _ + "/share/msite_api/share_article_add_gold",
getMoreColumnUrl: "https://" + M + "/Column_columnMore.cc",
getColumnInfoUrl: "https://" + M + "/Column_columnInfo.cc",
getYetColumnListUrl: "https://" + M + "/Column_columnlist.cc",
postAttentionColumnUrl: "https://" + M + "/Column_focustatus.cc",
getColumnNewsUrl: "https://" + M + "/Column_columnNews.cc",
openIdForAppUrl: "https://" + y + "/hxwx-web/get/openIdForApp",
openIdForNewAppUrl: "https://" + y + "/hxwx-web/get/openIdForAppUseCookies",
getHostIdeaListUrl: "https://" + b + "/concept/list",
getHostDetailsUrl: "https://" + b + "/concept/detail/",
getHostStocksUrl: "https://" + b + "/concept/stocks/",
getHostNewsUrl: "https://" + b + "/concept/h5/news/",
getHostChartUrl: "https://" + b + "/concept/chart/",
getHotSpotDetailUrl: "https://" + b + "/concept/h5/newsdetail/",
getCouponQueryUrl: "https://" + _ + "/cbsapi/api/couponQuery",
useCouponUrl: "https://" + _ + "/cbsapi/api/cashCouponConvert",
getCouponUrl: "https://" + _ + "/cbsapi/api/receiveCoupon",
getVideoInfoUrl: "https://" + _ + "/wxitem/hxApplet/getVideoInfo",
getAdInfoUrl: "https://" + _ + "/wxitem/hxApplet/getAdInfo",
getAuthRedPacketUrl: "https://" + _ + "/wxitem/hxApplet/getAuthRedPacketInfo",
getRedPacketInfoUrl: "https://" + _ + "/wxitem/hxApplet/getActivitInfo",
getPushInfoUrl: "https://" + _ + "/wxitem/hxApplet/getPushInfo",
getTextConfigUrl: "https://" + _ + "//wxitem/hxAppletTextConfig/getTextConfig",
guessHeadBetGoldUrl: "https://" + _ + "/msite/gh/guess_headline",
guessHeadListsUrl: "https://" + _ + "/msite/gh/list_headline",
guessHeadHistoryListsUrl: "https://" + _ + "/msite/gh/history_headline",
guessHeadSearchUseGoldUrl: "https://" + _ + "/msite/gh/user_usegold",
guessHeadSearchTotalGoldUrl: "https://" + _ + "/msite/gh/user_totalgold",
guessHeadGetServeDaterUrl: "https://" + _ + "/msite/gh/get_serverdate",
guessHeadMyRecordUrl: "https://" + _ + "/msite/gh/guess_record"
};
e.exports = I
}),
总结
-
通过上述分析,可以看出这个小程序大部分都没有加密的,随机抽取一个接口使用postman获取数据请求数据,这个其实也可以捉包查看的.
-
在Safari的控制台中观察到,所有请求都没有在控制台中显示网络加载,所以猜测所有的app请求都没原生接管了,这里就可以通过原生,控制app允许访问的域名.
//测试请求域名
https://nwapi.hexun.com/version/selectchannel
从app角度分析
确认原生渲染使用是uiwebview还是wkwebview
- reaveal查看,百度小程序,打开一个小程序时,发现在于windon下存在一个隐藏wkwebview.初步猜测,该单例专做js与原生交互并做逻辑更新处理,另外发现该渲染的控制器为,SWANSlaveWebViewController.
- 分析SWANSlaveWebViewController.h,发现存在 masterWebView, webView , _webViewComponent 这个三个基于wkwebview的对象声明,所以基本可以肯定使用的wkwebview
@property(retain, nonatomic) WKWebView *masterWebView; // @synthesize masterWebView=_masterWebView;
@property(retain, nonatomic) WKWebView *webView; // @synthesize webView=_webView;
WKWebView *_webViewComponent;
#import <UIKit/UIViewController.h>
#import "BBAAutoKeyboardDelegate-Protocol.h"
#import "SWANCoverLayoutDelegate-Protocol.h"
#import "SWANSlaveWebViewControllerUIProtocol-Protocol.h"
#import "SWANVideoHandleDelegate-Protocol.h"
#import "WKNavigationDelegate-Protocol.h"
#import "WKUIDelegate-Protocol.h"
@class NSMutableDictionary, NSMutableSet, NSNumber, NSString, NSURLComponents, SWANCanvasComponent, SWANCircleRefreshHeader, SWANConfigWindow, SWANCoverComponent, SWANOpenFlowLogger, SWANSlaveWebViewFactory, UIBarButtonItem, UIButton, UILabel, UIProgressView, UIView, WKWebView;
@interface SWANSlaveWebViewController : UIViewController <BBAAutoKeyboardDelegate, SWANSlaveWebViewControllerUIProtocol, SWANVideoHandleDelegate, WKUIDelegate, WKNavigationDelegate, SWANCoverLayoutDelegate>
{
_Bool _didNavigationBarShowLoading;
_Bool _isFirstShow;
_Bool _notSupportFullscreenPopGesture;
_Bool _hasIninialAnimationVCAsChildVC;
_Bool _navbarColorAnimationing;
_Bool _isKeyboardShow;
_Bool _isFisrtPlayVideo;
NSString *_appId;
long long _webViewID;
SWANConfigWindow *_window;
NSString *_URL;
NSString *_bundlePath;
SWANCircleRefreshHeader *_refreshHeader;
NSString *_routeType;
NSString *_liveId;
SWANCanvasComponent *_canvasComponent;
SWANCoverComponent *_coverComponent;
WKWebView *_webView;
WKWebView *_masterWebView;
UILabel *_titleLabel;
NSString *_pagePath;
UIBarButtonItem *_backItem;
UIButton *_backButton;
UIButton *_closeButton;
UIButton *_goHomeButton;
UIBarButtonItem *_goHomeItem;
UIButton *_moreMenuButton;
UIView *_rightCustomView;
UIView *_dividingLineView;
WKWebView *_webViewComponent;
UIProgressView *_webViewComponentProgressView;
NSString *_passportRedirectURL;
NSMutableDictionary *_viewMap;
UIViewController *_initialAnimationController;
UIView *_navbarAnimationView;
long long _savedCurrentPlayerStatus;
UIView *_headerView;
NSURLComponents *_urlComponents;
NSString *_swanCoreVersion;
NSString *_swanCorePath;
NSMutableSet *_dispatcherSet;
NSMutableDictionary *_componentsViewMap;
UIView *_safeAreaView;
SWANSlaveWebViewFactory *_factory;
SWANOpenFlowLogger *_loadingTimeFlow;
struct CGRect _liveOriginFrame;
}
@property(retain, nonatomic) SWANOpenFlowLogger *loadingTimeFlow; // @synthesize loadingTimeFlow=_loadingTimeFlow;
@property(retain, nonatomic) SWANSlaveWebViewFactory *factory; // @synthesize factory=_factory;
@property(retain, nonatomic) UIView *safeAreaView; // @synthesize safeAreaView=_safeAreaView;
@property(readonly, nonatomic) NSMutableDictionary *componentsViewMap; // @synthesize componentsViewMap=_componentsViewMap;
@property(retain, nonatomic) NSMutableSet *dispatcherSet; // @synthesize dispatcherSet=_dispatcherSet;
@property(copy, nonatomic) NSString *swanCorePath; // @synthesize swanCorePath=_swanCorePath;
@property(copy, nonatomic) NSString *swanCoreVersion; // @synthesize swanCoreVersion=_swanCoreVersion;
@property(retain, nonatomic) NSURLComponents *urlComponents; // @synthesize urlComponents=_urlComponents;
@property(nonatomic) _Bool isFisrtPlayVideo; // @synthesize isFisrtPlayVideo=_isFisrtPlayVideo;
@property(retain, nonatomic) UIView *headerView; // @synthesize headerView=_headerView;
@property(nonatomic) _Bool isKeyboardShow; // @synthesize isKeyboardShow=_isKeyboardShow;
@property(nonatomic) long long savedCurrentPlayerStatus; // @synthesize savedCurrentPlayerStatus=_savedCurrentPlayerStatus;
@property(nonatomic) _Bool navbarColorAnimationing; // @synthesize navbarColorAnimationing=_navbarColorAnimationing;
@property(nonatomic) __weak UIView *navbarAnimationView; // @synthesize navbarAnimationView=_navbarAnimationView;
@property(nonatomic) __weak UIViewController *initialAnimationController; // @synthesize initialAnimationController=_initialAnimationController;
@property(nonatomic) _Bool hasIninialAnimationVCAsChildVC; // @synthesize hasIninialAnimationVCAsChildVC=_hasIninialAnimationVCAsChildVC;
@property(nonatomic) _Bool notSupportFullscreenPopGesture; // @synthesize notSupportFullscreenPopGesture=_notSupportFullscreenPopGesture;
@property(nonatomic) _Bool isFirstShow; // @synthesize isFirstShow=_isFirstShow;
@property(nonatomic) struct CGRect liveOriginFrame; // @synthesize liveOriginFrame=_liveOriginFrame;
@property(readonly, nonatomic) NSMutableDictionary *viewMap; // @synthesize viewMap=_viewMap;
@property(copy, nonatomic) NSString *passportRedirectURL; // @synthesize passportRedirectURL=_passportRedirectURL;
@property(retain, nonatomic) UIProgressView *webViewComponentProgressView; // @synthesize webViewComponentProgressView=_webViewComponentProgressView;
@property(retain, nonatomic) WKWebView *webViewComponent; // @synthesize webViewComponent=_webViewComponent;
@property(retain, nonatomic) UIView *dividingLineView; // @synthesize dividingLineView=_dividingLineView;
@property(retain, nonatomic) UIView *rightCustomView; // @synthesize rightCustomView=_rightCustomView;
@property(retain, nonatomic) UIButton *moreMenuButton; // @synthesize moreMenuButton=_moreMenuButton;
@property(retain, nonatomic) UIBarButtonItem *goHomeItem; // @synthesize goHomeItem=_goHomeItem;
@property(retain, nonatomic) UIButton *goHomeButton; // @synthesize goHomeButton=_goHomeButton;
@property(retain, nonatomic) UIButton *closeButton; // @synthesize closeButton=_closeButton;
@property(retain, nonatomic) UIButton *backButton; // @synthesize backButton=_backButton;
@property(retain, nonatomic) UIBarButtonItem *backItem; // @synthesize backItem=_backItem;
@property(nonatomic) _Bool didNavigationBarShowLoading; // @synthesize didNavigationBarShowLoading=_didNavigationBarShowLoading;
@property(copy, nonatomic) NSString *pagePath; // @synthesize pagePath=_pagePath;
@property(retain, nonatomic) UILabel *titleLabel; // @synthesize titleLabel=_titleLabel;
@property(retain, nonatomic) WKWebView *masterWebView; // @synthesize masterWebView=_masterWebView;
@property(retain, nonatomic) WKWebView *webView; // @synthesize webView=_webView;
@property(retain, nonatomic) SWANCoverComponent *coverComponent; // @synthesize coverComponent=_coverComponent;
@property(retain, nonatomic) SWANCanvasComponent *canvasComponent; // @synthesize canvasComponent=_canvasComponent;
@property(copy, nonatomic) NSString *liveId; // @synthesize liveId=_liveId;
@property(copy, nonatomic) NSString *routeType; // @synthesize routeType=_routeType;
@property(retain, nonatomic) SWANCircleRefreshHeader *refreshHeader; // @synthesize refreshHeader=_refreshHeader;
@property(copy, nonatomic) NSString *bundlePath; // @synthesize bundlePath=_bundlePath;
@property(copy, nonatomic) NSString *URL; // @synthesize URL=_URL;
@property(copy, nonatomic) SWANConfigWindow *window; // @synthesize window=_window;
@property(nonatomic) long long webViewID; // @synthesize webViewID=_webViewID;
@property(retain, nonatomic) NSString *appId; // @synthesize appId=_appId;
- (void).cxx_destruct;
- (unsigned long long)supportedInterfaceOrientations;
- (_Bool)shouldAutorotate;
- (id)coverLayout:(id)arg1 fetchViewWithID:(id)arg2;
- (void)componentsMapRemoveAllSubviewsFromView:(id)arg1 viewID:(id)arg2;
- (void)componentsMapRemoveSubviewWithID:(id)arg1;
- (id)componentsMapGetViewWithID:(id)arg1;
- (_Bool)componentsMapSetSubview:(id)arg1 withID:(id)arg2;
- (void)webView:(id)arg1 decidePolicyForNavigationAction:(id)arg2 decisionHandler:(CDUnknownBlockType)arg3;
- (void)webView:(id)arg1 decidePolicyForNavigationResponse:(id)arg2 decisionHandler:(CDUnknownBlockType)arg3;
- (void)webView:(id)arg1 runJavaScriptConfirmPanelWithMessage:(id)arg2 initiatedByFrame:(id)arg3 completionHandler:(CDUnknownBlockType)arg4;
- (void)webView:(id)arg1 runJavaScriptAlertPanelWithMessage:(id)arg2 initiatedByFrame:(id)arg3 completionHandler:(CDUnknownBlockType)arg4;
- (void)webView:(id)arg1 runJavaScriptTextInputPanelWithPrompt:(id)arg2 defaultText:(id)arg3 initiatedByFrame:(id)arg4 completionHandler:(CDUnknownBlockType)arg5;
- (void)cancelLoadingTimeFLow;
- (_Bool)endLoadingTimeFLow:(id)arg1;
- (void)startLoadingTimeFlow;
- (void)dispatchToSlaveEventJS:(id)arg1 completionHandler:(CDUnknownBlockType)arg2;
- (void)dispatchToSlaveEventName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
- (_Bool)currentViewsContainsVideoPlayerBaseView:(id)arg1;
- (void)dealloc;
- (void)viewWillLayoutSubviews;
- (void)viewDidDisappear:(_Bool)arg1;
- (void)viewWillDisappear:(_Bool)arg1;
- (void)resetScrollViewOffsetYZero;
- (void)viewDidAppear:(_Bool)arg1;
- (void)viewWillAppear:(_Bool)arg1;
- (struct CGRect)getWebViewFrame;
- (void)addComponent;
- (void)createWebView;
- (void)viewDidLoad;
- (void)addSlaveWebView;
- (void)updateViewsColor;
- (void)actionsAfterViewDidLoad;
- (id)dispatchPageReadyEventParams;
- (void)updateWebViewProperty;
- (void)dispatchPageReadyEvent;
@property(readonly, nonatomic) _Bool finishLoadWindowConfig;
- (void)loadView;
- (void)setupWithModel:(id)arg1;
- (id)initWithModel:(id)arg1;
- (void)sendDeallocEvent;
- (void)sendRouteEvent;
- (void)sendNavigationBackEvent:(id)arg1;
- (void)dispatchOnHideEvent;
- (void)dispatchOnShowEvent;
- (void)composeShortcut:(id)arg1;
- (id)keyCommands;
- (void)updateLiveWebViewContrainerFrame;
- (unsigned long long)supportedInterfaceOrientations;
- (_Bool)shouldAutorotate;
- (void)playerScreenChangeToFull:(_Bool)arg1 direction:(long long)arg2 liveId:(id)arg3;
- (void)livePlayerExitFullScreen;
- (void)livePlayerEnterFullScreen:(long long)arg1;
- (void)viewWillTransitionToSize:(struct CGSize)arg1 withTransitionCoordinator:(id)arg2;
- (void)salveWebViewFroLiveClose;
- (void)slaveWebViewForLiveDealloc;
- (void)slaveWebViewForLiveDidEnterBackground;
- (void)slaveWebViewForLiveWillEnterForeground;
- (void)slaveWebViewForLiveDidDisappear;
- (void)slaveWebViewForLiveWillDisappear;
- (void)slaveWebViewForLiveWillAppear;
- (void)slaveWebViewForLiveDidAppear;
- (void)handlekeyboardWillhide:(id)arg1;
- (void)handlekeyboardWillShow:(id)arg1;
- (void)handleStatusBarClicked:(id)arg1;
- (void)handleApplicationDidEnterBackground:(id)arg1;
- (void)handleApplicationWillEnterForeground:(id)arg1;
- (void)addNotificationObserver;
- (_Bool)webViewDidTerminate;
- (void)setWebViewDidTerminate:(_Bool)arg1;
- (void)reloadWKWebViewWhenOutOfMemory;
- (_Bool)masterWebViewIsValid;
- (void)recoverySlaveWebViewIfNeeded;
- (void)recoveryIfNeeded;
- (void)webViewWebContentProcessDidTerminate:(id)arg1;
- (void)textView:(id)arg1 updateTextPostionWithKeyboardHeight:(double)arg2;
- (void)textViewKeyboardWillHide:(id)arg1;
- (void)creatScrollViewHeaderView;
@property(retain, nonatomic) NSNumber *webViewYWhenKeyboardIsVisible;
- (void)webViewScrollEnabled:(_Bool)arg1;
- (void)fullscreenPopGestureDidCancelPopAnimation;
- (void)fullscreenPopGestureWillCancelPopAnimation;
- (void)recoverFullscreenPopGestureStopAction;
- (void)fullscreenPopGestureDidFinishPopAnimation;
- (void)fullscreenPopGestureWillFinishPopAnimation;
- (void)fullscreenPopGestureWillPopAnimation;
- (_Bool)edgePopGestureShouldRecognizeSimultaneously;
- (_Bool)supportEdgePopGesture;
- (_Bool)supportFullscreenPopGesture;
- (void)scrollViewDidScroll:(id)arg1;
- (void)openWebViewUserInteractionEnabled;
- (void)stopWebViewUserInteractionEnabled;
- (void)scrollViewDidEndDragging:(id)arg1 willDecelerate:(_Bool)arg2;
- (void)scrollViewWillBeginDragging:(id)arg1;
- (id)slaveContentScrollView;
- (void)enableWebViewScrollable;
- (void)disableWebViewScrollable;
- (id)findNativeViewWithViewID:(id)arg1;
- (void)removeNativeViewWithViewID:(id)arg1;
- (void)insertNativeView:(id)arg1 withViewID:(id)arg2;
- (void)removeNativeView:(id)arg1;
- (void)insertNativeView:(id)arg1;
- (void)adjustWebViewFrame;
- (void)layoutRightCustomViewIfNoNavigator;
- (double)mnpWebViewYDefaultValue;
- (_Bool)useCustomNavigationBar;
- (void)updateWebviewBackgroundStyle;
- (void)updateNavigationBarAppearance;
- (void)updateNavigationBarColorWithAnimation:(id)arg1;
- (void)changeLeftBarButtonItemsImage;
- (void)updateNavigationBarColor;
- (void)setStyleRightButtonView:(id)arg1;
- (void)loadRightCustomView;
- (void)updateNavigationRightBarButtonItems;
- (void)showLeftGoHomeBtnsIsFirstVC:(_Bool)arg1;
- (void)showLeftBackBtnsIsFirstVC:(_Bool)arg1;
- (void)updateNavigationLeftBarButtonItems;
- (void)updateRefreshHeaderCircleStyle;
- (void)addRefreshHeaderForWebView:(id)arg1;
- (void)replaceGlobalWindowData;
- (void)realUpdateNavBarTitle;
- (void)updateNavigationBarTitle;
- (void)updateNavigationBarLoading;
- (void)hideNavigationBarLoading;
- (void)showNavigationBarLoading;
- (void)resetNavigationItemTitleView:(id)arg1;
- (void)setSlaveStatusBarStyle;
- (id)getTextStyleColorForStyle:(id)arg1;
- (void)generateTitleLabelForRect:(struct CGRect)arg1;
- (void)showMoreMenu:(id)arg1;
- (void)cleanReportCountAndTimer;
- (void)setMoreMenuClickCount:(long long)arg1;
- (long long)moreMenuClickCount;
- (void)setReportTriggerTimer:(id)arg1;
- (id)reportTriggerTimer;
- (void)clickGoHome:(id)arg1;
- (_Bool)shouldShowComeBackGuideView;
- (void)close;
- (void)close:(id)arg1;
- (void)back:(id)arg1;
- (void)createButtonsAndItems;
- (void)viewSafeAreaInsetsDidChange;
- (void)setScrollViewDelegate;
- (void)panGestureEnabledBBAPlayer:(_Bool)arg1;
- (_Bool)stopPlayWhenDidDisappear;
- (void)slaveWebViewForVideoDidAppear;
- (void)slaveWebViewForVideoWillDisappear;
- (void)playerVidWillChanged:(id)arg1;
- (void)playerDelegateWillChanged:(id)arg1 vid:(id)arg2;
- (void)playerViewDidRemovedFromSurperView:(id)arg1 vid:(id)arg2;
- (void)playerViewDidAddedToSurperView:(id)arg1 vid:(id)arg2;
- (_Bool)isNeedClearDraftAfterSendClick;
- (_Bool)isNeedUpdateBarrageListFromServer;
- (_Bool)playerShouldShowBarrageComponent:(long long)arg1 withPlayerMode:(unsigned long long)arg2;
- (void)playerSendBarrage:(id)arg1 playAtTime:(id)arg2 videoId:(id)arg3;
- (void)videoPlayerUpdateTimeProgress:(double)arg1 duration:(double)arg2 videoId:(id)arg3;
- (void)playerScreenChangeToNewMode:(unsigned long long)arg1 oldModel:(unsigned long long)arg2 videoId:(id)arg3 frame:(struct CGRect)arg4;
- (void)triggerEventWithType:(id)arg1 vid:(id)arg2 data:(id)arg3;
- (void)triggerEventWithType:(id)arg1 vid:(id)arg2;
- (void)playerDidFailed:(id)arg1 errorInfo:(id)arg2;
- (void)playerBufferEmpty:(id)arg1;
- (void)playerDidEnd:(id)arg1;
- (void)playerDidPause:(id)arg1;
- (void)playerDidPlaySuccess:(id)arg1;
- (void)playerDidPlay:(id)arg1;
- (void)reloadWebViewComponentVideoPage;
- (void)pauseWebViewComponentVideo;
- (void)webView:(id)arg1 didFinishNavigation:(id)arg2;
- (void)observeValueForKeyPath:(id)arg1 ofObject:(id)arg2 change:(id)arg3 context:(void *)arg4;
- (void)removeWebViewComponent;
- (_Bool)updateWebViewComponentForURL:(id)arg1 optionsDict:(id)arg2;
- (_Bool)gestureRecognizerShouldBegin:(id)arg1;
- (void)panGestureRecognizerAction:(id)arg1;
- (id)webView:(id)arg1 createWebViewWithConfiguration:(id)arg2 forNavigationAction:(id)arg3 windowFeatures:(id)arg4;
- (_Bool)insertWebViewComponentForURL:(id)arg1 optionsDict:(id)arg2;
- (void)stopCamera;
- (void)dismissTabBarWithAnimation:(_Bool)arg1;
- (void)showTabBarWithAnimation:(_Bool)arg1;
- (id)fakeTabBar;
- (void)setFakeTabBar:(id)arg1;
- (id)isInAnimation;
- (void)setIsInAnimation:(id)arg1;
// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;
@end
分析Safari,思考页面切换时,webview处理
- 在首页时,小程序只有一个webview容器,在二级页面时, webview容器增加.且jscontext容器,数量不变.
- 优点,正常情况下,wkwebview加载页面时,可能出现的性能问题,直接通过webview预加载,或者返回时,直接使用上一层已加载过的webview,所以小程序性能接近原生
- 缺点,内存资源消耗会比正常页面多.
分析Safari,渲染时的html
- html标准标签看,明显右边的格式和一个正常网页类似,但是标签又不是通用标签,也不是.swan标签,猜测是,存在一个三方工具,动态解析百度自定义的.swan内使用的标签,转为一种html的自定义标签,猜测是使用 “HTML Imports” 然后百度了一下,苹果的浏览器不支持.所以有可能是使用WebComponent的 document.registerElement来自定义标签.
- 通过具体定位,还是可以看出部分页面,还是使用了原生的html标记,详见下图.例如,养老这个标签,还是使用span标签的
- 可以看出播放器时使用原生控件,SWANContainer使用这个类,而其他部分还是使用wkcompositingview渲染,另外选择中播放器元素时,并没有发生高亮,所以,基本可以确定,存在一个占位层,而原生播放器就放在占位层上.另外,当列表滚动时,播放器不跟随列表滚动,猜测可能是滚动换算,原生坐标可能存在难点.
总结
- 小程序最根源也是使用wkwebview,使用web的标准实现基础功能,另外还有,播放器之类的就是,是在wkwebview上嵌套一个原生控件实现的并在底层放置占位的div,所以控件只能在最顶或最底层
从原生开发角度思考
更具百度小程序官方分析,概况为下图
hook (SWANSlaveWebViewController) 方法继续分析
- hook以下方法,并debug,经分析基本可以确定, SWANSlaveWebViewController为单个展示小程序页面的容器
//hook这堆方法
//- (void)webView:(id)arg1 decidePolicyForNavigationAction:(id)arg2 decisionHandler:(CDUnknownBlockType)arg3;
//- (void)webView:(id)arg1 decidePolicyForNavigationResponse:(id)arg2 decisionHandler:(CDUnknownBlockType)arg3;
//- (void)webView:(id)arg1 runJavaScriptConfirmPanelWithMessage:(id)arg2 initiatedByFrame:(id)arg3 completionHandler:(CDUnknownBlockType)arg4;
//- (void)webView:(id)arg1 runJavaScriptAlertPanelWithMessage:(id)arg2 initiatedByFrame:(id)arg3 completionHandler:(CDUnknownBlockType)arg4;
//- (void)webView:(id)arg1 runJavaScriptTextInputPanelWithPrompt:(id)arg2 defaultText:(id)arg3 initiatedByFrame:(id)arg4 completionHandler:(CDUnknownBlockType)arg5;
//- (void)dispatchToSlaveEventJS:(id)arg1 completionHandler:(CDUnknownBlockType)arg2;
//- (void)dispatchToSlaveEventName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
小程序文件,及对应内核关系
-
可以看出swan-core是可以根据app动态下发的,用app版本是可以存在多个小程序内核
-
而真正小程序会根据具配置动态下发代码
debug,对应接口的log
- 这个函数就是 逻辑容器中,js执行setdata更新ui时,通知渲染容器更新对应ui的
- 分析消息体,如下.这个涉及到数据的单向更新,逻辑容器执行js->渲染容器更新展示
//这是个捉更新ui时的通讯数据
{
"type": "setData", //对应js更新方法名
"slaveId": "1", //对应渲染webview的编号
"setObject": { //具体跟新的数值
"tipsVisible": true
},
"pageUpdateStart": "1548991012832" //触发事件的时间戳
}
//苏宁小程序 切换顶部tab时捉出的通讯数据
{
"type": "querySlaveSelector",
"value": {
"selector": ".detail-tab-warpper",
"queryType": "select",
"index": 0,
"operation": "boundingClientRect",
"fields": {},
"execId": 99,
"contextId": null
}
}
//============================================
//原生函数
- (void)dispatchToSlaveEventName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
//部分参数log
2019-02-01 11:16:52.852357+0800 BaiduBoxApp[1364:197984] dispatchToSlaveEventName
arg1:message
arg2:{
"message" : "{"type":"setData","slaveId":"1","setObject":{"tipsVisible":true},"pageUpdateStart":"1548991012832"}"
}
arg3:(null)
2019-02-01 11:16:52.852879+0800 BaiduBoxApp[1364:197984] dispatchToSlaveEventName
arg1:message
arg2:{
"message" : "{"type":"setData","slaveId":"1","setObject":{"isRequesting":false},"pageUpdateStart":"1548991012832"}"
}
arg3:(null)
2019-02-01 13:51:37.186025+0800 BaiduBoxApp[1627:245407] dispatchToSlaveEventName
arg1:message
arg2:{
"message" : "{"type":"querySlaveSelector","value":{"selector":".detail-tab-warpper","queryType":"select","index":0,"operation":"boundingClientRect","fields":{},"execId":99,"contextId":null}}"
}
- 这个暂时未看出,但是好似每次初始化一个新的小程序页面都会触发如下,感觉是加载url时,拦截链接,并通过指定回调传给js.这里这么做的原材,猜测为小程序共享一个js容器,这个在渲染容器直接拦截这个结果,能减少逻辑容器和渲染容器的通讯.
getSlaveIdSync?callback=__jsna_6,getAppInfoSync?callback=__jsna_7,getSystemInfoSync?callback=__jsna_8
- salaves.html js不太熟悉,暂时未看出具体实现功能,猜测是生成一个全局交互对象.方便原生和js交互
//slaves.html 原文件
//file:///var/mobile/Containers/Data/Application/B75FC88D-09AE-47F9-A436-79BBD0C2504C/Documents/SwanCaches/swan-core/preset/3.15.3/slaves/slaves.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>世界很复杂,百度更懂你</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<script type="text/javascript">
(function (global) {
// global._naSwan = undefined;
global.swanGlobal = undefined;
global.swanVersion = '3.15.4';
global.feSlavePreloadStart = Date.now();
})(window);
</script>
<link href="./styles_slaves.css" rel="stylesheet">
</head>
<body>
<script type="text/javascript" src="./index.js"></script>
<script type="text/javascript">
(function (global) {
global.feSlavePreloadEnd = Date.now();
})(window);
</script>
</body>
</html>
//xcode 中打印函数
//============================================
//原生函数
- (void)webView:(id)arg1 runJavaScriptTextInputPanelWithPrompt:(id)arg2 defaultText:(id)arg3 initiatedByFrame:(id)arg4 completionHandler:(CDUnknownBlockType)arg5;
//部分参数log
2019-02-01 11:20:31.909926+0800 BaiduBoxApp[1364:197984] runJavaScriptTextInputPanelWithPrompt
arg1:<SWANSlaveWebView: 0x10bab2a00; frame = (0 64; 375 603); opaque = NO; autoresize = W+H; layer = <CALayer: 0x280e178e0>>
arg2:{"func":"dispatch","args":["baiduboxapp://v19/swan/getSlaveIdSync?callback=__jsna_6"]}
arg3:
arg4:<WKFrameInfo: 0x10b397600; webView = 0x10bab2a00; isMainFrame = YES; request = <NSMutableURLRequest: 0x280c73280> { URL: file:///var/mobile/Containers/Data/Application/B75FC88D-09AE-47F9-A436-79BBD0C2504C/Documents/SwanCaches/swan-core/preset/3.15.3/slaves/slaves.html }>
arg5:(null)
2019-02-01 11:20:31.965279+0800 BaiduBoxApp[1364:197984] runJavaScriptTextInputPanelWithPrompt
arg1:<SWANSlaveWebView: 0x10bab2a00; frame = (0 64; 375 603); opaque = NO; autoresize = W+H; layer = <CALayer: 0x280e178e0>>
arg2:{"func":"dispatch","args":["baiduboxapp://v19/swan/getAppInfoSync?callback=__jsna_7"]}
arg3:
arg4:<WKFrameInfo: 0x120b7d940; webView = 0x10bab2a00; isMainFrame = YES; request = <NSMutableURLRequest: 0x280c6c870> { URL: file:///var/mobile/Containers/Data/Application/B75FC88D-09AE-47F9-A436-79BBD0C2504C/Documents/SwanCaches/swan-core/preset/3.15.3/slaves/slaves.html }>
arg5:(null)
2019-02-01 11:20:31.974673+0800 BaiduBoxApp[1364:197984] runJavaScriptTextInputPanelWithPrompt
arg1:<SWANSlaveWebView: 0x10bab2a00; frame = (0 64; 375 603); opaque = NO; autoresize = W+H; layer = <CALayer: 0x280e178e0>>
arg2:{"func":"dispatch","args":["baiduboxapp://v19/utils/getSystemInfoSync?callback=__jsna_8"]}
arg3:
arg4:<WKFrameInfo: 0x10a61d750; webView = 0x10bab2a00; isMainFrame = YES; request = <NSMutableURLRequest: 0x280c6d810> { URL: file:///var/mobile/Containers/Data/Application/B75FC88D-09AE-47F9-A436-79BBD0C2504C/Documents/SwanCaches/swan-core/preset/3.15.3/slaves/slaves.html }>
arg5:(null)
- 这个页面是我点击,一个含有原生播放器是我页面,且在播放中时的log,可以看出原生播放,定时触发一个播放时间更新的通知,渲染容器生成一个js格式的消息,逻辑容器接收js消息后,更新我们小程序层面代码的数值.请注意其中的vtype字段
//============================================
//原生函数
- (void)dispatchToSlaveEventJS:(id)arg1 completionHandler:(CDUnknownBlockType)arg2;
//部分参数log
2019-02-01 14:19:11.214991+0800 BaiduBoxApp[1757:256196] dispatchToSlaveEventJS
arg1:var event = new Event("message");event.message={"type":"abilityMessage","slaveId":"2","value":{"type":"video","params":{"id":"detailVideo","action":"timeupdate","e":{"wvID":"2","vtype":"timeupdate","data":"{\"videoId\":\"detailVideo\",\"duration\":206,\"currentTime\":0.13800000000000001}"}}}};document.dispatchEvent(event);
arg2:(null)
2019-02-01 14:19:12.212894+0800 BaiduBoxApp[1757:256196] dispatchToSlaveEventJS
arg1:var event = new Event("message");event.message={"type":"abilityMessage","slaveId":"2","value":{"type":"video","params":{"id":"detailVideo","action":"timeupdate","e":{"wvID":"2","vtype":"timeupdate","data":"{\"videoId\":\"detailVideo\",\"duration\":206,\"currentTime\":1.133}"}}}};document.dispatchEvent(event);
arg2:(null)
2019-02-01 14:19:13.211944+0800 BaiduBoxApp[1757:256196] dispatchToSlaveEventJS
arg1:var event = new Event("message");event.message={"type":"abilityMessage","slaveId":"2","value":{"type":"video","params":{"id":"detailVideo","action":"timeupdate","e":{"wvID":"2","vtype":"timeupdate","data":"{\"videoId\":\"detailVideo\",\"duration\":206,\"currentTime\":2.145}"}}}};document.dispatchEvent(event);
arg2:(null)
update_total_flow_bytes() called input bytes: 12669826,add after: 126698262019-02-01 14:19:14.212116+0800 BaiduBoxApp[1757:256196] dispatchToSlaveEventJS
arg1:var event = new Event("message");event.message={"type":"abilityMessage","slaveId":"2","value":{"type":"video","params":{"id":"detailVideo","action":"timeupdate","e":{"wvID":"2","vtype":"timeupdate","data":"{\"videoId\":\"detailVideo\",\"duration\":206,\"currentTime\":3.1419999999999999}"}}}};document.dispatchEvent(event);
arg2:(null)
2019-02-01 14:19:15.186957+0800 BaiduBoxApp[1757:256196] dispatchToSlaveEventJS
arg1:var event = new Event("message");event.message={"type":"abilityMessage","slaveId":"2","value":{"type":"video","params":{"id":"detailVideo","action":"pause","e":{"wvID":"2","vtype":"pause","data":"{\"videoId\":\"detailVideo\"}"}}}};document.dispatchEvent(event);
2019-02-01 14:45:52.650876+0800 BaiduBoxApp[1757:256196] dispatchToSlaveEventJS
arg1:document.body.style.pointerEvents = 'auto'
arg2:(null)
hook (SWANApp) 方法继续分析
//- (void)dispatchToMasterLifeCycleName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
//- (void)dispatchToMasterEventName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
//- (void)dispatchToMasterEventJS:(id)arg1 completionHandler:(CDUnknownBlockType)arg2;
- 疑似管理类,触发masterwebview原生方法执行js环境下的,生命周期函数
//原生函数
- (void)dispatchToMasterLifeCycleName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
//log
2019-02-01 16:58:57.998478+0800 BaiduBoxApp[384:20174] dispatchToMasterLifeCycleName
arg1:onShow
arg2:{
"wvID" : "1"
}
arg3:(null)
2019-02-01 16:58:58.164016+0800 BaiduBoxApp[384:20174] dispatchToMasterLifeCycleName
arg1:onAppShow
arg2:{
"appId" : "TbUaiGOSOjo23SOxWDA5UkjvIGNLOHwE",
"extraData" : {
},
"mtjCuid" : "AE2408DA37BF7CD89FA09BE25712A00DCEFB4BF51OHCTLQKKLN",
"cuid" : "AE2408DA37BF7CD89FA09BE25712A00DCEFB4BF51OHCTLQKKLN",
"clkid" : "",
"scene" : "1201004410071000"
}
- 疑似管理类,触发masterwebview原生方法,把整个_devtool注入js
//原生函数
- (void)dispatchToMasterEventJS:(id)arg1 completionHandler:(CDUnknownBlockType)arg2;
//log
2019-02-01 16:58:58.147050+0800 BaiduBoxApp[384:20174] dispatchToMasterEventJS
arg1:window.__san_devtool__.data
arg2:(null)
- 暂时未猜到
//原生函数
- (void)dispatchToMasterEventName:(id)arg1 eventParam:(id)arg2 completionHandler:(CDUnknownBlockType)arg3;
//部分参数log
2019-02-01 16:58:57.550525+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:AppReady
arg2:{
"root" : "",
"extraData" : {
},
"wvID" : 1,
"devhook" : "false",
"pageUrl" : "pages/index/index",
"appPath" : "/var/mobile/Containers/Data/Application/40889C2C-FBCC-414A-AE1E-D658E60ABD8A/Documents/SwanCaches/0f745bd975d2735b397186bf0f2e17f0/86",
"appConfig" : "{"pages":["pages\/index\/index","pages\/programActivity\/index","pages\/activity\/index","pages\/lottery\/index","pages\/program\/index","pages\/detail\/index","pages\/vote\/index","pages\/comment\/index","pages\/map\/index"],"networkTimeout":{"request":10000,"downloadFile":10000},"debug":true,"splitAppJs":true,"window":{"navigationBarTitleText":"触电短视频","backgroundTextStyle":"light","navigationBarTextStyle":"white","navigationBarBackgroundColor":"#ff6666"}}"
}
arg3:(null)
2019-02-01 16:58:57.795054+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:message
arg2:{
"message" : "{"type":"slaveLoaded","value":{"status":"loaded"},"slaveId":"1"}"
}
arg3:(null)
2019-02-01 16:58:57.998208+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:route
arg2:{
"toTabIndex" : -1,
"routeType" : "init",
"toPage" : "pages/index/index",
"toId" : "1",
"fromId" : "-1"
}
arg3:(null)
2019-02-01 16:58:58.144832+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:message
arg2:{
"message" : "{"type":"setMasterRainMonitorEndtime","value":{}}"
}
arg3:(null)
2019-02-01 16:58:58.145077+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:message
arg2:{
"message" : "{"type":"sendLogMessage","value":{"perfLogList":[{"actionId":"fe_slave_dispatch_start","timestamp":1549011537624},{"actionId":"fe_start_load_css","timestamp":1549011537629},{"actionId":"fe_start_load_app_css","timestamp":1549011537629},{"actionId":"fe_start_load_page_css","timestamp":1549011537629},{"actionId":"fe_end_load_app_css","timestamp":1549011537683},{"actionId":"fe_end_load_page_css","timestamp":1549011537683},{"actionId":"fe_end_load_css","timestamp":1549011537683},{"actionId":"fe_start_load_swan_js","timestamp":1549011537683},{"actionId":"slave_first_loaded","timestamp":1549011537793},{"actionId":"slave_js_parsed","timestamp":1549011537793},{"actionId":"fe_end_load_swan_js","timestamp":1549011537793},{"actionId":"fe_end_init_swan_js","timestamp":1549011537793},{"actionId":"fe_end_parse_swan_js","timestamp":1549011537728},{"actionId":"fe_slave_real_get_data","timestamp":1549011538079},{"actionId":"slave_first_recieve_data","timestamp":1549011538079},{"actionId":"fe_first_render_start","timestamp":1549011538082},{"actionId":"slave_first_rendered","timestamp":1549011538141},{"actionId":"fe_slave_preload_start","timestamp":1549010848871},{"actionId":"fe_slave_preload_end","timestamp":1549010850982}]}}"
}
arg3:(null)
2019-02-01 16:58:58.147983+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:message
arg2:{
"message" : "{"type":"abilityMessage","value":{"type":"rendered","params":[]},"slaveId":"1"}"
}
arg3:(null)
2019-02-01 16:58:58.148324+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:message
arg2:{
"message" : "{"type":"abilityMessage","value":{"type":"nextTickReach","params":{}},"slaveId":"1"}"
}
arg3:(null)
2019-02-01 16:58:58.149230+0800 BaiduBoxApp[384:20174] dispatchToMasterEventName
arg1:message
arg2:{
"message" : "{"type":"slaveAttached","slaveId":"1"}"
}
arg3:(null)