之前项目平台没有定好是大厂独代模式,所以在大厂提出省流量更新问题的时候,大厂给的建议是用大厂自家的应用宝省流量更新功能,说是项目组自己去写这个功能的话,需要的时间估计会有点超过预期,想了一下,也对,对接的事项这么多,不能花太多时间去研究,然后就坑了。
MSDK上的应用宝省流量更新接起来非常快,只要MSDK接好,调用几个接口,接入游戏加上拼UI啥的,估计1个小时左右就能完成,而且在线上测试的时候,全量更新和增量更新功能都非常棒。自认为没有问题的时候,大厂测试人员反馈有的真机在增量更新的时候不能安装,有解析包时出现问题,咨询大厂的技术后,得到一个情况是MSDK的应用宝省流量更新算法太老了,不支持Android7.0以上设备,然后我马不停蹄地找了2部机子,一部Android4.4.4,一部Android9.0,测试Android4.4.4可以增量更新,Android9.0不可以。那咋办,大厂技术给的解决方案是单独接入应用宝省流量更新SDK。
哎,死循环啊,跟大厂对接感觉就是一个套路啊,接完一个SDK,再继续接另外一个SDK。咋办,还是硬上啊。避免和MSDK冲突,应用宝省流量更新SDK需要用带external的jar包。
按照文档,修改AndroidManifest文件,将MSDK上的配置android:name="com.tencent.tmdownloader.TMAssistantDownloadService"修改成android:name="com.tencent.external.tmdownloader.TMAssistantDownloadService",
添加provider,也是带external的:
代码就照着MSDK和官方文档一样:
/**
* 自更新任务监听,包括检查更新回调和下载状态回调
*/
private ITMSelfUpdateListener mSelfUpdateListener = new ITMSelfUpdateListener() {
/**
* 更新时的下载状态回调
* @param state 状态码
* @param errorCode 错误码
* @param errorMsg 错误信息
*/
@Override
public void onDownloadAppStateChanged(int state, int errorCode, String errorMsg) {
StringBuilder sb = new StringBuilder();
sb.append(state + "|");
sb.append(errorCode + "|");
sb.append(errorMsg);
UnityPlayer.UnitySendMessage("GameMain", "OnDownloadAppState", sb.toString());
}
/**
* 点击普通更新时的下载进度回调
* @param receiveDataLen 已经接收的数据长度
* @param totalDataLen 全部需要接收的数据长度(如果无法获取目标文件的总长度,此参数返回-1)
*/
@Override
public void onDownloadAppProgressChanged(final long receiveDataLen, final long totalDataLen) {
StringBuilder sb = new StringBuilder();
sb.append(receiveDataLen + "|");
sb.append(totalDataLen);
UnityPlayer.UnitySendMessage("GameMain", "OnDownloadAppProgress", sb.toString());
}
/**
* 检查更新返回更新信息回调
* @param tmSelfUpdateUpdateInfo 更新信息结构体
*/
@Override
public void onUpdateInfoReceived(TMSelfUpdateUpdateInfo tmSelfUpdateUpdateInfo) {
long newApkSize = 0;
long pathSize = 0;
int state = TMSelfUpdateUpdateInfo.STATUS_CHECKUPDATE_FAILURE;
int updateMethod = TMSelfUpdateUpdateInfo.UpdateMethod_NoUpdate;
String updateDownloadUrl = "";
String newFeature = "";
try
{
if (null != tmSelfUpdateUpdateInfo) {
state = tmSelfUpdateUpdateInfo.getStatus();
if (state == TMSelfUpdateUpdateInfo.STATUS_CHECKUPDATE_FAILURE) {
StringBuilder sb = new StringBuilder();
sb.append(newApkSize + "|");
sb.append(pathSize + "|");
sb.append(state + "|");
sb.append(updateMethod + "|");
sb.append(updateDownloadUrl + "|");
sb.append(newFeature);
UnityPlayer.UnitySendMessage("GameMain", "OnCheckUpdate", sb.toString());
return;
}
newApkSize = tmSelfUpdateUpdateInfo.getNewApkSize();
pathSize = tmSelfUpdateUpdateInfo.getPatchSize();
updateMethod = tmSelfUpdateUpdateInfo.getUpdateMethod();
updateDownloadUrl = tmSelfUpdateUpdateInfo.getUpdateDownloadUrl();
newFeature = tmSelfUpdateUpdateInfo.getNewFeature();
StringBuilder sb = new StringBuilder();
sb.append(newApkSize + "|");
sb.append(pathSize + "|");
sb.append(state + "|");
sb.append(updateMethod + "|");
sb.append(updateDownloadUrl + "|");
sb.append(newFeature);
UnityPlayer.UnitySendMessage("GameMain", "OnCheckUpdate", sb.toString());
return;
}
}
catch(Exception e)
{
Log.e("ZP: ", "onUpdateInfoReceived: " + e.toString());
}
newApkSize = 0;
pathSize = 0;
state = TMSelfUpdateUpdateInfo.STATUS_CHECKUPDATE_FAILURE;
updateMethod = TMSelfUpdateUpdateInfo.UpdateMethod_NoUpdate;
updateDownloadUrl = "";
newFeature = "";
StringBuilder sb = new StringBuilder();
sb.append(newApkSize + "|");
sb.append(pathSize + "|");
sb.append(state + "|");
sb.append(updateMethod + "|");
sb.append(updateDownloadUrl + "|");
sb.append(newFeature);
UnityPlayer.UnitySendMessage("GameMain", "OnCheckUpdate", sb.toString());
}
};
/**
* 应用宝状态监听
*/
private YYBDownloadListener mDownloadYYBCallback = new YYBDownloadListener() {
/**
* 预下载应用宝状态变化回调
*
* @param url 指定任务的url
* @param state 下载状态: 取自 TMAssistantDownloadTaskState.DownloadSDKTaskState_*
* @param errorCode 错误码
* @param errorMsg 错误描述,有可能为null
*/
@Override
public void onDownloadYYBStateChanged(String url, int state, int errorCode, String errorMsg) {
StringBuilder sb = new StringBuilder();
sb.append(state + "|");
sb.append(errorCode + "|");
sb.append(errorMsg + "|");
sb.append(url);
UnityPlayer.UnitySendMessage("GameMain", "OnDownloadYYBState", sb.toString());
}
/**
* 预下载应用宝进度回调
*
* @param url 当前任务的url
* @param receiveDataLen 已经接收的数据长度
* @param totalDataLen 全部需要接收的数据长度(如果无法获取目标文件的总长度,此参数返回 -1)
*/
@Override
public void onDownloadYYBProgressChanged(String url, long receiveDataLen, long totalDataLen) {
StringBuilder sb = new StringBuilder();
sb.append(receiveDataLen + "|");
sb.append(totalDataLen + "|");
sb.append(url);
UnityPlayer.UnitySendMessage("GameMain", "OnDownloadYYBProgress", sb.toString());
}
/**
* 查询应用宝的下载状态:当用户调了查询应用宝状态时回调
*
* @param url 当前任务的url
* @param state 下载状态: 取自 TMAssistantDownloadTaskState.DownloadSDKTaskState_*
* @param receiveDataLen 已经接收的数据长度
* @param totalDataLen 全部需要接收的数据长度(如果无法获取目标文件的总长度,此参数返回 -1)
*/
@Override
public void onCheckDownloadYYBState(String url, int state, long receiveDataLen, long totalDataLen) {
//TODO 检查应用宝下载状态处理逻辑
}
};
private static boolean bIsInitSelfUpdateSDK = false;
/**
* 初始化省流量更新SDK,传入的Context必须为ApplicationContext
*/
public void initSelfUpdateSDK(Object object, String channel) {
Activity activity = (Activity)object;
if(activity != null)
{
bIsInitSelfUpdateSDK = true;
//附加参数的bundle,一般情况下传空,可以由外部传入场景信息等,具体字段可参考示例{@link TMSelfUpdateConst}中BUNDLE_KEY_*的定义(详见注2)
Bundle bundle = new Bundle();
bundle.putString(TMSelfUpdateConst.BUNDLE_KEY_SCENE, "FOO");
TMAssistantSDK.get().initSelfUpdate(activity.getApplicationContext(), channel, mSelfUpdateListener, mDownloadYYBCallback, bundle);
}
}
public void DestroyUpdate()
{
TMAssistantSDK.get().destroy();
}
public void CheckNeedUpdate()
{
TMAssistantSDK.get().checkSelfUpdate();
}
public void StartSaveUpdate(boolean isUseYYB)
{
TMAssistantSDK.get().startSelfUpdate(isUseYYB);
}
米大师托管模式的问题,之前公司其他项目组有接过米大师,我们项目就按照他们的方式来接,在正式测试的时候,大厂反馈我们的接法有问题,是假托管,不是真托管,如果是YSDK,不是独代的话,没问题。客户端改动量不是很大,服务器改动就有点夸张了。之前假托管的方式,服务器还让我做了补单功能,自己只写了一半,分享下,如果有人刚好跟我一样是用假托管,还可以用上。
///
/// 订单文件名
///
private string m_saveOrderFileName = "saveOrder.txt";
///
/// 上次的订单
///
private OrderData m_lastOrderData;
#region Midas订单数据
public class OrderDataJson
{
public Dictionary> before;//打开Midas之前
public Dictionary> after;//充值成功(需要删除before中的数据)
}
public class OrderData
{
public string openID;
public string openKey;
public string offerId;
public string pf;
public string pfkey;
public string zoneid;
public string userType;
public string money;
public string passport;
public int serverid;
public int device;
public int bIsSandBox;
public long savetime;
public byte bHasUsed;//向服务器请求发货:0表示还没有请求过,1表示已经请求过(但是还是发货失败)
}
private bool IsStringEqual(string str1, string str2)
{
bool bIsEqual = false;
if (!string.IsNullOrEmpty(str1) && !string.IsNullOrEmpty(str2))
bIsEqual = str1.Equals(str2);
return bIsEqual;
}
private bool IsOrderDataEqual(OrderData order1, OrderData order2)
{
bool bIsEqual = false;
bIsEqual = IsStringEqual(order1.openID, order2.openID)
&& IsStringEqual(order1.openKey, order2.openKey)
&& IsStringEqual(order1.offerId, order2.offerId)
&& IsStringEqual(order1.pf, order2.pf)
&& IsStringEqual(order1.pfkey, order2.pfkey)
&& IsStringEqual(order1.zoneid, order2.zoneid)
//&& IsStringEqual(order1.userType, order2.userType)
&& IsStringEqual(order1.money, order2.money)
//&& IsStringEqual(order1.passport, order2.passport)
&& order1.serverid == order2.serverid
&& order1.device == order2.device
&& order1.bIsSandBox == order2.bIsSandBox
&& order1.savetime == order2.savetime;
return bIsEqual;
}
///
/// 生成订单
///
///
///
private OrderData BuildOrder(APMidasGameRequest req)
{
OrderData orderData = new OrderData();
orderData.openID = req.openId;
orderData.openKey = req.openKey;
orderData.offerId = req.offerId;
orderData.pf = req.pf;
orderData.pfkey = req.pfKey;
orderData.zoneid = req.zoneId;
//orderData.userType = GetUserType();
orderData.money = req.saveValue;
//orderData.device = MobileUtility.GetPlatofrmID();
//orderData.bIsSandBox = SDKUtility.GetIsSandBox();
DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
orderData.savetime = (DateTime.Now.ToUniversalTime().Ticks - startTime.ToUniversalTime().Ticks) / 10000000;
orderData.bHasUsed = 0;
return orderData;
}
///
/// 订单是否有效
///
///
///
private bool GetIsOrderDataValidate(OrderData orderData)
{
bool bIsValidate = false;
bIsValidate = orderData != null && !string.IsNullOrEmpty(orderData.money) && !string.IsNullOrEmpty(orderData.openID);
return bIsValidate;
}
///
/// 获取订单文件路径
///
///
private string GetSaveOrderFileName()
{
string saveOrderFilePath = Application.persistentDataPath + "/" + m_saveOrderFileName;
if (!File.Exists(saveOrderFilePath))
{
FileStream fs = File.Create(saveOrderFilePath);
fs.Close();
}
return saveOrderFilePath;
}
#endregion
public OrderData GetOrderData(string openID, string rmb)
{
OrderData tempOrderData = null;
string saveOrderFilePath = GetSaveOrderFileName();
if (!string.IsNullOrEmpty(saveOrderFilePath))
{
try
{
StreamReader reader = new StreamReader(saveOrderFilePath);
var content = reader.ReadToEnd().Trim();
reader.Close();
OrderDataJson tempOrderDataJson = null;
if (string.IsNullOrEmpty(content))
tempOrderDataJson = new OrderDataJson();
else
tempOrderDataJson = JsonMapper.ToObject(content);
//if (tempOrderDataJson.after != null)
//{
// foreach (var tempOpenIDOrderDict in tempOrderDataJson.after)
// {
// foreach (var tempMoneyOrderDict in tempOpenIDOrderDict.Value)
// {
// for (int i = 0; i < tempMoneyOrderDict.Value.Length; ++i)
// {
// tempMoneyOrderDict.Value[i].bHasUsed = 0;
// }
// }
// }
//}
}
catch(Exception e)
{
Debug.LogError(e.ToString());
}
}
return tempOrderData;
}
///
/// 储存Midas的订单信息
///
///
/// 是否在充值前
private void SaveMidasOrder(OrderData orderData, bool bIsBefore)
{
if (!GetIsOrderDataValidate(orderData)) return;
string saveOrderFilePath = GetSaveOrderFileName();
if (string.IsNullOrEmpty(saveOrderFilePath)) return;
try
{
StreamReader reader = new StreamReader(saveOrderFilePath);
var content = reader.ReadToEnd().Trim();
reader.Close();
OrderDataJson tempOrderDataJson = null;
if (string.IsNullOrEmpty(content))
tempOrderDataJson = new OrderDataJson();
else
tempOrderDataJson = JsonMapper.ToObject(content);
string firstKey = orderData.openID;
string secondKey = orderData.money;
if (bIsBefore)//充值前
{
if (tempOrderDataJson.before == null)
tempOrderDataJson.before = new Dictionary>();
if (!tempOrderDataJson.before.ContainsKey(firstKey))
{
List temOrderList = new List();
temOrderList.Add(orderData);
Dictionary tempOrderDict = new Dictionary();
tempOrderDict.Add(secondKey, temOrderList.ToArray());
tempOrderDataJson.before.Add(firstKey, tempOrderDict);
}
else
{
Dictionary tempOrderDict = tempOrderDataJson.before[firstKey];
if(!tempOrderDict.ContainsKey(secondKey))
{
List temOrderList = new List();
temOrderList.Add(orderData);
tempOrderDict.Add(secondKey, temOrderList.ToArray());
}
else
{
List temOrderList = new List(tempOrderDict[secondKey]);
temOrderList.Add(orderData);
tempOrderDict[secondKey] = temOrderList.ToArray();
tempOrderDataJson.before[firstKey] = tempOrderDict;
}
}
}
else//充值后
{
if (tempOrderDataJson.after == null)
tempOrderDataJson.after = new Dictionary>();
if (!tempOrderDataJson.after.ContainsKey(firstKey))
{
List temOrderList = new List();
temOrderList.Add(orderData);
Dictionary tempOrderDict = new Dictionary();
tempOrderDict.Add(secondKey, temOrderList.ToArray());
tempOrderDataJson.after.Add(firstKey, tempOrderDict);
}
else
{
Dictionary tempOrderDict = tempOrderDataJson.after[firstKey];
if (!tempOrderDict.ContainsKey(secondKey))
{
List temOrderList = new List();
temOrderList.Add(orderData);
tempOrderDict.Add(secondKey, temOrderList.ToArray());
}
else
{
List temOrderList = new List(tempOrderDict[secondKey]);
temOrderList.Add(orderData);
tempOrderDict[secondKey] = temOrderList.ToArray();
tempOrderDataJson.after[firstKey] = tempOrderDict;
}
}
// 移除充值前的订单
if (tempOrderDataJson.before != null && tempOrderDataJson.before.ContainsKey(firstKey) && tempOrderDataJson.before[firstKey].ContainsKey(secondKey))
{
List temOrderList = new List(tempOrderDataJson.before[firstKey][secondKey]);
for (int i = 0; i < temOrderList.Count; ++i)
{
if (IsOrderDataEqual(temOrderList[i], orderData))
{
temOrderList.RemoveAt(i);
break;
}
}
tempOrderDataJson.before[firstKey][secondKey] = temOrderList.ToArray();
}
}
StreamWriter writer = new StreamWriter(new FileStream(saveOrderFilePath, FileMode.Create));
writer.Write(JsonMapper.ToJson(tempOrderDataJson));
writer.Flush();
writer.Close();
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
}
///
/// 删除Midas的订单信息
///
///
///
private void RemoveMidasOrder(OrderData orderData, bool bIsBefore)
{
if (!GetIsOrderDataValidate(orderData)) return;
string saveOrderFilePath = GetSaveOrderFileName();
if (string.IsNullOrEmpty(saveOrderFilePath)) return;
try
{
StreamReader reader = new StreamReader(saveOrderFilePath);
var content = reader.ReadToEnd().Trim();
reader.Close();
OrderDataJson tempOrderDataJson = null;
if (string.IsNullOrEmpty(content))
tempOrderDataJson = new OrderDataJson();
else
tempOrderDataJson = JsonMapper.ToObject(content);
string firstKey = orderData.openID;
string secondKey = orderData.money;
if (bIsBefore)//充值前
{
if (tempOrderDataJson.before != null && tempOrderDataJson.before.ContainsKey(firstKey) && tempOrderDataJson.before[firstKey].ContainsKey(secondKey))
{
List temOrderList = new List(tempOrderDataJson.before[firstKey][secondKey]);
for (int i = 0; i < temOrderList.Count; ++i)
{
if (IsOrderDataEqual(temOrderList[i], orderData))
{
temOrderList.RemoveAt(i);
break;
}
}
tempOrderDataJson.before[firstKey][secondKey] = temOrderList.ToArray();
}
}else
{
if (tempOrderDataJson.after != null && tempOrderDataJson.after.ContainsKey(firstKey) && tempOrderDataJson.after[firstKey].ContainsKey(secondKey))
{
List temOrderList = new List(tempOrderDataJson.after[firstKey][secondKey]);
for (int i = 0; i < temOrderList.Count; ++i)
{
if (IsOrderDataEqual(temOrderList[i], orderData))
{
temOrderList.RemoveAt(i);
break;
}
}
tempOrderDataJson.after[firstKey][secondKey] = temOrderList.ToArray();
}
}
StreamWriter writer = new StreamWriter(new FileStream(saveOrderFilePath, FileMode.Create));
writer.Write(JsonMapper.ToJson(tempOrderDataJson));
writer.Flush();
writer.Close();
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
}
/// 2.生成支付回调对象
private class MyMidasPayCallback : MidasPayCallback
{
void MidasPayCallback.OnMidasLoginExpired()
{
TencentSDKManager.Instance.SetMessage("MainScript.OnMidasLoginExpired");
}
void MidasPayCallback.OnMidasPayFinished(APMidasResponse result)
{
TencentSDKManager.Instance.SetMessage("MainScript.OnMidasPayFinished: Pay result = " + result.ToString());
if (result.resultCode == 0)//充值成功
{
TencentSDKManager.Instance.SetMessage("充值成功");
if (TencentSDKManager.Instance.m_payType == MidasPayType.GameMoney)
{
TencentSDKManager.Instance.SaveMidasOrder(TencentSDKManager.Instance.m_lastOrderData, false);
}
}
else//充值失败
{
TencentSDKManager.Instance.SetMessage("充值失败");
if (TencentSDKManager.Instance.m_payType == MidasPayType.GameMoney)
{
TencentSDKManager.Instance.RemoveMidasOrder(TencentSDKManager.Instance.m_lastOrderData, true);
}
}
}
}