chore: 简化 InstallationController 逻辑

oneRain 2019-08-08 18:49:28 +08:00
parent c994aad93e
commit 265a34aa9b
8 changed files with 62 additions and 138 deletions

View File

@ -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 {

View File

@ -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"]);

View File

@ -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;

View File

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

View File

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

View File

@ -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) {

View File

@ -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(_ => {

View File

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