标签:anysdk原理 sdk接入 sdk接入教程 sdk教程 unity3d sdk
题记:很多做游戏开发的人,估计都或多或少地接过渠道SDK,什么UC,当乐,91,小米,360......据统计国内市场当前不下于100家渠道,还包括一些没有SDK的小渠道。每个渠道SDK接入的方法呢,多是大同小异。但是,正是这些小异,又让SDK的接入,产生了无穷无尽的变数。所以,接入SDK之前,如果你没有经验,或者没有被SDK坑过,那么当你看到这系列文章的时候,你很幸运,你可以避免这一切了。如果你之前被坑过,而且还在继续被坑着,那么现在,就是你解脱的时刻。
上一篇我们展示了游戏在需要接入SDK时的调用。它仅仅调用抽象层提供的各个插件的单例包装类就可以了。而每个单例包装类里面,就是引用对应的插件接口。那么这个接口的实例化是怎么做了,上上一篇,我们说到,他是从asssets下面读取的配置。然后根据配置里填写的完整类名来实例化的。这个实现类就是在接入各个渠道的时候实现的。那么,本篇我们就来以UC渠道为例,将其接入到我们的U8 SDK中来。
首先,根据UC提供的文档,我们知道,需要接入登陆和支付两大功能。那么,对应的,在我们这里,我们就需要两个类,一个类实现抽象SDK的IUser接口,一个实现抽象SDK的IPay接口。那么,我们这里再回顾下,实现类需要注意的地方。在抽象层讲解的文章中,我们看各个插件的实例化过程:
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; }
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()); } }
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); } }
同样的,支付实现类里面,实现了pay方法,也是通过调用UCSDK单例来完成支付界面的调用。
那么,大家看到,现在所有的问题都简单化了。就是需要在UCSDK这个单例类里面来实现所有UC SDK需要实现的功能。我们先看下UCSDK里面的代码:
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首先进行了参数的解析:看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等数据。也不知道这样接好了之后,然后怎么用呢,怎么测试,怎么维护呢?所有这些我们后面来说。
本文作者:小黑
教你快速高效接入SDK——渠道SDK的接入(就是实现抽象层的接口而已)
标签:anysdk原理 sdk接入 sdk接入教程 sdk教程 unity3d sdk
原文地址:http://blog.csdn.net/chenjie19891104/article/details/42614129