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 block

e.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接入怎么来实现这个抽象层。

本文转自: http://www.uustory.com/?p=1416

时间: 2024-10-18 17:12:00

SDK接入(U8SDK)——SDK抽象层的设计的相关文章

教你快速高效接入SDK——SDK接入抽象层的设计

题记:很多做游戏开发的人,估计都或多或少地接过渠道SDK,什么UC,当乐,91,小米,360......据统计国内市场当前不下于100家渠道,还包括一些没有SDK的小渠道.每个渠道SDK接入的方法呢,多是大同小异.但是,正是这些小异,又让SDK的接入,产生了无穷无尽的变数.所以,接入SDK之前,如果你没有经验,或者没有被SDK坑过,那么当你看到这系列文章的时候,你很幸运,你可以避免这一切了.如果你之前被坑过,而且还在继续被坑着,那么现在,就是你解脱的时刻. 上一篇文章,我们总体地分析并设计了一套

教你快速高效接入SDK——渠道SDK的接入(就是实现抽象层的接口而已)

题记:很多做游戏开发的人,估计都或多或少地接过渠道SDK,什么UC,当乐,91,小米,360......据统计国内市场当前不下于100家渠道,还包括一些没有SDK的小渠道.每个渠道SDK接入的方法呢,多是大同小异.但是,正是这些小异,又让SDK的接入,产生了无穷无尽的变数.所以,接入SDK之前,如果你没有经验,或者没有被SDK坑过,那么当你看到这系列文章的时候,你很幸运,你可以避免这一切了.如果你之前被坑过,而且还在继续被坑着,那么现在,就是你解脱的时刻. 先将之前的每一篇做个索引,方便亲们查阅

统一SDK接入(U8SDK)——总体思路和架构

题记:很多做游戏开发的人,估计都或多或少地接过渠道SDK,什么UC,当乐,91,小米,360……据统计国内市场当前不下于100家渠道,还包括一些没有SDK的小渠道.每个渠道SDK接入的方法呢,多是大同小异.但是,正是这些小异,又让SDK的接入,产生了无穷无尽的变数.所以,接入SDK之前,如果你没有经验,或者没有被SDK坑过,那么当你看到这系列文章的时候,你很幸运,你可以避免这一切了.如果你之前被坑过,而且还在继续被坑着,那么现在,就是你解脱的时刻. 完成一个SDK的接入并没有多少技术含量,但是能

Unity3d Android SDK接入解析(二)Unity3d Android SDK的设计与两种接入方式

一.前言 上篇说清楚了Unity和Android调用的方式,但很多实际接入的部分没有讲的很详细,因为重头在这篇,会详细讲述具体接入Android SDK的方式,和怎么去做一个方便Unity接入的SDK. 传送门: 前篇:Unity3d 与 Android之间的互相调用 http://blog.csdn.net/yang8456211/article/details/51331358 后篇:Unity3d Android SDK接入解析(三)接入Android Library的理解 http://

教你高速高效接入SDK——Unity统一接入渠道SDK(Android篇)

U8SDK的设计之初,就是为了可以支持各种游戏引擎开发的游戏,而不不过Android的原生平台.眼下一大半的手游,都是採用Unity3D和Cocos2dx开发,那么这里,我们就先来一步步给大家演示,用Unity开发的游戏,怎样通过U8SDK来高速地完毕多家渠道SDK的接入. Unity研发的手游,仅仅须要调用U8SDK抽象层就可以完毕多家渠道SDK的接入.而不须要在Unity中耦合各个渠道SDK,保证游戏层逻辑层的简单,以及SDK部分的绝对重用. 以下,我们看看,在Unity中调用U8SDK主要

教你快速高效接入SDK——Unity统一接入渠道SDK(Unity篇)

上一篇,我们着重讲解了用Unity开发的手游,在接入U8SDK时,Android中的部分.接下来,这一篇,我们就来看看,在Unity工程中,我们需要怎么调用上一篇中我们提供的各种接口,以及怎么接收Android中的消息通知,比如登录成功,切换帐号成功等. 在写代码之前,我们先要做一个简单的配置,在Project面板中,按照Unity官方的要求,新建一个Plugins文件夹,在Plugins文件夹下,再建立一个Android文件夹.这里,将会放置所有Android工程相关的文件. 1.将上一篇中,

SDK接入(2)之Android Google Play内支付(in-app Billing)接入

SDK接入(2)之Android Google Play内支付(in-app Billing)接入 继上篇SDK接入(1)之Android Facebook SDK接入整理完Facebook接入流程之后,再来整理下Google Play in-app Billing支付的接入流程.众所周知,Google Play是Google Android官方的应用商店,也是将应用发布到世界各地一个重要的渠道.支付作为盈利的一个重要手段,可以借助Google in-app Billing api来达到目的.

Unity3d Android SDK接入解析(四)通用的Android SDK接入中间件

一.前言 接入Android SDK正式告一段落,在这段时间里面,依次接入了华为.应用宝.小米.360等等大大小小十来个SDK,也算对Unity接入渠道SDK有了较为全面的理解,对各个渠道的坑也算深有体会....在接入过程中时间比较紧张,没办法抽空来进行总结深思.今天正好有空,便对之前的接入SDK的代码进行了一次重构,写了一个比较通用的Unity接入Android SDK的中间件,前人栽树,后人乘凉. 进入正题 如果有对一些只是有疑问的,可以看看我之前的三篇文章: 传送门: Unity3d An

TypeSDK免费手游多渠道SDK接入方案

摘要: TypeSDK,一个开源的统一手游渠道SDK接入框架,拥有80个海内外渠道,具备快速出包.分布式打包.分权限管理.产品数据打点等功能. 经历了头两年的爆发之后,手游也和端游.页游一样,进入了一个利润变薄.产业整合的过渡期.除了那些自有渠道的大厂商,如何找到新的用户来源始终是中小CP面临的最大问题,解决办法目前看来只有不断新接入渠道这一条.这就催生了一条新问题,接渠道也是一件非常耗时耗力的工作,里面各种危机暗藏.这就是为什么做了4年手游CP的星渠,转型去做统一渠道SDK接入框架--Type