chore: 简化 InstallationController 逻辑
parent
c994aad93e
commit
265a34aa9b
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
|
|
|
@ -5,43 +5,35 @@ using System.Threading.Tasks;
|
|||
using LeanCloud.Utilities;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace LeanCloud.Storage.Internal
|
||||
{
|
||||
public class AVCloudCodeController
|
||||
{
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public class AVCloudCodeController {
|
||||
public Task<T> CallFunctionAsync<T>(String name,
|
||||
IDictionary<string, object> parameters,
|
||||
string sessionToken,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
CancellationToken cancellationToken) {
|
||||
var command = new EngineCommand {
|
||||
Path = $"functions/{Uri.EscapeUriString(name)}",
|
||||
Method = HttpMethod.Post,
|
||||
Content = parameters
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||
{
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||
if (!decoded.ContainsKey("result"))
|
||||
{
|
||||
if (!decoded.ContainsKey("result")) {
|
||||
return default(T);
|
||||
}
|
||||
return Conversion.To<T>(decoded["result"]);
|
||||
});
|
||||
}
|
||||
|
||||
public Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, string sessionToken, CancellationToken cancellationToken)
|
||||
{
|
||||
public Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, string sessionToken, CancellationToken cancellationToken) {
|
||||
var command = new EngineCommand {
|
||||
Path = $"call/{Uri.EscapeUriString(name)}",
|
||||
Method = HttpMethod.Post,
|
||||
Content = parameters
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||
{
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||
if (!decoded.ContainsKey("result"))
|
||||
{
|
||||
if (!decoded.ContainsKey("result")) {
|
||||
return default(T);
|
||||
}
|
||||
return Conversion.To<T>(decoded["result"]);
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace LeanCloud.Storage.Internal {
|
|||
Content = new StringContent(JsonConvert.SerializeObject(command.Content))
|
||||
};
|
||||
|
||||
var headers = await GetHeadersAsync();
|
||||
var headers = GetHeadersAsync();
|
||||
foreach (var header in headers) {
|
||||
if (!string.IsNullOrEmpty(header.Value)) {
|
||||
request.Headers.Add(header.Key, header.Value);
|
||||
|
@ -89,10 +89,10 @@ namespace LeanCloud.Storage.Internal {
|
|||
|
||||
private const string revocableSessionTokenTrueValue = "1";
|
||||
|
||||
async Task<Dictionary<string, string>> GetHeadersAsync() {
|
||||
Dictionary<string, string> GetHeadersAsync() {
|
||||
var headers = new Dictionary<string, string>();
|
||||
var installationId = await AVPlugins.Instance.InstallationIdController.GetAsync();
|
||||
headers.Add("X-LC-Installation-Id", installationId.ToString());
|
||||
var installationId = AVPlugins.Instance.InstallationIdController.Get();
|
||||
headers.Add("X-LC-Installation-Id", installationId);
|
||||
var conf = AVClient.CurrentConfiguration;
|
||||
headers.Add("X-LC-Id", conf.ApplicationId);
|
||||
long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||
|
|
|
@ -1,59 +1,39 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
/// <summary>
|
||||
/// 临时方案,后面将有 Android 和 iOS 提供 device token
|
||||
/// </summary>
|
||||
public class InstallationIdController {
|
||||
private const string InstallationIdKey = "InstallationId";
|
||||
private string installationId;
|
||||
private readonly object mutex = new object();
|
||||
private Guid? installationId;
|
||||
|
||||
public Task SetAsync(Guid? installationId) {
|
||||
lock (mutex) {
|
||||
Task saveTask;
|
||||
|
||||
if (installationId == null) {
|
||||
saveTask = AVPlugins.Instance.StorageController
|
||||
.LoadAsync()
|
||||
.OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey))
|
||||
.Unwrap();
|
||||
} else {
|
||||
saveTask = AVPlugins.Instance.StorageController
|
||||
.LoadAsync()
|
||||
.OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString()))
|
||||
.Unwrap();
|
||||
}
|
||||
this.installationId = installationId;
|
||||
return saveTask;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Guid?> GetAsync() {
|
||||
lock (mutex) {
|
||||
if (installationId != null) {
|
||||
return Task.FromResult(installationId);
|
||||
public string Get() {
|
||||
if (installationId == null) {
|
||||
lock (mutex) {
|
||||
if (installationId == null) {
|
||||
string installationPath = "installation.conf";
|
||||
// 文件读取或从 Native 平台读取
|
||||
if (File.Exists(installationPath)) {
|
||||
using (StreamReader reader = new StreamReader(installationPath)) {
|
||||
installationId = reader.ReadToEnd();
|
||||
if (installationId != null) {
|
||||
return installationId;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 生成新的 device token
|
||||
Guid newInstallationId = Guid.NewGuid();
|
||||
installationId = newInstallationId.ToString();
|
||||
// 写回文件
|
||||
using (StreamWriter writer = new StreamWriter(installationPath)) {
|
||||
writer.Write(installationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AVPlugins.Instance.StorageController
|
||||
.LoadAsync()
|
||||
.OnSuccess(s => {
|
||||
object id;
|
||||
s.Result.TryGetValue(InstallationIdKey, out id);
|
||||
try {
|
||||
lock (mutex) {
|
||||
installationId = new Guid((string)id);
|
||||
return Task.FromResult(installationId);
|
||||
}
|
||||
} catch (Exception) {
|
||||
var newInstallationId = Guid.NewGuid();
|
||||
return SetAsync(newInstallationId).OnSuccess<Guid?>(_ => newInstallationId);
|
||||
}
|
||||
})
|
||||
.Unwrap();
|
||||
}
|
||||
|
||||
public Task ClearAsync() {
|
||||
return SetAsync(null);
|
||||
return installationId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
/// <summary>
|
||||
/// <code>IAVObjectCurrentController</code> controls the single-instance <see cref="AVObject"/>
|
||||
/// persistence used throughout the code-base. Sample usages are <see cref="AVUser.CurrentUser"/> and
|
||||
/// <see cref="AVInstallation.CurrentInstallation"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object being persisted.</typeparam>
|
||||
public interface IAVObjectCurrentController<T> where T : AVObject {
|
||||
/// <summary>
|
||||
/// Persists current <see cref="AVObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj"><see cref="AVObject"/> to be persisted.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
Task SetAsync(T obj, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the persisted current <see cref="AVObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
Task<T> GetAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Task"/> that resolves to <code>true</code> if current
|
||||
/// <see cref="AVObject"/> exists.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
Task<bool> ExistsAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns <code>true</code> if the given <see cref="AVObject"/> is the persisted current
|
||||
/// <see cref="AVObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to check.</param>
|
||||
/// <returns>True if <code>obj</code> is the current persisted <see cref="AVObject"/>.</returns>
|
||||
bool IsCurrent(T obj);
|
||||
|
||||
/// <summary>
|
||||
/// Nullifies the current <see cref="AVObject"/> from memory.
|
||||
/// </summary>
|
||||
void ClearFromMemory();
|
||||
|
||||
/// <summary>
|
||||
/// Clears current <see cref="AVObject"/> from disk.
|
||||
/// </summary>
|
||||
void ClearFromDisk();
|
||||
}
|
||||
}
|
|
@ -30,14 +30,11 @@ namespace LeanCloud.Storage.Internal {
|
|||
}
|
||||
|
||||
public Type GetType(string className) {
|
||||
ObjectSubclassInfo info = null;
|
||||
mutex.EnterReadLock();
|
||||
registeredSubclasses.TryGetValue(className, out info);
|
||||
registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo info);
|
||||
mutex.ExitReadLock();
|
||||
|
||||
return info != null
|
||||
? info.TypeInfo.AsType()
|
||||
: null;
|
||||
return info?.TypeInfo.AsType();
|
||||
}
|
||||
|
||||
public bool IsTypeValid(string className, Type type) {
|
||||
|
|
|
@ -19,13 +19,13 @@ namespace LeanCloud.Storage.Internal {
|
|||
dictionary = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
internal Task SaveAsync() {
|
||||
internal async Task SaveAsync() {
|
||||
string json;
|
||||
locker.EnterReadLock();
|
||||
json = JsonConvert.SerializeObject(dictionary);
|
||||
json = await JsonUtils.SerializeObjectAsync(dictionary);
|
||||
locker.ExitReadLock();
|
||||
using (var sw = new StreamWriter(filePath)) {
|
||||
return sw.WriteAsync(json);
|
||||
await sw.WriteAsync(json);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +150,7 @@ namespace LeanCloud.Storage.Internal {
|
|||
private readonly TaskQueue taskQueue = new TaskQueue();
|
||||
private readonly Task<string> fileTask;
|
||||
private StorageDictionary storageDictionary;
|
||||
private IDictionary<string, object> storage;
|
||||
|
||||
public StorageController(string fileNamePrefix) {
|
||||
fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => {
|
||||
|
|
|
@ -3,17 +3,24 @@ using Newtonsoft.Json;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
/// <summary>
|
||||
/// 为 Json 解析提供异步接口
|
||||
/// </summary>
|
||||
public static class JsonUtils {
|
||||
public static Task<T> DeserializeObjectAsync<T>(string str) {
|
||||
var tcs = new TaskCompletionSource<T>();
|
||||
Task.Run(() => {
|
||||
try {
|
||||
tcs.SetResult(JsonConvert.DeserializeObject<T>(str));
|
||||
} catch (Exception e) {
|
||||
tcs.SetException(e);
|
||||
}
|
||||
public static async Task<string> SerializeObjectAsync(object obj) {
|
||||
string str = null;
|
||||
await Task.Run(() => {
|
||||
str = JsonConvert.SerializeObject(obj);
|
||||
});
|
||||
return tcs.Task;
|
||||
return str;
|
||||
}
|
||||
|
||||
public static async Task<T> DeserializeObjectAsync<T>(string str, params JsonConverter[] converters) {
|
||||
T obj = default;
|
||||
await Task.Run(() => {
|
||||
obj = JsonConvert.DeserializeObject<T>(str, converters);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue