第二部分:项目需求开发
基础库搭建好了之后就是根据项目需求进行实际功能开发了,因为不同的项目有不同的项目需求,我简单的以项目初始化、账号登录/切换账号/账号登出、支付三大功能点来进行框架的代码实现。
1、需求开发 - Manager控制模块
Manager模块是SDK的核心模块,主要负责业务的功能实现及逻辑控制。根据项目需求,暂定为初始化Manager、账号Manager、支付Manager。
初始化Manager:处理SDK的初始化逻辑,全局参数缓存、环境切换、权限问题等。
public class InitManager {
private final String TAG = getClass().getSimpleName();
private volatile static InitManager INSTANCE;
private InitManager() {
}
public static InitManager getInstance() {
if (INSTANCE == null) {
synchronized (InitManager.class) {
if (INSTANCE == null) {
INSTANCE = new InitManager();
}
}
}
return INSTANCE;
}
/**
* 加载SDK项目配置入口插件(这是项目最开始加载的)
* @param context 上下文
* @param isdebug 日志调试开关
*/
public void initApplication(Application cxt, Context context, boolean isdebug){
ApplicationCache.init(cxt);
LogUtils.setDebugLogModel(isdebug);
ProjectManager.init(context).loadAllProjects();
//聚合SDK加载渠道插件
ChannelManager.init(context).loadChannel();
}
private static Handler sApiHandler;
private static boolean initState = false;
/**
* SDK初始化逻辑
* @param activity
* @param callBackListener
*/
public void init(final Activity activity, final String gameid, final String gamekey, final CallBackListener callBackListener) {
if (sApiHandler == null) {
HandlerThread ht = new HandlerThread("project_sdk_thread",
Process.THREAD_PRIORITY_BACKGROUND);
ht.start();
sApiHandler = new Handler(ht.getLooper());
}
Runnable r = new Runnable() {
@Override
public void run() {
//1、初始化全局缓存变量
BaseCache.init(activity.getApplication());
BaseCache.getInstance().put(KeyConfig.GAME_ID,gameid);
BaseCache.getInstance().put(KeyConfig.GAME_KEY,gamekey);
//2、初始化SDK参数
SDKInfoCache.getDefault(activity.getApplication());
//3、初始化持久化数据
SharePreferencesCache spCache = new SharePreferencesCache(activity);
spCache.init();
//4、加载功能插件
PluginManager.init(activity).loadAllPlugins();
//5、初始化域名配置
UrlConfig.initUrl();
//6、开始初始化逻辑
startInitLogic(activity,callBackListener);
}
};
sApiHandler.post(r);
}
/**
* 真正的初始化逻辑
*/
private void startInitLogic(final Activity activity, final CallBackListener callBackListener){
//-----------------------------已初始化完成--------------------------------
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
setInitState(true);
callBackListener.onSuccess(null);
}
});
}
/**
* 初始化功能插件()
*/
private void initFunctionPlugin(Activity activity){
//腾讯bugly日志收集
}
public void setInitState(boolean state) {
initState = state;
//将当前状态存储到全局变量供其他模块插件使用
BaseCache.getInstance().put(KeyConfig.IS_INIT,initState);
}
public boolean getInitState() {
return initState;
}
}
账号Manager:管理账号的各个功能接口:登录、切换账号、注销账号、绑定账号。登录可细分为设备登陆、游客登录、账号登陆、三方登陆(google/facebook/微信)等登录逻辑和切换、绑定逻辑。
public class AccountManager {
public static final String TAG = "AccountManager";
private volatile static AccountManager INSTANCE;
private AccountManager() {
}
public static AccountManager getInstance() {
if (INSTANCE == null) {
synchronized (AccountManager.class) {
if (INSTANCE == null) {
INSTANCE = new AccountManager();
}
}
}
return INSTANCE;
}
private Activity mActivity;
private AccountBean mLoginInfo; //当前登陆的登陆信息
private boolean isSwitchAccount = false; //通过标记位来判断是否是切换账号按钮的登录回调
/****************************************** 获取Project账号监听 ****************************************/
private CallBackListener projectLoginCallBackListener;
public void setLoginCallBackLister(CallBackListener callBackLister){
projectLoginCallBackListener = callBackLister;
}
private void CallBackToProject(int event, int code, AccountBean accountBean, String msg){
//设置回调信息
AccountCallBackBean accountCallBackBean = new AccountCallBackBean();
accountCallBackBean.setEvent(event); //事件类型ID
accountCallBackBean.setErrorCode(code); //事件码
accountCallBackBean.setAccountBean(accountBean); //事件的账号信息
accountCallBackBean.setMsg(msg); //设置事件的信息
if (projectLoginCallBackListener != null){
projectLoginCallBackListener.onSuccess(accountCallBackBean);//回调给Project的信息
}
}
/**
* 登录结果监听
*/
private CallBackListener LoginCallBackLister = new CallBackListener<AccountBean>(){
@Override
public void onSuccess(AccountBean loginInfo) {
LogUtils.d(TAG, "loginInfo:" + loginInfo.toString());
mLoginInfo = loginInfo;
//登陆成功,设置登录信息
setLoginSuccess(loginInfo);
if (isSwitchAccount){
CallBackToProject(TypeConfig.SWITCHACCOUNT, ErrCode.SUCCESS,loginInfo, "user switchAccount success");
isSwitchAccount = false; //置为false
}else {
CallBackToProject(TypeConfig.LOGIN,ErrCode.SUCCESS,loginInfo, "user login success");
}
}
@Override
public void onFailure(int code, String msg) {
mLoginInfo = null; //当前登陆失败就置为null
if (isSwitchAccount){
if (code == ErrCode.CANCEL){ //如果切换账号时,不走登录,给登出回调
CallBackToProject(TypeConfig.LOGOUT, ErrCode.SUCCESS, null, "user logout success");
}else {
CallBackToProject(TypeConfig.SWITCHACCOUNT, code, null, msg);
}
}else {
CallBackToProject(TypeConfig.LOGIN, code, null, msg);
}
}
};
/****************************************** 登录 ****************************************/
/**
* 显示登录界面
*/
public void showLoginView(final Activity activity, HashMap<String,Object> loginMap){
mActivity = activity;
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage("是否登录?");
builder.setTitle("登录界面");
builder.setPositiveButton("登录",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
AccountBean loginInfo = new AccountBean();
loginInfo.setLoginState(true); //将登录成功状态返回
loginInfo.setUserToken("dasfkaf-SAFA-kfad");
loginInfo.setUserID("userID-123");
loginInfo.setUserName("测试用户"); //聚合将用名设置为UserID
LoginCallBackLister.onSuccess(loginInfo);
}
});
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
LoginCallBackLister.onFailure(ErrCode.FAILURE,"login fail");
}
});
builder.create().show();
}
/**
* 授权登录,具体项目具体实现逻辑
*/
public void authLogin(Activity activity, HashMap<String,Object> loginMap){
mActivity = activity;
AccountBean loginInfo = new AccountBean();
loginInfo.setLoginState(true); //将登录成功状态返回
loginInfo.setUserToken("dasfkaf-SAFA-kfad");
loginInfo.setUserID("userID-123");
loginInfo.setUserName("测试用户"); //聚合将用名设置为UserID
LoginCallBackLister.onSuccess(loginInfo);
}
/**
* 获取当前登陆状态,默认false
* @return
*/
public boolean getLoginState() {
if (mLoginInfo != null){
return mLoginInfo.getLoginState();
}
return false;
}
/****************************************** 切换账号 ****************************************/
/**
* 切换账号
* @param activity
*/
public void switchAccount(Activity activity){
mActivity = activity;
//先走登出逻辑
mLoginInfo = null; //登录信息清空
isSwitchAccount = true;
clearLoginInfo(activity);
}
/****************************************** 登出 ****************************************/
/**
* 账号登出
*/
public void logout(Activity activity){
mActivity = activity;
mLoginInfo = null; //登录信息清空
isSwitchAccount = false;
clearLoginInfo(activity);
CallBackToProject(TypeConfig.LOGOUT, ErrCode.SUCCESS, null, "user logout success");
}
/**
* 设置登录成功行为
*/
private void setLoginSuccess(AccountBean loginInfo){
if (loginInfo != null){
BaseCache.getInstance().put(KeyConfig.PLAYER_ID,loginInfo.getUserID());
BaseCache.getInstance().put(KeyConfig.PLAYER_NAME,loginInfo.getUserName());
BaseCache.getInstance().put(KeyConfig.PLAYER_TOKEN,loginInfo.getUserToken());
//将当前状态存储到全局变量供其他模块插件使用
BaseCache.getInstance().put(KeyConfig.IS_LOGIN, getLoginState());
}
}
/**
* 清空登陆信息
*/
private void clearLoginInfo(Activity activity){
mLoginInfo = null;
//清空内存的用户信息
BaseCache.getInstance().put(KeyConfig.PLAYER_ID,"");
BaseCache.getInstance().put(KeyConfig.PLAYER_NAME,"");
BaseCache.getInstance().put(KeyConfig.PLAYER_TOKEN,"");
//将当前状态存储到全局变量供其他模块插件使用
BaseCache.getInstance().put(KeyConfig.IS_LOGIN, getLoginState());
}
}
支付Manager:购买管理类,管理SDK的各个购买功能接口:创建订单、三方支付、运营商支付、渠道支付、补单逻辑、包月、订阅等。注意可能还会有各个复杂的支付逻辑: 可能会先短代支付、然后渠道支付、三方支付,还有后台切换支付开关等。
public class PurchaseManager {
public static final String TAG = "PurchaseManager";
private volatile static PurchaseManager INSTANCE;
private PurchaseManager() {
}
public static PurchaseManager getInstance() {
if (INSTANCE == null) {
synchronized (PurchaseManager.class) {
if (INSTANCE == null) {
INSTANCE = new PurchaseManager();
}
}
}
return INSTANCE;
}
/**
* 创建订单,具体项目具体实现
*/
public void createOrderId(Activity activity, HashMap<String, Object> payParams , final CallBackListener callBackListener){
LogUtils.debug_d(TAG,"payParams = " + payParams.toString());
String orderID = "DD1441";
callBackListener.onSuccess(orderID);
}
/**
* 显示支付界面
*/
public void showPayView(Activity activity, HashMap<String, Object> payParams, final CallBackListener callBackListener){
LogUtils.debug_d(TAG,"payParams = " + payParams.toString());
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
String message = "充值金额:" + "2"
+ "\n商品名称:" + "大饼"
+ "\n商品数量:" + "1"
+ "\n资费说明:" + "2元";
builder.setMessage(message);
builder.setTitle("请确认充值信息");
builder.setPositiveButton("确定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, int index) {
//支付结果回调到这里来
PurchaseResult purchaseResult = new PurchaseResult(PurchaseResult.PurchaseState,null);
callBackListener.onSuccess(purchaseResult);
}
});
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
callBackListener.onFailure(ErrCode.FAILURE,"pay fail");
}
});
builder.create().show();
}
}
2、需求开发 - Plugin三方功能插件层
Plugin三方功能插件层分两部分组成:反射API和具体Plugin插件。反射API是动态插拔功能插件的关键,可以打包时动态打对应的供插件。Plugin插件是具体实现和封装对应的功能层。额外拓展新的功能插件时,继承Plugin类即可。下面以微信插件为例:
微信功能Plugin:封装和实现微信的登录、支付、分享等常见功能
public class WechatPlugin extends Plugin {
private String TAG = "WechatPlugin";
@Override
protected synchronized void initPlugin() {
super.initPlugin();
LogUtils.d(TAG,"init " + getClass().getSimpleName());
}
/**
* 调用微信支付接口
*/
public void wechatPay(Context context, Map<String,Object> payMap, CallBackListener callBackListener){
WechatPay.getInstance().pay(context,payMap,callBackListener);
}
/**
* 调用微信登录接口
*/
public void wechatLogin(Context context, Map<String,Object> LoginMap, CallBackListener callBackListener){
}
/**
* 调用微信分享接口
*/
public void wechatShare(Context context, Map<String,Object> ShareMap, CallBackListener callBackListener){
}
/**
* 根据当前的生命周期
* @param context
*/
@Override
public void onResume(Context context) {
WechatPay.getInstance().onResume(context);
}
}
微信功能PluginAPI:对接微信Plugin接口。
public class WechatPluginApi extends PluginReflectApi {
private String TAG = "WechatPluginApi";
private Plugin wechatPlugin;
private volatile static WechatPluginApi INSTANCE;
private WechatPluginApi() {
wechatPlugin = PluginManager.getInstance().getPlugin("plugin_wechat");
}
public static WechatPluginApi getInstance() {
if (INSTANCE == null) {
synchronized (WechatPluginApi.class) {
if (INSTANCE == null) {
INSTANCE = new WechatPluginApi();
}
}
}
return INSTANCE;
}
/**
* 调用微信app支付
*/
public void pay(Context context, Map<String,Object> map, CallBackListener callBackListener){
if (wechatPlugin != null){
invoke(wechatPlugin,"wechatPay",new Class<?>[]{Context.class, Map.class, CallBackListener.class},
new Object[]{context, map, callBackListener});
}
}
}
3、需求开发 - Project项目业务层
Project层主要分自定义SDK项目和聚合SDK项目两大类,自定义SDK是自己实现SDK的功能逻辑;聚合SDK主要是封装渠道SDK用于游戏的联运。业务需求是不一样的。相对而已聚合SDK会简单点。如有新的项目需求,可继承Project对应实现就OK了。主要自定义SDK为例讲解下
public class CustomProject extends Project{
private final String TAG = getClass().getSimpleName();
/**
* 项目实例化入口
*/
@Override
protected synchronized void initProject() {
LogUtils.d(TAG, getClass().getSimpleName() + " has init");
super.initProject();
}
/****************************************** 初始化 ****************************************/
@Override
public void init(Activity activity, String gameid, String gamekey, final CallBackListener callBackListener) {
LogUtils.d(TAG,"init");
if (activity == null || callBackListener == null) {
callBackListener.onFailure(ErrCode.PARAMS_ERROR,"activity or callBackListener is null");
return;
}
//设置账号监听
AccountManager.getInstance().setLoginCallBackLister(projectAccountCallBackListener);
InitManager.getInstance().init(activity, gameid, gamekey, new CallBackListener() {
@Override
public void onSuccess(Object object) {
callBackListener.onSuccess(null);
}
@Override
public void onFailure(int code, String msg) {
callBackListener.onFailure(code,msg);
}
});
}
/****************************************** 账号 ****************************************/
/*** SDKApi层设置回调监听 */
private CallBackListener ApiAccountCallback;
@Override
public void setAccountCallBackLister(CallBackListener callBackLister) {
ApiAccountCallback = callBackLister;
}
/**
* 监听AccountManager登录、切换账号、绑定、注销的回调信息
*/
private CallBackListener projectAccountCallBackListener = new CallBackListener<AccountCallBackBean>() {
@Override
public void onSuccess(AccountCallBackBean callBackBean) {
ApiAccountCallback.onSuccess(callBackBean);
}
@Override
public void onFailure(int code, String msg) {
//不会走到这里来
}
};
private void AccountOnFailCallBack(int event, int code, String msg){
AccountCallBackBean callBackBean = new AccountCallBackBean();
callBackBean.setEvent(event);
callBackBean.setErrorCode(code);
callBackBean.setMsg(msg);
ApiAccountCallback.onSuccess(callBackBean);
}
@Override
public void login(Activity activity, HashMap<String, Object> loginParams) {
LogUtils.d(TAG,"login");
if (!InitManager.getInstance().getInitState()){
Toast.makeText(activity,"请先初始化",Toast.LENGTH_SHORT).show();
return;
}
if (activity == null ) {
AccountOnFailCallBack(TypeConfig.LOGIN,ErrCode.PARAMS_ERROR,"activity is null");
return;
}
AccountManager.getInstance().showLoginView(activity,loginParams);
}
@Override
public void switchAccount(Activity activity) {
LogUtils.d(TAG,"switchAccount");
if (!InitManager.getInstance().getInitState()){
Toast.makeText(activity,"请先初始化",Toast.LENGTH_SHORT).show();
return;
}
if (!AccountManager.getInstance().getLoginState()){
AccountOnFailCallBack(TypeConfig.SWITCHACCOUNT,ErrCode.NO_LOGIN,"account has not login");
return;
}
if (activity == null ) {
AccountOnFailCallBack(TypeConfig.LOGIN,ErrCode.PARAMS_ERROR,"activity is null");
return;
}
AccountManager.getInstance().switchAccount(activity);
}
@Override
public void logout(Activity activity) {
LogUtils.d(TAG,"logout");
if (!InitManager.getInstance().getInitState()){
Toast.makeText(activity,"请先初始化",Toast.LENGTH_SHORT).show();
return;
}
if (!AccountManager.getInstance().getLoginState()){
AccountOnFailCallBack(TypeConfig.LOGOUT,ErrCode.NO_LOGIN,"account has not login");
return;
}
if (activity == null ) {
AccountOnFailCallBack(TypeConfig.LOGIN,ErrCode.PARAMS_ERROR,"activity is null");
return;
}
AccountManager.getInstance().logout(activity);
}
/****************************************** 购买 ****************************************/
@Override
public void pay(Activity activity, HashMap<String, Object> payParams, CallBackListener callBackListener) {
LogUtils.d(TAG,"pay");
if (!InitManager.getInstance().getInitState()){
Toast.makeText(activity,"请先初始化",Toast.LENGTH_SHORT).show();
return;
}
if (!AccountManager.getInstance().getLoginState()){
callBackListener.onFailure(ErrCode.NO_LOGIN,"account has not login");
return;
}
if (activity == null || payParams == null || callBackListener == null) {
callBackListener.onFailure(ErrCode.PARAMS_ERROR,"activity or PayParams or callBackListener is null");
return;
}
PurchaseManager.getInstance().showPayView(activity,payParams,callBackListener);
}
/****************************************** 退出 ****************************************/
/**
* 退出SDK
*/
@Override
public void exit(Activity activity, CallBackListener callBackListener) {
LogUtils.d(TAG,"exit");
if (activity == null || callBackListener == null) {
callBackListener.onFailure(ErrCode.PARAMS_ERROR,"activity or callBackListener is null");
return;
}
callBackListener.onFailure(ErrCode.NO_EXIT_DIALOG,"channel not exitDialog");
}
/************************************* 生命周期接口(必接) ****************************************/
@Override
public void onCreate(Activity activity, Bundle savedInstanceState) {
LogUtils.d(TAG,"onCreate");
if (InitManager.getInstance().getInitState()){
super.onCreate(activity, savedInstanceState);
}
}
@Override
public void onStart(Activity activity) {
LogUtils.d(TAG,"onStart");
if (InitManager.getInstance().getInitState()){
super.onStart(activity);
}
}
@Override
public void onResume(Activity activity) {
LogUtils.d(TAG,"onResume");
if (InitManager.getInstance().getInitState()){
super.onResume(activity);
}
}
@Override
public void onPause(Activity activity) {
LogUtils.d(TAG,"onPause");
if (InitManager.getInstance().getInitState()){
super.onPause(activity);
}
}
@Override
public void onStop(Activity activity) {
LogUtils.d(TAG,"onStop");
if (InitManager.getInstance().getInitState()){
super.onStop(activity);
}
}
@Override
public void onDestroy(Activity activity) {
LogUtils.d(TAG,"onDestroy");
if (InitManager.getInstance().getInitState()){
super.onDestroy(activity);
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
LogUtils.d(TAG,"onActivityResult");
if (InitManager.getInstance().getInitState()){
super.onActivityResult(activity, requestCode, resultCode, data);
}
}
@Override
public void onRequestPermissionsResult(Activity activity, int requestCode, String[] permissions,int[] grantResults) {
LogUtils.d(TAG,"onRequestPermissionsResult");
if (InitManager.getInstance().getInitState()){
super.onRequestPermissionsResult(activity, requestCode, permissions, grantResults);
}
}
}
4、需求开发 - 项目Channel业务层
主要是面向聚合SDK项目,主要封装渠道的SDK内容。如需封装新的渠道SDK,继承Channel类即可。
public class TestChannelSDK extends Channel {
private final String TAG = getClass().getSimpleName();
@Override
protected void initChannel() {
LogUtils.d(TAG, getClass().getSimpleName() + " has init");
}
@Override
public String getChannelID() {
return "1";
}
@Override
public boolean isSupport(int FuncType) {
switch (FuncType){
case TypeConfig.FUNC_SWITCHACCOUNT:
return true;
case TypeConfig.FUNC_LOGOUT:
return true;
case TypeConfig.FUNC_SHOW_FLOATWINDOW:
return true;
case TypeConfig.FUNC_DISMISS_FLOATWINDOW:
return true;
default:
return false;
}
}
@Override
public void init(Context context, HashMap<String, Object> initMap, CallBackListener initCallBackListener) {
LogUtils.d(TAG,getClass().getSimpleName() + " init");
initOnSuccess(initCallBackListener);
}
@Override
public void login(Context context, HashMap<String, Object> loginMap, CallBackListener loginCallBackListener) {
LogUtils.d(TAG,getClass().getSimpleName() + " login");
showLoginView(context,loginCallBackListener);
}
@Override
public void switchAccount(final Context context, final CallBackListener changeAccountCallBackLister) {
LogUtils.d(TAG,getClass().getSimpleName() + " switchAccount");
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("是否切换账号?");
builder.setTitle("切换账号");
builder.setPositiveButton("切换账号",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
showLoginView(context,changeAccountCallBackLister);
}
});
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
switchAccountOnCancel("channel switchAccount cancel",changeAccountCallBackLister);
}
});
builder.create().show();
}
@Override
public void logout(Context context, final CallBackListener logoutCallBackLister) {
LogUtils.d(TAG,getClass().getSimpleName() + " logout");
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("是否注销账号?");
builder.setTitle("注销账号");
builder.setPositiveButton("成功",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
logoutOnSuccess(logoutCallBackLister);
}
});
builder.setNegativeButton("失败",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
logoutOnFail("channel logout fail",logoutCallBackLister);
}
});
builder.create().show();
}
@Override
public void pay(Context context, HashMap<String, Object> payMap, final CallBackListener payCallBackListener) {
LogUtils.d(TAG,getClass().getSimpleName() + " pay");
String orderID = (String) payMap.get("orderId");
String productName = (String) payMap.get("productName");
String productDesc = (String) payMap.get("productDesc");
String money = String.valueOf(payMap.get("money"));
String productID = String.valueOf(payMap.get("productID"));
LogUtils.d(TAG,productID);
final HashMap<String,Object> paymap = new HashMap<>();
paymap.put("orderID",orderID);
paymap.put("productName",productName);
paymap.put("money",money);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
String message = "充值金额:" + money
+ "\n商品名称:" + productName
+ "\n商品数量:" + "1"
+ "\n资费说明:" + productDesc;
builder.setMessage(message);
builder.setTitle("请确认充值信息");
builder.setPositiveButton("确定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, int index) {
payOnSuccess(payCallBackListener);
}
});
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
OnCancel(payCallBackListener);
}
});
builder.create().show();
}
private void showLoginView(final Context context, final CallBackListener loginCallBackListener){
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("是否登录?");
builder.setTitle("登录界面");
builder.setPositiveButton("登录",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
JSONObject json = new JSONObject();
try {
json.put("sid", "testID");
} catch (JSONException e) {
e.printStackTrace();
}
loginOnSuccess(json.toString(),loginCallBackListener);
}
});
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
loginOnFail("channel login fail",loginCallBackListener);
}
});
builder.create().show();
}
@Override
public void exit(Context context, CallBackListener exitCallBackLister) {
LogUtils.d(TAG,getClass().getSimpleName() + " exit");
channelNotExitDialog(exitCallBackLister);
}
}
项目代码总结
----------------------------------------------------------------------------------------------
GameSDK_API层:
SDK对外接口层:是暴露给CP的接口,底层返回的数据格式在这一层转化,该层不参与混淆。
所以不要在该层做业务逻辑处理,避免被反射调用修改。
后续有新的接口,可以在该层做拓展对外给CP。
----------------------------------------------------------------------------------------------
GameSDK_BeginProject层:
SDK项目层:是与Api层对接的项目入口层,可以通过修改配置文件 Project_config.txt
进入和替换成不同的Project SDK项目。
注意:
顶层Project已经确定好后,不要修改太多,如果有拓展的功能接口,
可以通过extendFunction()对应type拓展,避免改动太多底层代码。
SDK大体分为两类:
1、Project_JuHe为聚合SDK项目,主要业务用于封装三方渠道。
2、Project_Custom为自定义SDK项目,主要是做自己的渠道,每个公司可能会有不同的名称,
主要业务是,实现用户入口、支付逻辑、数据统计等功能。
可能根据不同的游戏运营需求会有不同的登录逻辑、绑定逻辑、支付方式、数据上报等
会细分为公版项目、海外项目(针对地区)、根据游戏的定制化项目等。可根据不同的项目,拓展Project
该层是做业务逻辑处理的,希望能有清晰的架构思路。
----------------------------------------------------------------------------------------------
SYSDK_Channel: SDK项目渠道层
该层负责对接渠道接入的业务,不同的渠道都会有对应的Module实现具体的代码调用和逻辑处理,以及资源配置
通过配置文件 Channel_config.txt 管理。
而且每个渠道Module都会有对应的开发者说明文件,方便后续的开发同事维护更新。详见开发者说明。
开发者格式说明:
格式: 渠道名 -- 版本号 -- 开发人员 (如果渠道没有版本就按V1.0.0来) 日期
相关注意事项说明:
1、.... xxx ....
2、.... xxx ....
----------------------------------------------------------------------------------------------
SYSDK_Manager:
SDK逻辑管理层:
该层为整个SDK功能逻辑的实现:初始化、账号、支付、获取道具信息、补单逻辑、退出
为避免逻辑层因业务太乱导致代码过多,及后续的功能模块抽离.
采用模块化化思想进行模块管理.
初始化:SDK初始化(全局参数缓存、环境切换、权限问题等) >- 项目初始化(默认初始化 和 项目初始化)
登陆:设备登陆、游客登录、账号登陆、三方登陆(google/facebook/微信)
支付:三方支付:google、支付宝、微信、及特有的项目支付、补单
退出:
当有额外的功能添加的时候,在该层实现即可。
----------------------------------------------------------------------------------------------
GameSDK_Manager_Impl:
SDK逻辑功能拓展反射层,通过反射解耦插件:
该层更多的是针对后续Plugin插件做不同的功能反射,具体调用Plugin层插件。
只负责对接GameSDK_Manager_Impl 和 Plugin插件层。
----------------------------------------------------------------------------------------------
GameSDK_Plugin:
SDK功能插件层,与渠道层有点类似,通过配置文件 Plugin_config.txt 管理。
通过逻辑控制层 Manager_Logic_Impl 反射调用实现,把插件拔除不影响。
可以是三方的功能插件,也可以是自己实现的插件。
目前为说明功能,用支付宝和微信说明
Alipay功能插件层:实现Alipay功能,封装Alipay相关接口
Wechat功能插件层:实现Wechat功能,封装Wechat相关接口
当有额外的功能添加的时候,在该层实现即可。
----------------------------------------------------------------------------------------------
GameSDK_Utils:
GameSDK_Utils层:该层为整个SDK功能基础库:目前分为业务基础库 和 功能基础库,方便后续将业务分离。
基础组件:
1、数据缓存、域名配置、项目/插件/渠道管理
注意:将项目、渠道、插件分别加载的目的:
是为了快速替换项目Project的入口类
一个项目Project 可以对应多个渠道、多个插件。后续可以在多渠道、多插件上
进行快速的插拔和后台开关的切换渠道。
不过正常的需求都是一个项目,对应零个或一个渠道、一个或多个功能插件
2、网络请求、日志输出、Gson解析
注意:网络请求,目前封装的volly (volly是比较轻量级的,主要是为减少SDK包体)
日志输出,目前封装的logger(logger比较轻量级,主要用于开发日志信息输出)
第三方库快速替换方案思路:(方便后续维护替换成更好的库)
接口解耦封装,实现上层业务网络请求接口不改动,只做封装层的api调用即可
基础库不要轻易修改! 不要轻易修改! 不要轻易修改! 不要轻易修改! 不要轻易修改!
---------------------------------------------------------------------------------------
结语:
关于手游SDK的客户端的实现就大体介绍到这里。Demo的地址: 手游SDK框架Demo。
声明下,该Demo只是框架的讲解,不具备任何商业价值。如涉及到纠纷,可不能赖我呀。
关于打包篇,可移步:
手游SDK — 第五篇(游戏打包篇(上)- 打包系统设计)
手游SDK — 第六篇(游戏打包篇(中)- 自动化打包)
手游SDK — 第七篇(游戏打包篇(下)- 自动化打包踩坑记录)
如果觉得我的文章对你有帮助,请随意赞赏。您的支持将鼓励我继续创作!