第二部分:項目需求開發(fā)
基礎(chǔ)庫搭建好了之后就是根據(jù)項目需求進行實際功能開發(fā)了详炬,因為不同的項目有不同的項目需求滞伟,我簡單的以項目初始化惕蹄、賬號登錄/切換賬號/賬號登出、支付三大功能點來進行框架的代碼實現(xiàn)。
1齐帚、需求開發(fā) - Manager控制模塊
Manager模塊是SDK的核心模塊,主要負責業(yè)務的功能實現(xiàn)及邏輯控制。根據(jù)項目需求偿洁,暫定為初始化Manager、賬號Manager沟优、支付Manager涕滋。
初始化Manager:處理SDK的初始化邏輯,全局參數(shù)緩存挠阁、環(huán)境切換宾肺、權(quán)限問題等。
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 日志調(diào)試開關(guān)
*/
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參數(shù)
SDKInfoCache.getDefault(activity.getApplication());
//3、初始化持久化數(shù)據(jù)
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;
//將當前狀態(tài)存儲到全局變量供其他模塊插件使用
BaseCache.getInstance().put(KeyConfig.IS_INIT,initState);
}
public boolean getInitState() {
return initState;
}
}
賬號Manager:管理賬號的各個功能接口:登錄、切換賬號掌栅、注銷賬號秩仆、綁定賬號。登錄可細分為設(shè)備登陸猾封、游客登錄逗概、賬號登陸、三方登陸(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; //通過標記位來判斷是否是切換賬號按鈕的登錄回調(diào)
/****************************************** 獲取Project賬號監(jiān)聽 ****************************************/
private CallBackListener projectLoginCallBackListener;
public void setLoginCallBackLister(CallBackListener callBackLister){
projectLoginCallBackListener = callBackLister;
}
private void CallBackToProject(int event, int code, AccountBean accountBean, String msg){
//設(shè)置回調(diào)信息
AccountCallBackBean accountCallBackBean = new AccountCallBackBean();
accountCallBackBean.setEvent(event); //事件類型ID
accountCallBackBean.setErrorCode(code); //事件碼
accountCallBackBean.setAccountBean(accountBean); //事件的賬號信息
accountCallBackBean.setMsg(msg); //設(shè)置事件的信息
if (projectLoginCallBackListener != null){
projectLoginCallBackListener.onSuccess(accountCallBackBean);//回調(diào)給Project的信息
}
}
/**
* 登錄結(jié)果監(jiān)聽
*/
private CallBackListener LoginCallBackLister = new CallBackListener<AccountBean>(){
@Override
public void onSuccess(AccountBean loginInfo) {
LogUtils.d(TAG, "loginInfo:" + loginInfo.toString());
mLoginInfo = loginInfo;
//登陸成功,設(shè)置登錄信息
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){ //如果切換賬號時,不走登錄,給登出回調(diào)
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); //將登錄成功狀態(tài)返回
loginInfo.setUserToken("dasfkaf-SAFA-kfad");
loginInfo.setUserID("userID-123");
loginInfo.setUserName("測試用戶"); //聚合將用名設(shè)置為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();
}
/**
* 授權(quán)登錄,具體項目具體實現(xiàn)邏輯
*/
public void authLogin(Activity activity, HashMap<String,Object> loginMap){
mActivity = activity;
AccountBean loginInfo = new AccountBean();
loginInfo.setLoginState(true); //將登錄成功狀態(tài)返回
loginInfo.setUserToken("dasfkaf-SAFA-kfad");
loginInfo.setUserID("userID-123");
loginInfo.setUserName("測試用戶"); //聚合將用名設(shè)置為UserID
LoginCallBackLister.onSuccess(loginInfo);
}
/**
* 獲取當前登陸狀態(tài),默認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");
}
/**
* 設(shè)置登錄成功行為
*/
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());
//將當前狀態(tài)存儲到全局變量供其他模塊插件使用
BaseCache.getInstance().put(KeyConfig.IS_LOGIN, getLoginState());
}
}
/**
* 清空登陸信息
*/
private void clearLoginInfo(Activity activity){
mLoginInfo = null;
//清空內(nèi)存的用戶信息
BaseCache.getInstance().put(KeyConfig.PLAYER_ID,"");
BaseCache.getInstance().put(KeyConfig.PLAYER_NAME,"");
BaseCache.getInstance().put(KeyConfig.PLAYER_TOKEN,"");
//將當前狀態(tài)存儲到全局變量供其他模塊插件使用
BaseCache.getInstance().put(KeyConfig.IS_LOGIN, getLoginState());
}
}
支付Manager:購買管理類枚钓,管理SDK的各個購買功能接口:創(chuàng)建訂單铅搓、三方支付、運營商支付搀捷、渠道支付星掰、補單邏輯、包月嫩舟、訂閱等氢烘。注意可能還會有各個復雜的支付邏輯: 可能會先短代支付、然后渠道支付家厌、三方支付播玖,還有后臺切換支付開關(guān)等。
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;
}
/**
* 創(chuàng)建訂單,具體項目具體實現(xiàn)
*/
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商品數(shù)量:" + "1"
+ "\n資費說明:" + "2元";
builder.setMessage(message);
builder.setTitle("請確認充值信息");
builder.setPositiveButton("確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, int index) {
//支付結(jié)果回調(diào)到這里來
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饭于、需求開發(fā) - Plugin三方功能插件層
Plugin三方功能插件層分兩部分組成:反射API和具體Plugin插件蜀踏。反射API是動態(tài)插拔功能插件的關(guān)鍵,可以打包時動態(tài)打?qū)墓┎寮馈lugin插件是具體實現(xiàn)和封裝對應的功能層果覆。額外拓展新的功能插件時,繼承Plugin類即可殖熟。下面以微信插件為例:
微信功能Plugin:封裝和實現(xiàn)微信的登錄局待、支付、分享等常見功能
public class WechatPlugin extends Plugin {
private String TAG = "WechatPlugin";
@Override
protected synchronized void initPlugin() {
super.initPlugin();
LogUtils.d(TAG,"init " + getClass().getSimpleName());
}
/**
* 調(diào)用微信支付接口
*/
public void wechatPay(Context context, Map<String,Object> payMap, CallBackListener callBackListener){
WechatPay.getInstance().pay(context,payMap,callBackListener);
}
/**
* 調(diào)用微信登錄接口
*/
public void wechatLogin(Context context, Map<String,Object> LoginMap, CallBackListener callBackListener){
}
/**
* 調(diào)用微信分享接口
*/
public void wechatShare(Context context, Map<String,Object> ShareMap, CallBackListener callBackListener){
}
/**
* 根據(jù)當前的生命周期
* @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;
}
/**
* 調(diào)用微信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钳榨、需求開發(fā) - Project項目業(yè)務層
Project層主要分自定義SDK項目和聚合SDK項目兩大類,自定義SDK是自己實現(xiàn)SDK的功能邏輯照皆;聚合SDK主要是封裝渠道SDK用于游戲的聯(lián)運重绷。業(yè)務需求是不一樣的。相對而已聚合SDK會簡單點膜毁。如有新的項目需求昭卓,可繼承Project對應實現(xiàn)就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;
}
//設(shè)置賬號監(jiān)聽
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層設(shè)置回調(diào)監(jiān)聽 */
private CallBackListener ApiAccountCallback;
@Override
public void setAccountCallBackLister(CallBackListener callBackLister) {
ApiAccountCallback = callBackLister;
}
/**
* 監(jiān)聽AccountManager登錄、切換賬號候醒、綁定能颁、注銷的回調(diào)信息
*/
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、需求開發(fā) - 項目Channel業(yè)務層
主要是面向聚合SDK項目,主要封裝渠道的SDK內(nèi)容倒淫。如需封裝新的渠道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商品數(shù)量:" + "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);
}
}
項目代碼總結(jié)
----------------------------------------------------------------------------------------------
GameSDK_API層:
SDK對外接口層:是暴露給CP的接口,底層返回的數(shù)據(jù)格式在這一層轉(zhuǎn)化,該層不參與混淆敌土。
所以不要在該層做業(yè)務邏輯處理镜硕,避免被反射調(diào)用修改。
后續(xù)有新的接口返干,可以在該層做拓展對外給CP兴枯。
----------------------------------------------------------------------------------------------
GameSDK_BeginProject層:
SDK項目層:是與Api層對接的項目入口層,可以通過修改配置文件 Project_config.txt
進入和替換成不同的Project SDK項目矩欠。
注意:
頂層Project已經(jīng)確定好后财剖,不要修改太多,如果有拓展的功能接口癌淮,
可以通過extendFunction()對應type拓展躺坟,避免改動太多底層代碼。
SDK大體分為兩類:
1乳蓄、Project_JuHe為聚合SDK項目,主要業(yè)務用于封裝三方渠道咪橙。
2、Project_Custom為自定義SDK項目,主要是做自己的渠道,每個公司可能會有不同的名稱,
主要業(yè)務是,實現(xiàn)用戶入口栓袖、支付邏輯匣摘、數(shù)據(jù)統(tǒng)計等功能店诗。
可能根據(jù)不同的游戲運營需求會有不同的登錄邏輯裹刮、綁定邏輯、支付方式庞瘸、數(shù)據(jù)上報等
會細分為公版項目捧弃、海外項目(針對地區(qū))、根據(jù)游戲的定制化項目等擦囊∥ハ迹可根據(jù)不同的項目,拓展Project
該層是做業(yè)務邏輯處理的,希望能有清晰的架構(gòu)思路瞬场。
----------------------------------------------------------------------------------------------
SYSDK_Channel: SDK項目渠道層
該層負責對接渠道接入的業(yè)務买鸽,不同的渠道都會有對應的Module實現(xiàn)具體的代碼調(diào)用和邏輯處理,以及資源配置
通過配置文件 Channel_config.txt 管理贯被。
而且每個渠道Module都會有對應的開發(fā)者說明文件眼五,方便后續(xù)的開發(fā)同事維護更新妆艘。詳見開發(fā)者說明。
開發(fā)者格式說明:
格式: 渠道名 -- 版本號 -- 開發(fā)人員 (如果渠道沒有版本就按V1.0.0來) 日期
相關(guān)注意事項說明:
1看幼、.... xxx ....
2批旺、.... xxx ....
----------------------------------------------------------------------------------------------
SYSDK_Manager:
SDK邏輯管理層:
該層為整個SDK功能邏輯的實現(xiàn):初始化、賬號诵姜、支付汽煮、獲取道具信息、補單邏輯棚唆、退出
為避免邏輯層因業(yè)務太亂導致代碼過多,及后續(xù)的功能模塊抽離.
采用模塊化化思想進行模塊管理.
初始化:SDK初始化(全局參數(shù)緩存暇赤、環(huán)境切換、權(quán)限問題等) >- 項目初始化(默認初始化 和 項目初始化)
登陸:設(shè)備登陸宵凌、游客登錄翎卓、賬號登陸、三方登陸(google/facebook/微信)
支付:三方支付:google摆寄、支付寶失暴、微信、及特有的項目支付微饥、補單
退出:
當有額外的功能添加的時候逗扒,在該層實現(xiàn)即可。
----------------------------------------------------------------------------------------------
GameSDK_Manager_Impl:
SDK邏輯功能拓展反射層欠橘,通過反射解耦插件:
該層更多的是針對后續(xù)Plugin插件做不同的功能反射矩肩,具體調(diào)用Plugin層插件。
只負責對接GameSDK_Manager_Impl 和 Plugin插件層肃续。
----------------------------------------------------------------------------------------------
GameSDK_Plugin:
SDK功能插件層黍檩,與渠道層有點類似,通過配置文件 Plugin_config.txt 管理始锚。
通過邏輯控制層 Manager_Logic_Impl 反射調(diào)用實現(xiàn)刽酱,把插件拔除不影響。
可以是三方的功能插件瞧捌,也可以是自己實現(xiàn)的插件棵里。
目前為說明功能,用支付寶和微信說明
Alipay功能插件層:實現(xiàn)Alipay功能姐呐,封裝Alipay相關(guān)接口
Wechat功能插件層:實現(xiàn)Wechat功能殿怜,封裝Wechat相關(guān)接口
當有額外的功能添加的時候,在該層實現(xiàn)即可曙砂。
----------------------------------------------------------------------------------------------
GameSDK_Utils:
GameSDK_Utils層:該層為整個SDK功能基礎(chǔ)庫:目前分為業(yè)務基礎(chǔ)庫 和 功能基礎(chǔ)庫头谜,方便后續(xù)將業(yè)務分離。
基礎(chǔ)組件:
1鸠澈、數(shù)據(jù)緩存柱告、域名配置砖织、項目/插件/渠道管理
注意:將項目、渠道末荐、插件分別加載的目的:
是為了快速替換項目Project的入口類
一個項目Project 可以對應多個渠道侧纯、多個插件。后續(xù)可以在多渠道甲脏、多插件上
進行快速的插拔和后臺開關(guān)的切換渠道眶熬。
不過正常的需求都是一個項目,對應零個或一個渠道块请、一個或多個功能插件
2娜氏、網(wǎng)絡請求、日志輸出墩新、Gson解析
注意:網(wǎng)絡請求贸弥,目前封裝的volly (volly是比較輕量級的,主要是為減少SDK包體)
日志輸出海渊,目前封裝的logger(logger比較輕量級绵疲,主要用于開發(fā)日志信息輸出)
第三方庫快速替換方案思路:(方便后續(xù)維護替換成更好的庫)
接口解耦封裝,實現(xiàn)上層業(yè)務網(wǎng)絡請求接口不改動臣疑,只做封裝層的api調(diào)用即可
基礎(chǔ)庫不要輕易修改! 不要輕易修改! 不要輕易修改! 不要輕易修改! 不要輕易修改!
---------------------------------------------------------------------------------------
結(jié)語:
關(guān)于手游SDK的客戶端的實現(xiàn)就大體介紹到這里盔憨。Demo的地址: 手游SDK框架Demo。
聲明下讯沈,該Demo只是框架的講解郁岩,不具備任何商業(yè)價值。如涉及到糾紛缺狠,可不能賴我呀问慎。
關(guān)于打包篇,可移步:
手游SDK — 第五篇(游戲打包篇(上)- 打包系統(tǒng)設(shè)計)
手游SDK — 第六篇(游戲打包篇(中)- 自動化打包)
手游SDK — 第七篇(游戲打包篇(下)- 自動化打包踩坑記錄)
如果覺得我的文章對你有幫助挤茄,請隨意贊賞如叼。您的支持將鼓勵我繼續(xù)創(chuàng)作!