一 跳转native页面需求入口来源分析
跳转native页面的源头分五类:
1.第三方app唤起跳转,包含短信;这类多用于第三方市场商务合作以及运营活动;
2.推送消息指令跳转;这类可帮助运营提高老用户活跃,提高转化;
3.服务器下发指令跳转;(长链接场景服务端主动推消息)
4.App native内部跳转;iOS自身技术设计需要;
5.native与h5的交互跳转;在电商app中,十分广泛,可帮助产品更快试错,更快速的迭代更新;
分析了来源和意义,我们来看看电商app实现任意入口的任意跳转到底有什么好处呢?
二 电商app实现任意跳转app页面的好处
1.从产品上:服务端即可灵活的控制app,满足产品天马行空的跳转想象,更灵活的满足产品需求;
2.从运营支持上:我们要明白任何没有着落页的短信及推送消息,都是耍流氓,否则对点击了消息而没有看到着落的用户就是一种伤害;而它能解决点击每个活动消息都可以自定义着落页面;
3.从技术上:当两位领导意见分歧,一个要跳a页面,一个要跳转到b页面,此时则可搞a/b test,再结合BI数据分析,分析转化;最终帮助公司沉淀最优方案,提高转化,最终实现理想IPO上市;这就是技术的力量(哈哈平和领导的关系,维护了公司的团结稳定#共勉#)
估计大部分技术都没有意识到自己还有这种力量,以上对话勿让领导听到#奸笑#
这样看起来无论哪个入口要跳转哪个页面,技术上去做好它都变得十分有意义!
三 河狸家app早期协议
(因安全原因,由于苹果可通过class-dump命令,反编译处理,若直接暴露类名/方法/对象会引来安全问题,故以下代码的方法和类名都经过处理,命名也不规范,请谅解,注意思路)
背景:
设计理念,如同http://访问一样,只需要一个知道Url抛给底层服务,即可访问想要到达的页面,而上层工程师不需要关注中间如何访问及如何跳转;
由此,河狸家app定义了一个openUrl,来控制所有的页面跳转访问;
下面来看openUrl结构:
首先要建立协议:
1.协议头定义:考虑到第三方app唤起的原因,我们将协议头定义为URL Schemes(如微信的weixin://),这里考虑到安全问题,则隐藏河狸家app URL Schemes;用“HLJ://”代替
~urlString 形如 @"HLJ://page?jsonData={json}";
~1、区分事件类型 如"page"
~2、获取json
json对象结构形如
{
"hljType” : enum, //跳转页面标示 建立一个页面映射表来对应enum
“hljPageN” : “作品详情”,//页面名称
“jData” : {
“proId” : “sldkjf3l2”
}
}
json对应的model对象ScEntity如图:
制定完协议结构,下面来看看过程核心代码结构:
核心管理类ScManager,负责接收openUrl协议,校验协议,解析json,定向跳转;
工程师在开发过程只需要注意在openurl中填写跳转目的页面,以及model丢出数据即可,其他则不用关注;
第一步
首先校验跳转协议,必须要遵循HLJ://才可进入跳转逻辑,否则认为非法;
再解析jsondata,并解码son串;
第二步
将json解析成字典,利用MJ将数据转成SceneBaseEntity对象
第三步,通过枚举映射表对应跳转类,并拼接model
再通过
TabBarController *rootNav = (TabBarController *) kWindow.rootViewController;
[[rootNav.viewControllers objectAtIndex:rootNav.selectedIndex] pushViewController:controller animated:YES];
实现了跳转;
优点:
1.直观;
2.工程师编码只关注目的,和对应的唯一model。
缺点:
1.它还是不够灵活,每个页面都需要在枚举表中对应上映射,无法做到不发版本即可跳转无映射的页面;
2.当项目逐渐扩大,页面无穷多个时,产品的野心也在膨胀后,要写一堆的switch判断;
那还是不完全满足啊~~~~
别急!!!!来看第四段~~~~~
四 如何设计一套协议另app能够自由任意的跳转呢?
核心:runtime运行时机制+组件化
(这里插个题外话,无论老人代码写的怎么样,都是带动的创业公司走过风雨,尊重与感谢,勿吐槽多敬重!!!)
注意:由于旧工程需要兼容,还考虑这套设计中的openurl协议涉及安卓/iOS /前端/后端,考虑团队,则需要一个过渡方案。于是只能在旧工程上switch case,并且为了兼容旧版本,最新的运行时动态跳转设计中添加了一个enum为PgType_Runtime=100;来判断走新的跳转机制;
并且我们的ScEntity增加了三个属性 cname/mcname/moname
而我们的openUrl所对应json结构也发生了如下变化;
~1、区分事件类型 如"page"
~2、获取json
json对象结构形如
{
“type” : enum, //跳转页面标示 建立一个页面映射表来对应enum
“pgName” : “作品详情”,//页面名称
“cName” : “ViewController”,//页面class名
“mCName” : “Model”,//model class名
“mOName” : “model”,//model对象名
“jdata” : {
“proId” : “sldkjf3l2”
}
}
例如要跳转作品列表:
"openUrl": "hlj://pg?jData={\"hljtype\":100,\"cName\":\"SearchtResultController\",\"mCName\":\"SeachCModel\",\"mObjName\":\"searchC\",\"pgN\":\"作品列表\",\"data\":{\"artType\":1,\"cate\":\"tag_m\"}}}"}"
第一步,
通过cName,获取cName所对应的类名pageclass,通过pageclass创建唤起的目标页面对象;
第二步,
通过mcName去获取mcName所对应的类名modelclass;
再通过objc_getClass(modelclass)获取modelclass的isa指针指向(唤起的目标页面)所对应的modelc类对象;
假如不存在modelc类,就去创建一个nsobject类,nsobject是对象的root类;([NSObject class]只是返回一个NSobject类),那么superClass为一个NSobject类;
使用objc_allocateClassPair为"class pair"分配空间,来创建一个NSobject子类;
(什么是“class pair"? objc_allocateClassPair只返回一个值:Class。)
(拓展:如果想要为类添加方法可使用class_addMethod添加了一个方法,如class_addMethod(superClass, @selector(report), (IMP)ReportFunction, "v@:");
@selector(report)获取一个SEL类型,IMP是oc实现代码块的地址,类时函数指针,通过他可以直接访问人意一个方法,免去发送消息的代价;imp(对象自己(self),方法标示SEL,第三个是方法的参数);通过IMP直接调用方法 等效调用:[self SEL:参数]; //另外增加实 例变量用class_addIvar)
注册你创建的这个类,使其可用;
第三步,
遍历外部传入的参数data的key;
利用kvc对model对象每个属性进行赋值;
伪代码表现model.key = obj;//大致是这样一个形式
且要注意一定要去判断key所对应的model中的属性(名字与key一致)是否存在;(方法在最后代码片段里)
这样就得到一个完整数据的model;
第四步,
model也是一个page页面的属性,则可以同样的方式将model赋值给pageclass
最后为检查属性是否存在的代码片段:
这样就愉快的做到了在不发布版本的前提下实现任意跳转;
五 技术上总结
协议跨平台兼容性:安卓也是走的同样的一套设计协议,这样安卓iOS都不需要发布版本则可以做到实现任意页面跳转;
架构提升:对团队工程师的模块化/封装性的要求比较高,提升app的架构设计;
开发工程师开发成本:不需要关注如何跳转,只需要调用一段代码,将想要到达的目的页面class以及传递的数据model告知给消息中心即可;
解除模块间的耦合;