U8SDK——渠道SDK的接入
上一篇(游戏接入SDK)我们展示了游戏在需要接入SDK时的调用。它仅仅调用抽象层提供的各个插件的单例包装类就可以了。而每个单例包装类里面,就是引用对应的插件接口。那么这个接口的实例化是怎么做了,上上一篇,我们说到,他是从asssets下面读取的配置。然后根据配置里填写的完整类名来实例化的。这个实现类就是在接入各个渠道的时候实现的。那么,本篇我们就来以UC渠道为例,将其接入到我们的U8 SDK中来。
首先,根据UC提供的文档,我们知道,需要接入登陆和支付两大功能。那么,对应的,在我们这里,我们就需要两个类,一个类实现抽象SDK的IUser接口,一个实现抽象SDK的IPay接口。那么,我们这里再回顾下,实现类需要注意的地方。在抽象层讲解的文章中,我们看各个插件的实例化过程:
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 |
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; } |
我们可以看到,我们是调用实现类的带有一个Activity参数的构造函数进行实例化的。这就要求我们在实现插件接口的时候,需要定义一个带有Activity参数的构造函数。我们看看UC的登陆实现类和支付实现类。
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 |
package com.u8.sdk; import com.u8.sdk.IUser; import com.u8.sdk.U8SDK; import android.app.Activity; public class UCUser implements IUser { private Activity context; public UCUser(Activity context) { this.context = context; this.initSDK(); } public void initSDK() { UCSDK.getInstance().initSDK(this.context, U8SDK.getInstance().getSDKParams()); } @Override public void login() { UCSDK.getInstance().login(this.context, U8SDK.getInstance().getSDKParams()); } } |
支付:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.u8.sdk; import com.u8.sdk.IPay; import com.u8.sdk.PayParams; import android.app.Activity; public class UCPay implements IPay { private Activity context; public UCPay(Activity context) { this.context = context; } @Override public void pay(PayParams data) { UCSDK.getInstance().pay(this.context, data); } } |
我们先看上面的登陆插件实现类,可以看到它实现了login接口,同时,定义了一个以Activity为参数的构造函数。在login方法里面,通过调用UCSDK这个单例类的login来完成登陆界面的调用。同时,注意到,登陆实现类里面还有一个initSDK方法,在实例化的时候调用。这个是因为UC SDK要求必须在游戏刚开始运行的时候,就初始化SDK。
同样的,支付实现类里面,实现了pay方法,也是通过调用UCSDK单例来完成支付界面的调用。
那么,大家看到,现在所有的问题都简单化了。就是需要在UCSDK这个单例类里面来实现所有UC SDK需要实现的功能。我们先看下UCSDK里面的代码:
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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
package com.u8.sdk; import org.json.JSONException; import org.json.JSONObject; import com.u8.sdk.ActivityCallbackAdapter; import com.u8.sdk.LoginResult; import com.u8.sdk.PayParams; import com.u8.sdk.SDKConfigData; import com.u8.sdk.U8Code; import com.u8.sdk.U8SDK; import com.u8.sdk.utils.SDKTools; import android.app.Activity; import android.app.ProgressDialog; import android.content.DialogInterface; import android.util.Log; import cn.uc.gamesdk.UCCallbackListener; import cn.uc.gamesdk.UCCallbackListenerNullException; import cn.uc.gamesdk.UCFloatButtonCreateException; import cn.uc.gamesdk.UCGameSDK; import cn.uc.gamesdk.UCGameSDKStatusCode; import cn.uc.gamesdk.UCLogLevel; import cn.uc.gamesdk.UCLoginFaceType; import cn.uc.gamesdk.UCOrientation; import cn.uc.gamesdk.info.FeatureSwitch; import cn.uc.gamesdk.info.GameParamInfo; import cn.uc.gamesdk.info.OrderInfo; import cn.uc.gamesdk.info.PaymentInfo; public class UCSDK { enum SDKState { StateDefault, StateIniting, StateInited, StateLogin, StateLogined } private SDKState state = SDKState.StateDefault; private boolean loginAfterInit = false; private ProgressDialog loadingActivity = null; private static UCSDK instance; private UCLogLevel logLevel = UCLogLevel.DEBUG; private int cpId; private int gameId; private int channel; private boolean debugMode = true; private UCSDK() { } public static UCSDK getInstance() { if (instance == null) { instance = new UCSDK(); } return instance; } private void parseSDKParams(SDKConfigData params) { this.gameId = params.getInt("UCGameId"); this.cpId = params.getInt("UCCpId"); this.channel = U8SDK.getInstance().getCurrChannel(); if (this.channel <= 0) { this.channel = params.getInt("UCChannel"); } this.debugMode = params.getBoolean("UCDebugMode"); } public void initSDK(Activity context, SDKConfigData params) { this.parseSDKParams(params); this.initSDK(context); } public void login(Activity context, SDKConfigData params) { this.parseSDKParams(params); this.login(context); } /** * 必接功能<br> * sdk初始化功能<br> */ public void initSDK(final Activity context) { this.state = SDKState.StateIniting; try { showWaitDialog(context); U8SDK.getInstance().setActivityCallback( new ActivityCallbackAdapter() { @Override public void onBackPressed() { ucSdkExit(context); } @Override public void onDestroy() { ucSdkDestoryFloatButton(context); } }); if (loginAfterInit) { // showProgressDialog(context); } GameParamInfo gpi = new GameParamInfo();// 下面的值仅供参考 gpi.setCpId(this.cpId); gpi.setGameId(this.gameId); gpi.setServerId(0); // 服务器ID可根据游戏自身定义设置,或传入0 // 在九游社区设置显示查询充值历史和显示切换账号按钮, // 在不设置的情况下,默认情况情况下,生产环境显示查询充值历史记录按钮,不显示切换账户按钮 // 测试环境设置无效 gpi.setFeatureSwitch(new FeatureSwitch(true, false)); // 设置SDK登录界面为横屏,个人中心及充值页面默认为强制竖屏,无法修改 // UCGameSDK.defaultSDK().setOrientation(UCOrientation.LANDSCAPE); // 设置SDK登录界面为竖屏 UCGameSDK.defaultSDK().setOrientation(UCOrientation.LANDSCAPE); // 设置登录界面: // USE_WIDGET - 简版登录界面 // USE_STANDARD - 标准版登录界面 UCGameSDK.defaultSDK().setLoginUISwitch(UCLoginFaceType.USE_WIDGET); // setUIStyle已过时,不需调用。 // UCGameSDK.defaultSDK().setUIStyle(UCUIStyle.STANDARD); UCGameSDK.defaultSDK().initSDK(context, this.logLevel, this.debugMode, gpi, new UCCallbackListener<String>() { @Override public void callback(int code, String msg) { Log.e("UCGameSDK", "UCGameSDK初始化接口返回数据 msg:" + msg + ",code:" + code + ",debug:" + debugMode + "\n"); hideWaitDialog(context); if (code == UCGameSDKStatusCode.SUCCESS) { if (state != SDKState.StateIniting) { U8SDK.getInstance().onResult( U8Code.CODE_INIT_FAIL, "uc sdk init fail. not initing. curr state is:" + state); return; } state = SDKState.StateInited; U8SDK.getInstance().onResult( U8Code.CODE_INIT_SUCCESS, "uc sdk init success"); if (loginAfterInit) { login(context); } } else { hideWaitDialog(context); state = SDKState.StateDefault; U8SDK.getInstance().onResult( U8Code.CODE_INIT_FAIL, "uc sdk init fail.err:" + msg); } } }); } catch (UCCallbackListenerNullException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private LoginResult encodeLoginResult(String sid) { String channel = "" + getChannel(); String expansion = ""; JSONObject ext = new JSONObject(); try { ext.put("ext", "u8"); expansion = ext.toString(); } catch (JSONException e) { e.printStackTrace(); } return new LoginResult(sid, channel, expansion); } public void login(final Activity context) { if (state.ordinal() < SDKState.StateInited.ordinal()) { loginAfterInit = true; initSDK(context); return; } if (!SDKTools.isNetworkAvailable(context)) { U8SDK.getInstance().onResult(U8Code.CODE_NO_NETWORK, "The network now is unavailable"); return; } try { state = SDKState.StateLogin; UCGameSDK.defaultSDK().login(context, new UCCallbackListener<String>() { @Override public void callback(int code, String msg) { Log.e("UCGameSDK", "UCGameSdk登录接口返回数据:code=" + code + ",msg=" + msg); // 登录成功。此时可以获取sid。并使用sid进行游戏的登录逻辑。 // 客户端无法直接获取UCID if (code == UCGameSDKStatusCode.SUCCESS) { state = SDKState.StateLogined; String sid = UCGameSDK.defaultSDK().getSid(); U8SDK.getInstance().onResult( U8Code.CODE_LOGIN_SUCCESS, sid); LoginResult result = encodeLoginResult(sid); U8SDK.getInstance().onLoginResult(result); // 执行悬浮按钮创建方法 ucSdkCreateFloatButton(context); // 执行悬浮按钮显示方法 ucSdkShowFloatButton(context); } else { state = SDKState.StateInited; U8SDK.getInstance().onResult( U8Code.CODE_LOGIN_FAIL, msg); } // 登录失败。应该先执行初始化成功后再进行登录调用。 if (code == UCGameSDKStatusCode.NO_INIT) { // 没有初始化就进行登录调用,需要游戏调用SDK初始化方法 initSDK(context); } // 登录退出。该回调会在登录界面退出时执行。 if (code == UCGameSDKStatusCode.LOGIN_EXIT) { // 登录界面关闭,游戏需判断此时是否已登录成功进行相应操作 } } }); } catch (UCCallbackListenerNullException e) { e.printStackTrace(); } } private PaymentInfo decodePayParams(PayParams payParams) { Log.i("UCSDK", "The payParams is " + payParams.toString()); PaymentInfo pInfo = new PaymentInfo(); // 创建Payment对象,用于传递充值信息 // 设置充值自定义参数,此参数不作任何处理, // 在充值完成后,sdk服务器通知游戏服务器充值结果时原封不动传给游戏服务器传值,字段为服务端回调的callbackInfo字段 if (!SDKTools.isNullOrEmpty(payParams.getExtension())) { pInfo.setCustomInfo(payParams.getExtension()); } // 非必选参数,可不设置,此参数已废弃,默认传入0即可。 // 如无法支付,请在开放平台检查是否已经配置了对应环境的支付回调地址,如无请配置,如有但仍无法支付请联系UC技术接口人。 pInfo.setServerId(0); pInfo.setRoleId(payParams.getRoleId()); // 设置用户的游戏角色的ID,此为必选参数,请根据实际业务数据传入真实数据 pInfo.setRoleName(payParams.getRoleName()); // 设置用户的游戏角色名字,此为必选参数,请根据实际业务数据传入真实数据 pInfo.setGrade("" + payParams.getRoleLevel()); // 设置用户的游戏角色等级,此为可选参数 // 非必填参数,设置游戏在支付完成后的游戏接收订单结果回调地址,必须为带有http头的URL形式。 // pInfo.setNotifyUrl("http://192.168.1.1/notifypage.do"); // 当传入一个amount作为金额值进行调用支付功能时,SDK会根据此amount可用的支付方式显示充值渠道 // 如你传入6元,则不显示充值卡选项,因为市面上暂时没有6元的充值卡,建议使用可以显示充值卡方式的金额 pInfo.setAmount(payParams.getPrice());// 设置充值金额,此为可选参数 return pInfo; } public void pay(Activity context, PayParams data) { try { if (!isInited()) { U8SDK.getInstance().onResult(U8Code.CODE_INIT_FAIL, "The sdk is not inited."); return; } if (!SDKTools.isNetworkAvailable(context)) { U8SDK.getInstance().onResult(U8Code.CODE_NO_NETWORK, "The network now is unavailable"); return; } PaymentInfo pInfo = decodePayParams(data); UCGameSDK.defaultSDK().pay(context, pInfo, new UCCallbackListener<OrderInfo>() { @Override public void callback(int code, OrderInfo orderInfo) { if (code == UCGameSDKStatusCode.NO_INIT) { // 没有初始化就进行登录调用,需要游戏调用SDK初始化方法 U8SDK.getInstance().onResult( U8Code.CODE_INIT_FAIL, "The SDK is not inited"); } if (code == UCGameSDKStatusCode.SUCCESS) { // 成功充值 if (orderInfo != null) { String ordereId = orderInfo.getOrderId();// 获取订单号 float orderAmount = orderInfo .getOrderAmount();// 获取订单金额 int payWay = orderInfo.getPayWay(); String payWayName = orderInfo .getPayWayName(); System.out.print(ordereId + "," + orderAmount + "," + payWay + "," + payWayName); U8SDK.getInstance().onResult( U8Code.CODE_PAY_SUCCESS, "uc pay success."); } } else { U8SDK.getInstance().onResult( U8Code.CODE_PAY_FAIL, "uc pay failed."); } if (code == UCGameSDKStatusCode.PAY_USER_EXIT) { // 用户退出充值界面。 U8SDK.getInstance().onResult( U8Code.CODE_PAY_FAIL, "The user is exit."); } } }); } catch (Exception e) { e.printStackTrace(); } } /** * 必接功能<br> * 悬浮按钮创建及显示<br> * 悬浮按钮必须保证在SDK进行初始化成功之后再进行创建需要在UI线程中调用<br> */ private void ucSdkCreateFloatButton(final Activity context) { U8SDK.getInstance().runOnMainThread(new Runnable() { public void run() { try { // 创建悬浮按钮。该悬浮按钮将悬浮显示在GameActivity页面上,点击时将会展开悬浮菜单,菜单中含有 // SDK 一些功能的操作入口。 // 创建完成后,并不自动显示,需要调用showFloatButton(Activity, // double, double, boolean)方法进行显示或隐藏。 UCGameSDK.defaultSDK().createFloatButton(context, new UCCallbackListener<String>() { @Override public void callback(int statuscode, String data) { Log.d("SelectServerActivity`floatButton Callback", "statusCode == " + statuscode + " data == " + data); } }); } catch (UCCallbackListenerNullException e) { e.printStackTrace(); } catch (UCFloatButtonCreateException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } /** * 必接功能<br> * 悬浮按钮显示<br> * 悬浮按钮显示需要在UI线程中调用<br> */ private void ucSdkShowFloatButton(final Activity context) { U8SDK.getInstance().runOnMainThread(new Runnable() { public void run() { // 显示悬浮图标,游戏可在某些场景选择隐藏此图标,避免影响游戏体验 try { UCGameSDK.defaultSDK().showFloatButton(context, 100, 50, true); } catch (UCCallbackListenerNullException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } /** * 必接功能<br> * 悬浮按钮销毁<br> * 悬浮按钮销毁需要在UI线程中调用<br> */ private void ucSdkDestoryFloatButton(final Activity context) { U8SDK.getInstance().runOnMainThread(new Runnable() { public void run() { // 悬浮按钮销毁功能 UCGameSDK.defaultSDK().destoryFloatButton(context); } }); } /** * 必接功能<br> * 当游戏退出前必须调用该方法,进行清理工作。建议在游戏退出事件中进行调用,必须在游戏退出前执行<br> * 如果游戏直接退出,而不调用该方法,可能会出现未知错误,导致程序崩溃<br> */ private void ucSdkExit(final Activity context) { UCGameSDK.defaultSDK().exitSDK(context, new UCCallbackListener<String>() { @Override public void callback(int code, String msg) { if (UCGameSDKStatusCode.SDK_EXIT_CONTINUE == code) { // 此加入继续游戏的代码 } else if (UCGameSDKStatusCode.SDK_EXIT == code) { // 在此加入退出游戏的代码 ucSdkDestoryFloatButton(context); System.exit(0); } } }); } public int getChannel() { return this.channel; } public boolean isInited() { return this.state.ordinal() >= SDKState.StateInited.ordinal(); } public boolean isLogined() { return this.state.ordinal() >= SDKState.StateLogined.ordinal(); } private void showWaitDialog(Activity context) { if (loadingActivity != null) { return; } loadingActivity = new ProgressDialog(context); loadingActivity.setIndeterminate(true); loadingActivity.setCancelable(true); loadingActivity.setMessage("正在初始化,请稍后..."); loadingActivity .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { state = SDKState.StateDefault; } }); loadingActivity.show(); } private void hideWaitDialog(Activity context) { if (loadingActivity == null) { return; } loadingActivity.dismiss(); loadingActivity = null; } } |
这个类似乎有点庞大。但是,没关系。我们慢慢地剥皮抽丝。首先,从我们实现类的调用入口进入。首先是initSDK。我们看看initSDK做了些什么.
initSDK首先进行了参数的解析:看UCUser的initSDK方法里面,我们通过U8SDK.getInstance().getSDKParams()方法获取到了当前SDK需要的参数。那么这些参数是什么呢。这些参数就是每个SDK在运行时,需要传入的参数。也就是你在接入渠道SDK之前,向渠道方申请的appID,appKey等信息。大家这里可能会有疑问,这些信息按说各个渠道都是不同的。这里怎么能通过抽象层获得到呢?说的没错,抽象层如何得到呢?这个要归功于我们后面要说的打包工具,我们会通过一个巧妙的设计来完成这一工作。这里你先知道,所有渠道的接入需要的appID等信息,这里都直接通过U8SDK.getInstance().getSDKParams()方法来获取就可以了。
紧接着,我们看到initSDK里面设置了U8SDK的IActivityListener接口。这是因为UC的SDK需要在activity的某些系统事件中完成相应的工作。
接下来,大家可以看到就是按照UC的Demo里面往下走就可以了。关键是初始化好之后的回调里,不管初始化成功还是失败,最好调用下U8SDK.getInstance().onResult()方法来向抽象层抛出一个状态信息。这样你在debug调试的时候,在游戏层实现的接口里加上输出或者Toast就可以及时看到这些状态信息,方便调试和查错。
然后,我们看login方法,login方法也一样,直接调用UC提供的登陆方法,关键是在登陆回调中,我们除了调用onResult方法之外,如果登陆成功,我们还需要调用U8SDK.getInstance().onLoginResult(result)方法。这个是因为游戏层会在onLoginResult中来处理登陆成功的回调,同时需要将SDK返回的数据封装到LoginResult类中。
最后,我们来看pay方法,pay方法也一样的简单。只是多了支付参数的解析。之前在说抽象层的实现时,我们说到了支付参数PayParams。这个类里所有的信息是游戏里面可以提供的。但是,每个渠道需要的可能各不相同。所以,这里各个渠道需要根据渠道自身的需要,按需所取。比如这里,我们通过decodePayParams方法从PayParams里面取到UC需要的参数。然后,同样的,我们在回调中调用onResult方法来提示状态信息。
对于其他的方法,向什么隐藏悬浮图标,显示悬浮图标都在UCSDK这里接入就可以了。需要在对应的地方加以调用。
那么,到这里我们UC SDK就算接入完毕了。接好之后他的目录结构大致如下:
这里大家也许看到了工程里面,有sdk_confgi.xml和sdk_manifest.xml。这里,我们先留个悬念。后面我们说到打包工具的时候,在回头来说。因为这里大家可以看到,我们没有提SDK需要在AndroidManifest.xml中设置的权限信息和一些Activity或者Service等数据。也不知道这样接好了之后,然后怎么用呢,怎么测试,怎么维护呢?所有这些我们后面来说。
本文出自 U8SDK技术博客,转载时请注明出处及相应链接。
本文永久链接: http://www.uustory.com/?p=1425