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;
using System.Net; using System.Net;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Net.Http; using System.Net.Http;
namespace LeanCloud.Storage.Internal { namespace LeanCloud.Storage.Internal {

View File

@ -5,43 +5,35 @@ using System.Threading.Tasks;
using LeanCloud.Utilities; using LeanCloud.Utilities;
using System.Net.Http; using System.Net.Http;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal {
{ public class AVCloudCodeController {
public class AVCloudCodeController
{
public Task<T> CallFunctionAsync<T>(String name, public Task<T> CallFunctionAsync<T>(String name,
IDictionary<string, object> parameters, IDictionary<string, object> parameters,
string sessionToken, string sessionToken,
CancellationToken cancellationToken) CancellationToken cancellationToken) {
{
var command = new EngineCommand { var command = new EngineCommand {
Path = $"functions/{Uri.EscapeUriString(name)}", Path = $"functions/{Uri.EscapeUriString(name)}",
Method = HttpMethod.Post, Method = HttpMethod.Post,
Content = parameters 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>; var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
if (!decoded.ContainsKey("result")) if (!decoded.ContainsKey("result")) {
{
return default(T); return default(T);
} }
return Conversion.To<T>(decoded["result"]); 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 { var command = new EngineCommand {
Path = $"call/{Uri.EscapeUriString(name)}", Path = $"call/{Uri.EscapeUriString(name)}",
Method = HttpMethod.Post, Method = HttpMethod.Post,
Content = parameters 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>; var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
if (!decoded.ContainsKey("result")) if (!decoded.ContainsKey("result")) {
{
return default(T); return default(T);
} }
return Conversion.To<T>(decoded["result"]); return Conversion.To<T>(decoded["result"]);

View File

@ -36,7 +36,7 @@ namespace LeanCloud.Storage.Internal {
Content = new StringContent(JsonConvert.SerializeObject(command.Content)) Content = new StringContent(JsonConvert.SerializeObject(command.Content))
}; };
var headers = await GetHeadersAsync(); var headers = GetHeadersAsync();
foreach (var header in headers) { foreach (var header in headers) {
if (!string.IsNullOrEmpty(header.Value)) { if (!string.IsNullOrEmpty(header.Value)) {
request.Headers.Add(header.Key, header.Value); request.Headers.Add(header.Key, header.Value);
@ -89,10 +89,10 @@ namespace LeanCloud.Storage.Internal {
private const string revocableSessionTokenTrueValue = "1"; private const string revocableSessionTokenTrueValue = "1";
async Task<Dictionary<string, string>> GetHeadersAsync() { Dictionary<string, string> GetHeadersAsync() {
var headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();
var installationId = await AVPlugins.Instance.InstallationIdController.GetAsync(); var installationId = AVPlugins.Instance.InstallationIdController.Get();
headers.Add("X-LC-Installation-Id", installationId.ToString()); headers.Add("X-LC-Installation-Id", installationId);
var conf = AVClient.CurrentConfiguration; var conf = AVClient.CurrentConfiguration;
headers.Add("X-LC-Id", conf.ApplicationId); headers.Add("X-LC-Id", conf.ApplicationId);
long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds; long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;

View File

@ -1,59 +1,39 @@
using System; using System;
using System.Threading.Tasks; using System.IO;
namespace LeanCloud.Storage.Internal { namespace LeanCloud.Storage.Internal {
/// <summary>
/// 临时方案,后面将有 Android 和 iOS 提供 device token
/// </summary>
public class InstallationIdController { public class InstallationIdController {
private const string InstallationIdKey = "InstallationId"; private string installationId;
private readonly object mutex = new object(); private readonly object mutex = new object();
private Guid? installationId;
public Task SetAsync(Guid? installationId) { public string Get() {
lock (mutex) { if (installationId == null) {
Task saveTask; lock (mutex) {
if (installationId == null) {
if (installationId == null) { string installationPath = "installation.conf";
saveTask = AVPlugins.Instance.StorageController // 文件读取或从 Native 平台读取
.LoadAsync() if (File.Exists(installationPath)) {
.OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey)) using (StreamReader reader = new StreamReader(installationPath)) {
.Unwrap(); installationId = reader.ReadToEnd();
} else { if (installationId != null) {
saveTask = AVPlugins.Instance.StorageController return installationId;
.LoadAsync() }
.OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString())) }
.Unwrap(); }
} // 生成新的 device token
this.installationId = installationId; Guid newInstallationId = Guid.NewGuid();
return saveTask; installationId = newInstallationId.ToString();
} // 写回文件
} using (StreamWriter writer = new StreamWriter(installationPath)) {
writer.Write(installationId);
public Task<Guid?> GetAsync() { }
lock (mutex) { }
if (installationId != null) {
return Task.FromResult(installationId);
} }
} }
return 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);
} }
} }
} }

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) { public Type GetType(string className) {
ObjectSubclassInfo info = null;
mutex.EnterReadLock(); mutex.EnterReadLock();
registeredSubclasses.TryGetValue(className, out info); registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo info);
mutex.ExitReadLock(); mutex.ExitReadLock();
return info != null return info?.TypeInfo.AsType();
? info.TypeInfo.AsType()
: null;
} }
public bool IsTypeValid(string className, Type type) { public bool IsTypeValid(string className, Type type) {

View File

@ -19,13 +19,13 @@ namespace LeanCloud.Storage.Internal {
dictionary = new Dictionary<string, object>(); dictionary = new Dictionary<string, object>();
} }
internal Task SaveAsync() { internal async Task SaveAsync() {
string json; string json;
locker.EnterReadLock(); locker.EnterReadLock();
json = JsonConvert.SerializeObject(dictionary); json = await JsonUtils.SerializeObjectAsync(dictionary);
locker.ExitReadLock(); locker.ExitReadLock();
using (var sw = new StreamWriter(filePath)) { 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 TaskQueue taskQueue = new TaskQueue();
private readonly Task<string> fileTask; private readonly Task<string> fileTask;
private StorageDictionary storageDictionary; private StorageDictionary storageDictionary;
private IDictionary<string, object> storage;
public StorageController(string fileNamePrefix) { public StorageController(string fileNamePrefix) {
fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => { fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => {

View File

@ -3,17 +3,24 @@ using Newtonsoft.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LeanCloud.Storage.Internal { namespace LeanCloud.Storage.Internal {
/// <summary>
/// 为 Json 解析提供异步接口
/// </summary>
public static class JsonUtils { public static class JsonUtils {
public static Task<T> DeserializeObjectAsync<T>(string str) { public static async Task<string> SerializeObjectAsync(object obj) {
var tcs = new TaskCompletionSource<T>(); string str = null;
Task.Run(() => { await Task.Run(() => {
try { str = JsonConvert.SerializeObject(obj);
tcs.SetResult(JsonConvert.DeserializeObject<T>(str));
} catch (Exception e) {
tcs.SetException(e);
}
}); });
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;
} }
} }
} }