IOS游戏渠道SDK抽象⼯程封装
iOS游戏渠道SDK抽象⼯程封装(上)
⼀款⼿机游戏,要是想挣钱,接⼊渠道SDK是很重要滴。但是渠道SDK有那么多家,每⼀家的接⼝也不⼀样,那么是否需要每⼀家渠道
SDK都来接⼊⼀次呢?游戏的研发同学,每次想到这边,都表⽰⼀个头,两个⼤。
那么为了给研发的同学减轻负担,让他们专⼼搞研发,给所有渠道SDK封装⼀个抽象⼯程,是很有必要的⼀件事情。这样,游戏接⼊⼀次抽
象⼯程就OK了,到时候要接⼊渠道SDK,只需要把⽂件替换⼀下,省时⼜省⼒,岂不是美美哒。
什么是渠道SDK的抽象⼯程?
抽象⼯程,可以说是渠道SDK的驱壳。这个驱壳,可以装下各种各样的渠道SDK。
虽然渠道SDK种类繁多,但是细⼼⼀看,他们的接⼝也是⼤同⼩异的。⼤体上有这么⼏个:
-初始化
-⽤户登陆
-⽤户退出
-⽤户⽀付
-⽤户中⼼
-⼯具栏打开关闭
摸清了他们的套路,咱们也可以⼤⼤⽅⽅地出⼿了。
抽象⼯程的总⼊⼝
游戏与抽象⼯程的所有交互,都是由这个类来完成。我们把它命名为SDKAccount。
先来个SDKAccount.h的代码
为了使我们的抽象⼯程更⽅便地使⽤,我们这边采⽤单例的模式,使⽤sharedInstance来获取抽象⼯程的单例。
然后⼤家是不是以为,接下来就是在SDKAccount.m⾥头来实现渠道SDK的代码啦。nonono,这样会使我们抽象⼯程的业务代码,和渠
道SDK的业务混杂在⼀起,使代码变得杂乱不堪,这是我所不能容忍的。我们把渠道SDK的代码,统统放在另外⼀个地⽅,这个后⾯再讲。
先来讲讲SDKAccount.m
获取单例,我们⽤最常见的gcd⽅式来创建。
//获取单例
+(instancetype)sharedInstance;
//sdk⽤户初始化
-(void)doInit:(NSString*)gameVersion;
//sdk登陆
-(void)doLogin;
//sdk退出
-(void)doLogout;
//sdk切换⽤户
-(void)doSwitchAccount;
//sdk⽀付
-(void)doPay:(SDKPayInfo*)sdkPayInfo;
//调⽤暂停页⾯
-(void)doPau;
//设置⼯具栏YES打开/NO关闭
-(void)doSetting:(BOOL)visible;
//打开⽤户中⼼
-(void)doUrCenter;
初始化
登陆
退出
⽀付
+(instancetype)sharedInstance{
staticSDKAccount*instance=nil;
staticdispatch_once_tonceToken=0;
dispatch_once(&onceToken,^{
instance=[[SDKAccountalloc]init];
});
returninstance;
}
-(void)doInit:(NSString*)gameVersion{
[[SDKContainersharedInstance]doThirdInit:lfgameVersion:gameVersion];
}
-(void)doLogin{
[[SDKContainersharedInstance]doLogin];
}
-(void)doLogout{
[[SDKContainersharedInstance]doLogout];
}
-(void)doPay:(SDKPayInfo*)sdkPayInfo{
SDKUr*sdkUr=[SDKUrsharedInstance];
if(==nil||==0){
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_NO_LOG
return;
}
if(sdkPayInfo==nil){
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_PARAM_E
return;
}
if([intValue]<=0||[length]==0||[Idlength]==0||[tIdlength]==0){
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_PARAM_E
return;
}
SDKPayReq*payReq=[[SDKPayReqalloc]init];
=;
rmUrId=[[SDKUrsharedInstance]uid];
erId=d;
ductId=tId;
ductName=tName;
rId=;
rName=me;
eId=;
eName=me;
eLevel=vel;
到这边,有些朋友会问,为什么doPay的代码会多出这么多?那是因为这些代码,是我们⾃⼰的业务逻辑。
在调⽤渠道SDK的⽀付之前,我们需要先把⽀付信息传到我们的服务器上⾯,⽣成⼀个订单号,这个订单号,会记录在数据库中,作为以后
游戏⽤户的⽀付凭据。⽣成完以后,再回传回来。这时候我们才能⽤这个订单号,来调⽤渠道SDK的⽀付接⼝。
暂停页⾯
eLevel=vel;
verId=Id;
verName=Name;
lId=[lfgetChannelId];
Id=[lfgetDeviceId];
ifyUri=Uri;
=;
el=el;
[payReqpost:^(NSHTTPURLRespon*respon,NSDictionary*data){
SDKLog(@"订单信息---%@",data);
NSNumber*code=data[@"code"];
if([codeintValue]==0){
NSDictionary*info=data[@"data"];
NSString*orderId=info[@"order_id"];
NSString*amountStr=[NSStringstringWithFormat:@"%d",[[payReqamount]intValue]/100];//单位为元
NSString*productId=ductId;
NSString*productName=ductName;
NSString*roleId=eId;
NSString*rverId=verId;
NSString*rverName=verName;
NSString*payDesc=[NSStringstringWithFormat:@"Product_%@",ductId];
NSDictionary*orderMsg=@{
@"orderId":CleanNil(orderId),
@"amountStr":CleanNil(amountStr),
@"productId":CleanNil(productId),
@"productName":CleanNil(productName),
@"roleId":CleanNil(roleId),
@"rverId":CleanNil(rverId),
@"rverName":CleanNil(rverName),
@"payDesc":CleanNil(payDesc)
};
[[SDKContainersharedInstance]doPayWithOrder:orderMsg];
}el{
[lfnotifitionCreateOrderError];
}
}failure:^(NSHTTPURLRespon*respon,NSDictionary*data,NSError*error){
[lfnotifitionCreateOrderError];
}];
}
-(void)doPau{
[[SDKContainersharedInstance]doPau];
}
有些渠道SDK(⽐如91助⼿),要求在按下home键回到主界⾯,再切换回游戏时,会弹出⼀个暂停页⾯,⽤来展⽰⼴告。这时就要在
AppDelegate中的applicationWillEnterForeground⽅法中,调⽤doPau这个⽅法。
⼯具栏(悬浮球)开
打开⽤户中⼼
切换账号
切换账号实际上也就是先退出,再调⽤登陆窗⼝。
盛放渠道SDK代码的容器
前⾯讲到,为了不使我们的代码变得杂乱不堪,我们把业务逻辑和渠道SDK的代码分开来。创建⼀个新的类,⽤来盛放渠道SDK代码。这个
类⼤家也猜到了,就叫做SDKContainer。
先上SDKContainer.h的代码。
-(void)doSetting:(BOOL)visible{
[[SDKContainersharedInstance]doSetting:visible];
}
-(void)doUrCenter{
[[SDKContainersharedInstance]doUrCenter];
}
-(void)doSwitchAccount{
[[SDKContainersharedInstance]doSwitchAccount];
}
⾸先⼀上来是⼀个协议,有协议就有⼈遵守。没错,这个协议是为SDKAccount准备的。
先在SDKAccount.h的头部写上
@interfaceSDKAccount:NSObject
1
然后在SDKAccount.m中实现这些⽅法
@protocolSDKContainerDelegate
-(void)initFinish:(NSDictionary*)initMsg;
-(void)loginFinished:(NSDictionary*)loginMsg;
-(void)logoutFinished:(NSDictionary*)logoutMsg;
-(void)payFinished:(NSDictionary*)payMsg;
//登录成功
-(void)notifitionLoginSuccess:(SDKUr*)sdkUr;
//登录失败
-(void)notifitionLoginError;
//登录取消
-(void)notifitionLoginCancel;
//注销成功
-(void)notifitionLogoutSuccess;
//创建订单失败
-(void)notifitionCreateOrderError;
//充值⽤户未登录
-(void)notifitionPayNoLogin;
//充值成功
-(void)notifitionPaySuccess;
//充值失败
-(void)notifitionPayError;
//充值取消
-(void)notifitionPayCancel;
//充值发货中
-(void)notifitionPayShipping;
//充值⽹络异常
-(void)notifitionPayNetError;
@end
-(void)initFinish:(NSDictionary*)initMsg{
[lfdoLogin];
}
-(void)loginFinished:(NSDictionary*)loginMsg{
//登录成功,开发者可继续游戏逻辑
SDKLoginReq*loginReq=[[SDKLoginReqalloc]init];
=loginMsg[@"code"];
=loginMsg[@"code"];
=loginMsg[@"uid"];
me=loginMsg[@"urname"];
me=loginMsg[@"nickname"];
[loginReqpost:^(NSHTTPURLRespon*respon,NSDictionary*data){
//打印⽇志
SDKLog(@"登录信息---%@",data);
NSNumber*code=data[@"code"];
if([codeintValue]==0){
NSDictionary*dataDic=data[@"data"];
SDKUr*sdkUr=[SDKUrsharedInstance];
=dataDic[@"uuid"];
=dataDic[@"check_token"];
rm=FYSDK_PLATFORM_NAME;
NSDictionary*ur=dataDic[@"ur"];
NSString*uid=ur[@"id"];
NSString*urname=ur[@"name"];
NSString*nickname=ur[@"nickname"];
if(!=0){
=;
}el{
=uid;
}
if(!=0){
me=me;
}el{
me=urname;
}
if(!=0){
me=me;
}el{
me=nickname;
}
r=sdkUr;
[lfnotifitionLoginSuccess:sdkUr];
}el{
[lfnotifitionLoginError];
}
}failure:^(NSHTTPURLRespon*respon,NSDictionary*data,NSError*error){
[lfnotifitionLoginError];
}];
}
-(void)logoutFinished:(NSDictionary*)logoutMsg{
[lfnotifitionLogoutSuccess];
}
-(void)payFinished:(NSDictionary*)payMsg{
}
//-------------------------各种通知-------------------------------
//登录成功
-(void)notifitionLoginSuccess:(SDKUr*)sdkUr{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_LOGINobject:lfurInfo:[SDKResponBadict:SDK_RESP_SUCCES
}
//登录失败
-(void)notifitionLoginError{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_LOGINobject:lfurInfo:[SDKResponBadict:SDK_RESP_LOGIN_E
}
//登录取消
-(void)notifitionLoginCancel{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_LOGINobject:lfurInfo:[SDKResponBadict:SDK_RESP_CANCEL_
}
//创建订单失败
-(void)notifitionCreateOrderError{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_CREATE_O
}
//注销成功
-(void)notifitionLogoutSuccess{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_LOGOUTobject:lfurInfo:[SDKResponBadict:SDK_RESP_SUCCE
}
//注销失败
-(void)notifitionLogoutError{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_LOGOUTobject:lfurInfo:[SDKResponBadict:SDK_RESP_PARAM
}
//充值⽤户未登录
-(void)notifitionPayNoLogin{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_NO_LOGIN]
}
//充值成功
-(void)notifitionPaySuccess{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_SUCCESS]]
}
//充值失败
-(void)notifitionPayError{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_PAY_ERRO
}
//充值取消
-(void)notifitionPayCancel{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_CANCEL_O
}
//充值发货中
-(void)notifitionPayShipping{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_PAY_ING]];
}
//充值⽹络异常
-(void)notifitionPayNetError{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAYobject:lfurInfo:[SDKResponBadict:SDK_RESP_NET_ERRO
}
//暂停页⾯关闭通知
-(void)notifitionPuaPageClo{
[[NSNotificationCenterdefaultCenter]postNotificationName:SDK_CALLBACK_PAUSE_PAGE_CLOSEobject:lfurInfo:nil];
}
在SDKAccount的doInit中,将lf传给SDKContainer。这样SDKContainer就可以回调SDKAccount啦。
上⾯的loginFinished⽅法,也是调⽤了我们的业务逻辑。在渠道SDK登陆完以后,需要将token发到我们的服务器中,然后再由我们的服
务器,转发给渠道SDK服务器去验证登陆。
我们来继续SDKContainer的内容。
有没有很眼熟,和SDKAccount很像,是吧。
然后是
SDKContainer.h
@interfaceSDKContainer:NSObject
+(instancetype)sharedInstance;
-(void)doThirdInit:(id
-(void)doLogin;
-(void)doLogout;
-(void)doPayWithOrder:(NSDictionary*)orders;
-(void)doPau;
-(void)doSetting:(BOOL)visible;
-(void)doUrCenter;
-(void)doSwitchAccount;
@end
#import"SDKContainer.h"
@interfaceSDKContainer()
@property(nonatomic)id
@end
@implementationSDKContainer
+(instancetype)sharedInstance{
staticSDKContainer*instance=nil;
staticdispatch_once_tonceToken=0;
dispatch_once(&onceToken,^{
instance=[[SDKContaineralloc]init];
});
returninstance;
}
-(void)doThirdInit:(id
te=delegate;
//----------------打印平台版本号---------------
NSString*platformVersion=@"";//更新SDK必填
NSLog(@"---PlatformVersion---%@",platformVersion);
//---------------sdk初始化代码-----------------
//0横屏1竖屏
if([SDK_CONFIG_ORIENTATIONisEqual:@"0"]){
//-----------sdk横屏设置-----------
}el{
//-----------sdk竖屏设置-----------
}
}
-(void)doLogin{
//-----------sdk登陆接⼝-----------
}
-(void)doLogout{
//-----------sdk退出接⼝-----------
}
-(void)doPayWithOrder:(NSDictionary*)orders{
NSString*orderId=orders[@"orderId"];//订单
NSString*amountStr=orders[@"amountStr"];//⾦额,单位为元
NSString*productName=orders[@"productName"];//商品名
NSString*roleId=orders[@"roleId"];//⾓⾊名
NSString*rverId=orders[@"rverId"];//区服id
NSString*payDesc=orders[@"payDesc"];//额外⽀付信息
//-----------sdk⽀付接⼝-----------
}
-(void)doPau{
//------------sdk暂停页⾯-------------
}
-(void)doSetting:(BOOL)visible{
if(visible){
//-----------sdk打开⼯具栏-----------
}el{
//-----------sdk关闭⼯具栏-----------
}
}
-(void)doUrCenter{
//-----------sdk打开个⼈中⼼-----------
}
-(void)doSwitchAccount{
[lfdoLogout];
[lfdoLogin];
}
//---------------渠道sdk回调接⼝----------------
这边我们为渠道SDK预留了位置,将来要接⼊渠道SDK的时候,只需要将渠道SDK的代码,放到中对应的位置就⾏
了。是不是很⽅便呢?
细⼼的朋友发现,多了⼀个m出来。这是因为有些渠道SDK是⽤C++和Obj-C混合写的。所以要求我们要将.m改
为.mm。平时我们使⽤.mm也不妨碍我们的代码。
当渠道SDK需要回调游戏的时候该怎么办?只需要调⽤⼀下delegate中的⽅法,就可以了。
*(1)初始化结束调⽤以下代码
*(2)登陆结束调⽤以下代码
*(3)登陆取消调⽤以下代码
*(4)登陆失败调⽤以下代码
*(5)⽀付成功调⽤以下代
(6)⽀付取消调⽤以下代码
*(7)⽀付失败调⽤以下代码
*(8)⽀付⽤户未登陆调⽤以下代码
*(9)创建订单失败调⽤以下代码
*(10)充值发货中调⽤以下代码
//-----------------------------------------
[teinitFinish:nil];
NSString*code=;//登陆时的token
NSString*uid=;//游戏账号的唯⼀值
NSString*urname=;//游戏账号名
NSDictionary*loginMsg=@{
@"code":code,
@"uid":uid,
@"urname":urname
};
[teloginFinished:loginMsg];
[tenotifitionLoginCancel];
[tenotifitionLoginError];
[tenotifitionPaySuccess];
[tenotifitionPayCancel];
[tenotifitionPayError];
[tenotifitionPayNoLogin];
[tenotifitionCreateOrderError];
*(11)充值⽹络异常调⽤以下代码
*(12)退出成功调⽤以下代码
然后由SDKAccount统⼀去回调游戏。
这样是不是将渠道SDK的代码和我们的业务代码,完全地分离开来了呢?
我们将渠道SDK的代码,和我们⾃⼰的业务代码分离,⼀个放在SDKContainer⾥⾯,⼀个放在SDKAccount⾥⾯。
这样做的好处,不⽌在于可以清晰地划分代码之间的界限,更重要的是,这样更加便于管理和维护。
试想⼀下,我们做抽象⼯程的⽬的是什么?是为了游戏可以不⽤频繁重复地接⼊渠道SDK嘛。那么怎么才能达到这个⽬的呢?
抽象⼯程的⽬录结构
我们将代码划分成两个部分:
第⼀部分是不⽤变动的部分,也就是说所有的渠道SDK,都来共⽤这部分的代码,是⼀些基础类。我们统⼀将他们放在⽂件夹Ba⾥⾯。
这个Ba⾥⾯,包含了我们所有⾃⼰的业务代码:⽐如SDKAccount,⽹络请求类,⼯具类等等。
第⼆部分是需要变动的部分,也就是说所有的渠道SDK都有各⾃⾃⼰的⼀份。我们统⼀将他们放在Replace⽂件夹下。这⾥⾯包含了
SDKContainer和渠道SDK的⼀些配置信息。
先给出⼀张⽬录截图:
这⾥写图⽚描述
ThirdLib⽤来存放渠道SDK的依赖库⽂件。
为什么我们要把⽬录的职责分得这么细呢?这是为了更好地在开发中起到⼀个分⼯明确地作⽤。
如何使⽤抽象⼯程
1.在实际开发过程中,我们先创建⼀个空的抽象⼯程。这时候SDKContainer中是还没有没有渠道SDK代码的。
[tenotifitionPayShipping];
[tenotifitionPayNetError];
[tenotifitionLogoutSuccess];
2.这时研发的同学,来把我们的抽象⼯程拿过去,然后在游戏代码中调⽤我们的SDKAccount⾥⾯的接⼝。抽象⼯程算是接好了。但是现在
还不能⽤,不是吗?你得到了我的驱壳,却没得到我的灵魂。。。不过这时研发的同学,可以把SDK的事情放下了,专⼼地去做研发。
3.负责专门接⼊SDK的同学,从商务⼿中拿到渠道SDK。然后他把抽象⼯程拷贝⼀份过来。拷贝完之后,把渠道SDK的依赖库,放到
ThirdLib⾥⾯。然后在SDKContainer中,填⼊渠道SDK的代码。接着在SDKConfig中,填⼊渠道SDK的配置信息。最后测试⼀下,看有
没有问题。没有问题再把它交给研发的同学。
4.研发的同学,拿到接好的渠道SDK的抽象⼯程,只需要动动⼿指头,把⾥⾯的Replace和ThirdLib⽂件夹拷贝到原来游戏⼯程中,将原来
的两个⽂件夹替换⼀下,OK,接⼊完成。
5.以后再有新的渠道SDK,只需要重复步骤3和步骤4,就可以了。这个过程中,研发的同学,只需要接⼊⼀次抽象⼯程,接下来的事情,
就是动动⼿指头,替换⼀下⽂件。接SDK的同学,也不⽤关⼼业务逻辑,只需要将SDK的代码,填⼊相应的位置就⾏。两个⼈都感觉⼯作量
瞬间减⼩了很多,嘿嘿。。
项⽬管理
有些朋友发现,说了这么多,只不过是替换⽂件夹⼀下⽽已,为什么要分为Ba和Replace?我把所有⽂件统统放在⼀个⽂件夹⾥⾯不⾏
啊,到时候我把这个⽂件夹替换⼀下就好了啊。
先来看看渠道的管理⽬录
从这张图可以发现,我们把Ba单独提出来了。我们知道,Xcode是可以使⽤引⽤的⽅式,引⽤⼀个⽂件夹⾥⾯的内容的,⽽不必⼀定要
拷贝⽂件到⼯程⾥⾯去。也就是说,渠道和Ba之间,是⼀个多对⼀的关系,每个渠道共⽤⼀个Ba。
为什么要这样做?假设有⼀天,你的⽼⼤叫你修改或者添加⼀些⾃家的业务逻辑。哈哈,幸好,我把Ba单独拿出来了。这样我只要修改
⼀份代码就可以了~
后记
尽管抽象⼯程还有很多事情要做,例如⽹络请求,⽤户信息记录,数据采集等等,但是这些都是属于业务逻辑的范畴了,这边也不⼀⼀介绍
了。希望抽象⼯程,对⼤家有帮助,让接⼊渠道SDK不再痛苦。。
转载出处
本文发布于:2023-03-16 00:18:14,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/a8fd63b0e86674766c1f4f145a7c8f66.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:手机悬浮球.doc
本文 PDF 下载地址:手机悬浮球.pdf
留言与评论(共有 0 条评论) |