SDK接入(U8SDK)——SDK抽象层的设计
上一篇文章,我们总体地分析并设计了一套高效的SDK接入方案,也罗列出这套方案,我们需要完成的工作。这里再罗列并回顾下:
1、统一抽象的SDK接入框架
2、各个SDK接入实现
3、一键打包工具
4、统一的登陆认证中心和支付中心
5、对多个平台的支持,比如Unity3D,Cocos2D等
那么接下来这篇文章,我们就开始第一部分:抽象的SDK接入框架的实现。在实现之前,我们再深入地想一下,抽象层需要提供哪些接口。因为,对于每个游戏来说,都只需要接入这个抽象层,而所有SDK的接入就是实现这个抽象层。所以,这个抽象层设计的好坏,不仅影响到游戏的接入,同时也影响到各个渠道SDK的实现。
没有好的思路,我们可以看下AnySDK,或者棱镜SDK他们的宣传资料和文档,我们发现他们支持的组件有这些:

渠道SDK就不用说了,除了渠道SDK,他把部分支付SDK,广告SDK,分享SDK,统计SDK,消息推送SDK等都放到了这套统一SDK接入框架中来了。那么,作为我们这套抽象框架,我们也需要考虑以后可能会加入这些其他非渠道的SDK。所以,我们总体的设计思想是:
1、游戏各个渠道有一个主渠道SDK,比如UC,当乐,91等SDK。这个各个渠道只能同时有一个。不可能同时为UC也是91SDK
2、非渠道的功能性SDK,包括广告,分享,统计,推送等。这些东西,我们作为插件集成到这套抽象框架来。
3、所有SDK的实现可以很方便,而且结构比较统一
4、所有的渠道SDK也好,还是功能性SDK也好,SDK抽象层都抽象出对应的接口。方便游戏层的调用,也方便具体插件的实现。
那么,接下来,我们就根据前一篇我们画的那个登陆和支付流程图,和上面提到的总体设计思路来实现这个抽象层。上篇文章说道,我们这套东西暂且命名为u8 sdk,那么我们这个抽象层就叫U8 SDK。为了可以将各个功能作为插件式开发,我们抽象接口的时候,也将各个功能分开。
首先,我们定义两个接口,一个是登陆接口,一个是支付接口:
|
1
2
3
4
5
|
public interface IUser { public void login();} |
|
1
2
3
4
|
public interface IPay {public void pay(PayParams data);} |
这两 个接口定义好了,我们简单说下。一般SDK的登陆成功之后,都会拿到sid对吧,那这个登陆login方法并没有返回值,而是后面,我们会定义一个总的
SDK监听器,通过监听器来实现。支付需要游戏层传入一些支付参数。但是,有童鞋可能要问了,各个渠道需要的支付参数好像不太一样吧?是的亲,但是对于游
戏来说,我能提供的支付数据也就那么些。我全传给你,各个渠道sdk各自按需索取就可以了。那么,我们简单看下,我们这个PayParams类,有哪些属 性呢?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
public class PayParams{private String productId;private String productName;private int price;private int buyNum;private int coinNum;private String serverId;private String roleId;private String roleName;private int roleLevel;private String extension;public String getProductId() {return productId;}public void setProductId(String productId) {this.productId = productId;}public String getProductName() {return productName;}public void setProductName(String productName) {this.productName = productName;}public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}public int getBuyNum() {return buyNum;}public void setBuyNum(int buyNum) {this.buyNum = buyNum;}public int getCoinNum() {return coinNum;}public void setCoinNum(int coinNum) {this.coinNum = coinNum;}public String getServerId() {return serverId;}public void setServerId(String serverId) {this.serverId = serverId;}public String getRoleId() {return roleId;}public void setRoleId(String roleId) {this.roleId = roleId;}public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}public int getRoleLevel() {return roleLevel;}public void setRoleLevel(int roleLevel) {this.roleLevel = roleLevel;}public String getExtension() {return extension;}public void setExtension(String extension) {this.extension = extension;}} |
大家要问这里参数怎么抽象出来的,那么我告诉你,不是我拍脑袋想出来的,是参考AnySDK文档中提供的充值参数信息来的。(小贱一把)
两个接口有了,紧接着,上层游戏需要登录和支付的地方,怎么调用呢?对于游戏来说,这个接口需要new一个哪个实现?是UC还是当乐还是91呢?
所以,我们对每个插件定义一个单例的包装类。简单地说,就是怎么方便,怎么搞。这样就是方便上层游戏层得调用。那么,我们实现两个包装类:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import com.u8.sdk.IUser;import com.u8.sdk.ComponentFactory;import com.u8.sdk.U8SDK;/*** 用户插件**/public class U8User{private static U8User instance;private IUser userComponent;private U8User(){}public void init(){this.userComponent = (IUser)ComponentFactory.getInstance().initComponent(U8SDK.TYPE_LOGIN);}public static U8User getInstance(){if(instance == null){instance = new U8User();}return instance;}public void login(){if(userComponent==null){return;}userComponent.login();}} |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import com.u8.sdk.IPay;import com.u8.sdk.PayParams;import com.u8.sdk.ComponentFactory;import com.u8.sdk.U8SDK;/**** 支付插件**/public class U8Pay{private static U8Pay instance;private IPay payComponent;private U8Pay(){}public static U8Pay getInstance(){if(instance == null){instance = new U8Pay();}return instance;}public void init(){this.payComponent = (IPay)ComponentFactory.getInstance().initComponent(U8SDK.TYPE_PAY);}public void pay(PayParams data){if(this.payComponent == null){return;}this.payComponent.pay(data);}} |
关于这两个包装类,大家可以看到,有一个初始化init方法,然后就是所有的插件对应的方法,他把插件接口作为一个私有属性,在init方法里面,将对应的插件接口赋值了。然后在插件对应的方法里面,间接地调用插件对应的接口。那么,这里的关键就是ComponentFactory这个类。刚刚说了,我们这个后面可能有多个插件,所以,我们需要一个插件管理类。ComponentFactory就是我们的插件管理类:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
import java.io.IOException;import java.io.StringReader;import java.util.HashMap;import java.util.Map;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;import com.u8.sdk.utils.SDKTools;import android.annotation.SuppressLint;import android.app.Activity;import android.util.Log;import android.util.Xml;@SuppressLint("UseSparseArrays")public class ComponentFactory {private static ComponentFactory instance;private Map<Integer, String> supportedComponents;private ComponentFactory(){supportedComponents = new HashMap<Integer, String>();}public static ComponentFactory getInstance(){if(instance == null){instance = new ComponentFactory();}return instance;}public void init(Activity context){loadComponentInfo();}private boolean isSupportComponent(int type){return supportedComponents.containsKey(type);}private String getComponentName(int type){if(supportedComponents.containsKey(type)){return supportedComponents.get(type);}return null;}public SDKConfigData getSDKConfigData(){Map<String, String> configs = SDKTools.getAssetPropConfig(U8SDK.getInstance().getContext(), "developer_config.properties");return new SDKConfigData(configs);}@SuppressWarnings({ "unchecked", "rawtypes" })public Object initComponent(int type){Class localClass = null;try {if(!isSupportComponent(type)){Log.e("U8SDK", "The config of the U8SDK is not support plugin type:"+type);return null;}String name = getComponentName(type);localClass = Class.forName(name);} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();return null;}try {return localClass.getDeclaredConstructor(new Class[]{Activity.class}).newInstance(new Object[]{U8SDK.getInstance().getContext()});} catch (Exception e) {e.printStackTrace();}return null;}private void loadComponentInfo(){String xmlStr = SDKTools.getAssetConfigs(U8SDK.getInstance().getContext(), "plugin_config.xml");Log.e("The plugin Str:", xmlStr);XmlPullParser parser = Xml.newPullParser();try {parser.setInput(new StringReader(xmlStr));int eventType = parser.getEventType();while(eventType != XmlPullParser.END_DOCUMENT){switch(eventType){case XmlPullParser.START_TAG:String tag = parser.getName();if("plugin".equals(tag)){String name = parser.getAttributeValue(0);int type = Integer.parseInt(parser.getAttributeValue(1));this.supportedComponents.put(type, name);Log.e("u8_plugin", "Curr Supported Plugin: "+type+"; name:"+name);}}eventType = parser.next();}} catch (XmlPullParserException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}} |
大家可以看到initComponent方法里面,就是判断当前插件是否支持,如果支持,则从supportedComponents 里 面根据当前插件类型取到对应插件实现类的完整类名。通过Class.forName().newInstance()进行初始化。那么,我们怎么知道支持 哪些插件呢?怎么得到当前支持的各个插件的实现类呢?大家也许已经看到了,这个管理类中,也有一个初始化init方法。在init方法中,调用了 loadComponentInfo方法来加载当前支持的插件信息。大家可以看到它是从assets目录下的plugin_config.xml配置来读 取的。关于这个plugin_config.xml怎么生成的,我们后面说打包工具的时候,会详细讲到。这个文件不是我们手动写的,而是打包工具在生成各 个渠道包的时候动态生成的。
为了在SDK抽象层和SDK实现层传递数据,我们定义一个监听器:
|
1
2
3
4
5
6
7
8
|
package com.u8.sdk;public interface IU8SDKListener {public void onResult(int code, String msg);public void onLoginResult(LoginResult result);} |
这个 监听器,我们定义了两个接口,一个是onResult,是SDK实现层传递的状态信息,比如SDK初始化成功,SDK初始化失败,SDK登陆成功,登陆失 败等信息。而onLoginResult()这个接口,就是之前说到的,登陆成功之后,SDK实现层需要调用该接口,将封装好的登陆结果,返回给SDK抽 象层。这个LoginResult里面,就包含sid信息。
为了能够将我们刚刚说的这些插件,插件包装类,插件管理类,和事件监听接口整合到一起,我们最后定义一个总的单例类,也是我们整个抽象层的核心纽带:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
package com.u8.sdk;import com.u8.sdk.components.U8Pay;import com.u8.sdk.components.U8User;import android.app.Activity;import android.content.Intent;import android.os.Handler;import android.os.Looper;import android.util.Log;public class U8SDK{public static final int TYPE_LOGIN = 1;public static final int TYPE_PAY = 2;private static U8SDK instance;private Activity context;private Handler mainThreadHandler;private SDKConfigData developInfo;private IU8SDKListener listener;private IActivityListener activityCallback;private U8SDK(){mainThreadHandler = new Handler(Looper.getMainLooper());}public static U8SDK getInstance(){if(instance == null){instance = new U8SDK();}return instance;}public SDKConfigData getSDKParams(){return developInfo;}public int getCurrChannel(){if(this.developInfo == null || !this.developInfo.contains("U8_Channel")){return 0;}return this.developInfo.getInt("U8_Channel");}public void setSDKListener(IU8SDKListener listener){this.listener = listener;}public void setActivityCallback(IActivityListener callback){this.activityCallback = callback;}public void init(Activity context){this.context = context;ComponentFactory.getInstance().init(context);developInfo = ComponentFactory.getInstance().getSDKConfigData();U8User.getInstance().init();U8Pay.getInstance().init();}public void runOnMainThread(Runnable runnable){if(mainThreadHandler != null){mainThreadHandler.post(runnable);return;}if(context != null){context.runOnUiThread(runnable);}}public Activity getContext(){return this.context;}public void onResult(int code, String msg){Log.e("U8SDK Action Result:", "code:"+code+";msg:"+msg);if(listener != null){listener.onResult(code, msg);}}public void onLoginResult(LoginResult result){if(listener != null){listener.onLoginResult(result);}}public void onActivityResult(int requestCode, int resultCode, Intent data) {if(this.activityCallback != null){this.activityCallback.onActivityResult(requestCode, resultCode, data);}}public void onBackPressed(){if(this.activityCallback != null){this.activityCallback.onBackPressed();}}public void onPause() {if(this.activityCallback != null){this.activityCallback.onPause();}}public void onResume() {if(this.activityCallback != null){this.activityCallback.onResume();}}public void onNewIntent(Intent newIntent) {if(this.activityCallback != null){this.activityCallback.onNewIntent(newIntent);}}public void onStop() {if(this.activityCallback != null){this.activityCallback.onStop();}}public void onDestroy() {if(this.activityCallback != null){this.activityCallback.onDestroy();}}public void onRestart() {if(this.activityCallback != null){this.activityCallback.onRestart();}}} |
大家可以看到,我们这里,通过U8SDK这个单例,将所有的东西进行了连接和整合。在init方法里面,我们init插件管理类,也init所有的插件包装类。然后,对事件监听器也进行一个简单的包装。使得SDK实现层的调用简单方便,游戏层的调用也简单方便。
最后,因为有的SDK,需要在Activity的系统事件中做一些处理操作,而Activity是在游戏接入我们这个抽象层时,传递进来的,所以,我们在抽象层定义了一个Activity事件监听器:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.u8.sdk;import android.content.Intent;public interface IActivityListener {public void onPause();public void onResume();public void onRestart();public void onBackPressed();public void onNewIntent(Intent newIntent);public void onStop();public void onDestroy();public void onActivityResult(int requestCode, int resultCode, Intent data);} |
这样,整个SDK接入的抽象层就差不多了。还有一些细节,我们可以后面边开发,边迭代,边完善。后面我们将用实例来看看,游戏层怎么调用这个抽象层SDK,以及具体的SDK接入怎么来实现这个抽象层。
本文出自 优优网事,转载时请注明出处及相应链接。
SDK接入(U8SDK)——SDK抽象层的设计的更多相关文章
- 统一SDK接入(U8SDK)——总体思路和架构
题记:很多做游戏开发的人,估计都或多或少地接过渠道SDK,什么UC,当乐,91,小米,360……据统计国内市场当前不下于100家渠道,还包括一些没有SDK的小渠道.每个渠道SDK接入的方法呢,多是大同 ...
- 教你高速高效接入SDK——Unity统一接入渠道SDK(Android篇)
U8SDK的设计之初,就是为了可以支持各种游戏引擎开发的游戏,而不不过Android的原生平台.眼下一大半的手游,都是採用Unity3D和Cocos2dx开发,那么这里,我们就先来一步步给大家演示,用 ...
- SDK接入(2)之Android Google Play内支付(in-app Billing)接入
SDK接入(2)之Android Google Play内支付(in-app Billing)接入 继上篇SDK接入(1)之Android Facebook SDK接入整理完Facebook接入流程之 ...
- 手机游戏渠道SDK接入工具项目分享(三)拨开云雾是个坑
一直在纠结是先写框架设计还是先写掉过的坑,最后本这娱乐大众的态度先写掉过的坑让大家乐呵下. 项目开发过程中遇问题无数,回顾下8个大坑照成了项目一定程度上延期甚至返工. 项目一开始几个人把现有3家主流的 ...
- 【Unity游戏开发】SDK接入与集成——小白入门篇
一.简介 通常一款游戏开发到后期,一般都会涉及到第三方SDK的接入与集成,对于不熟悉SDK接入的同学来说,接SDK每次都是云里雾里,而熟悉SDK接入的同学又觉得不断地重复做接入SDK工作这样没有成就感 ...
- Android开发SDK接入机智云,智能家居实现APP远程控制多设备
代码地址如下:http://www.demodashi.com/demo/12847.html 一.前言. 此框架只用了一周星期做了出来,因为对机智云的框架比较熟悉了 !期间SDK初始化出了问题,去咨 ...
- 手机游戏渠道SDK接入工具项目分享(二)万事开头难
一般接到任务后程序员们通常都开始着手进行技术调研了,但我这活是项目负责人.还有一大堆事情要先期准备,没人能帮忙. 一.人力配置 考虑的之前已经有一波人搞了大半年,但没有起色,先期也没有太大人力需求,所 ...
- SDK接入(3)之iOS内支付(In-App Purchase)接入
SDK接入(3)之iOS内支付(In-App Purchase)接入 继整理了Android平台的SDK接入过程.再来分享下iOS平台的内支付(In-App Purchase)接入,作为笔者在游戏开发 ...
- SDK接入(1)之Android Facebook SDK接入
SDK接入(1)之Android Facebook SDK接入 由于游戏已上线,且处于维护阶段,所以有空写写各种SDK接入过程和遇到的问题,也当作一种工作总结.SDK接入主流分为这么几类,登录.支付. ...
随机推荐
- oracle生成单据号
--创建单据号存放表 CREATE TABLE BU_TAB( DOC_NUM NUMBER --生成的单据号 ); --单据号 create table cux_doc_num( tab ), -- ...
- 【转】MySQL连接超时断开的问题
这遍文章转过来做个笔记,时不时看看. 转:http://blog.csdn.net/nethibernate/article/details/6658855 Exception如下: org.hi ...
- CoordinatorLayout+TabLayout+ViewPager
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.C ...
- WCF、WebAPI、WCFREST、WebService之间的区别
注明:转载 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下,你有很多的选择来构建一个HTTP Services.我 ...
- VS2015 +EF6 连接MYSQL数据库生成实体
VS2015 +EF6 连接MYSQL数据库生成实体 已安装软件:VS2015 XAMPP Control Panel(Mysql服务器) ...
- I/O复用
1.I/O模型 一个输入操作通常包括两个不同阶段:等待数据准备好:从内核到进程拷贝数据. 阻塞I/O模型 非阻塞I/O模型 I/O复用模型:内核发现进程指定的一个或多个I/O条件就绪,它就通知进程,由 ...
- Java(String)
1>String类型判等 (1)请运行以下示例代码StringPool.java,查看其输出结果.如何解释这样的输出结果?从中你能总结出什么? package StringPool; publi ...
- (转)selenuim-webdriver注解之@FindBy、@FindBys、@FindAll的区别
selenium-webdriver中获取页面元素的方式有很多,使用注解获取页面元素是其中一种途径, 方式有3种:@FindBy.@FindBys.@FindAll.下文对3中类型的区别和使用场景进行 ...
- js函数和运算符
函数是由事件驱动或者它被调用时执行可重复使用的代码块. <script> function myFunction(){ Alert(“hello World!”): } </scri ...
- Python中获取异常(Exception)信息
异常信息的获取对于程序的调试非常重要,可以有助于快速定位有错误程序语句的位置.下面介绍几种python中获取异常信息的方法,这里获取异常(Exception)信息采用try...except...程序 ...