码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-01-11 21:43:01      阅读:578      评论:0      收藏:0      [点我收藏+]

标签:anysdk原理   sdk接入   sdk接入教程   sdk教程   unity3d sdk   

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



先将之前的每一篇做个索引,方便亲们查阅:


记小黑——统一SDK接入框架(类似棱镜和AnySDK)

教你快速高效接入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;
	}

我们可以看到,我们是调用实现类的带有一个Activity参数的构造函数进行实例化的。这就要求我们在实现插件接口的时候,需要定义一个带有Activity参数的构造函数。我们看看UC的登陆实现类和支付实现类。

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);
	}

}

我们先看上面的登陆插件实现类,可以看到它实现了login接口,同时,定义了一个以Activity为参数的构造函数。在login方法里面,通过调用UCSDK这个单例类的login来完成登陆界面的调用。同时,注意到,登陆实现类里面还有一个initSDK方法,在实例化的时候调用。这个是因为UC SDK要求必须在游戏刚开始运行的时候,就初始化SDK。

同样的,支付实现类里面,实现了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。我们看看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等数据。也不知道这样接好了之后,然后怎么用呢,怎么测试,怎么维护呢?所有这些我们后面来说。


本文作者:小黑


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

标签:anysdk原理   sdk接入   sdk接入教程   sdk教程   unity3d sdk   

原文地址:http://blog.csdn.net/chenjie19891104/article/details/42614129

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!