
ããã«ã¡ã¯
å€ãã®äººãç¥ã£ãŠããããã«ãPavel Durovã¯èªåã®MTProtoãããã³ã«ã«åºã¥ããŠãWhat's Appããã®ä»ã®äººæ°ã®ããã€ã³ã¹ã¿ã³ãã¡ãã»ã³ãžã£ãŒã®æ°ããã¯ããŒã³ãéçºããŠããŸãã
æè¿ãã¢ã¡ãªã«ã®äŒç€ŸããTelegramãšåŒã°ãããã®ãããã³ã«çšã®iOSã¯ã©ã€ã¢ã³ãããªãªãŒã¹ããŸããã ãããšäžŠè¡ããŠãAndroidã¯ã©ã€ã¢ã³ãã®éçºç«¶äºãéå¬ãããŠããŸã ã
æè¿ã第2段éãå®äºãã人ã ã¯èªåèªèº«ãå«ãå·¥èžåãéããŸããã ç§ã¯ããã«èšããç§ã¯ç¬¬äºæ®µéãçµãŠããªãã£ãã
å€ãã®åå è ãšã¯ç°ãªããç§ã¯éçºã«CïŒèšèªãšXamarinã䜿çšããŸãããXamarinã«ã€ããŠã¯RuNetã§å°ãæ å ±ã説æããããã以äžã§è©³ãã説æããŸãã
ãšã³ããªãŒã®ä»£ããã«
ç§ã¯ã¢ãã£ãªãšã€ãã§ãã ç§ã¯ãŸã åŠçã ã£ã2çªç®ã®ããŒãžã§ã³ããåäŸãšä»äºãããŠããŸãããããã®ãã©ãããã©ãŒã ã®æ©èœãšæ©èœãããç¥ã£ãŠããŸãã å°ãåãŸã§ãã¢ãã€ã«ããã€ã¹ã§ãAndroid CïŒããšãããªã¯ãšã¹ããéçºããXamarin-MonoDroidã«é£ããŠè¡ããŸããã ãããããããŸã§ç§ã¯ããã ãã§éãã§æžãããããã¯æåã®äž»èŠãªAndroidãããžã§ã¯ãã§ãããç§ã¯ããã«ã€ããŠè©±ãããã§ãã ãã®èšäºãç解ããã«ã¯ãCïŒã. NETã®ç¥èãããã³å°ãªããšãAndroidã®åºæ¬çãªç解ãå¿ èŠã§ãã
ããã¯äœã§ãã-äžèšã§èšãã°
Xamarinã¯ãNat FriedmanãšGNOMEããã³Monoã®èè ã§ããMiguel de Icazaã«ãã£ãŠäœæãããäŒç€ŸïŒããã³ã¢ãã€ã«ãã©ãããã©ãŒã ïŒã§ãã ãããã£ãŠãXamarinã¯Monoã®è«ççãªéçºã§ãã
Xamarinã䜿çšãããšãCïŒã§AnroidãšiOSã®ãã€ãã£ãã¢ããªã±ãŒã·ã§ã³ãäœæã§ããŸã ãããã¯çŽ æŽãããããšã§ãã å人çã«ã¯ãæªæ¥ã¯ãã€ããªããã¯ãã¹ãã©ãããã©ãŒã ã«ãããšä¿¡ããŠããŸãã ãŸããMacã ãããŠã ãã€ã Xamarinã®äžéšã«ãªãå¯èœæ§ã®ããMonoBerryã«ç©æ¥µçã«æ祚ããŠããŸãã
ææŠãã
競äºã®èª²é¡ã¯ãæäŸãããMTProtoãããã³ã«ã®å®è£ ïŒç¬¬1段éïŒãšæ¬æ Œçãªã¢ããªã±ãŒã·ã§ã³ã®äœæïŒç¬¬2段éïŒã§ããã 第äžæ®µéã§ããªããžã§ã³ã
ãããã³ã«å šäœã¯ãé«åºŠãªæå·åãªã©ã®ããããçš®é¡ã®å©ç¹ãåããRPCã®å®è£ ã§ãã
解決ç
以äžããããã®åé¡ã®è§£æ±ºæ¹æ³ã«ã€ããŠèª¬æããŸãã ããã§ã¯å§ããŸãããã
Xamarinã®å ¥æ
Xamarinã®äŸ¡æ Œã¯2000ãã«ã§ãã ã¯ããããã§ãã ãæ°ã«å ¥ãã®ã¹ã¿ãžãªã«æžã蟌ã¿ããå Žåããã®äŸ¡æ Œã¯ãã©ãããã©ãŒã ããšã«999ãã«ã§ãã MonoDevelopã«ååãªç°å¢ãããã°ãMonoDevelop-ãã©ãããã©ãŒã ããã299ãã«ã§ãã èè ã«é£çµ¡ããŠããã©ãããã©ãŒã ããšã«æ倧799ãã«ã®å²åŒããé¡ãããããšãã§ããŸããã
xamarinãå ¥æããã«ã¯ã©ãããã°ããã§ããïŒ ãŸããåå¿è åãã«ã¯ãtorrent
ãœãªã¥ãŒã·ã§ã³æ§é ã®äœæ
åè¿°ããããã«ãXamarinã¯åã¢ãã€ã«OSã®ãã€ãã£ãã³ãŒããäœæããŸãã ã€ãŸããåOSã«ã¯ç¬èªã®ã¢ã»ã³ããªããããŸããããããã®éã®ã³ãŒãã¯åå²ããå¿ èŠããããŸãã Xamarinã®äœæè ã¯ãããè¡ãããã®3ã€ã®æ¹æ³ãæäŸããŠããŸãããVisual Studioã«ãšã£ãŠæãç°¡åãªã®ã¯ãç°å¢ã«çŽæ¥çµ±åãããçŽ æŽãããProject LinkerãŠãŒãã£ãªãã£ã§ãã
æ°åã®ããŠã¹ã¯ãªãã¯ãšã¯ãã¹ãã©ãããã©ãŒã ãœãªã¥ãŒã·ã§ã³ãçšæãããŠããŸãã

ãã¹ãŠã®ãã¡ã€ã«ã¯ãªã³ã¯ãšããŠæ¥ç¶ãããã¡ã€ã³ãããžã§ã¯ãã§ã®å€æŽã¯ãã¹ãŠã®ãªã³ã¯ããããããžã§ã¯ãã«è¡šç€ºãããŸãã
![]() | ![]() |
ãã®ãŠãŒãã£ãªãã£ã¯ããExtension Managerãã¹ã¿ãžãªããã€ã³ã¹ããŒã«ãããŸãã
ãœãªã¥ãŒã·ã§ã³æ§é
äž»ãªã¢ã»ã³ããªã¯MTProto.CoreãšTalks.Backendã§ãã ãããã¯.net 4.5ã®éåžžã®ã¢ã»ã³ããªã§ãããåäœãã¹ãã§ã«ããŒãããŠããŸãã
Mono.Stubã¯Monoã®ç¹å®ã®ã¯ã©ã¹ã§ãç¹ã«ããããBigIntegerã䜿çšããŠããŸãã
Droidãã©ã«ããŒ-ãããžã§ã¯ãã®Androidã¯ããŒã³ãDataflowãå«ãŸããŠããŸã-ãããã¯ãgithubããã®TPL.Dataflowã®ãœãŒã¹ã§ãã ç§ã¯ãããžã§ã¯ãã§CïŒ5.0ã®éåææ©èœãç©æ¥µçã«äœ¿çšããŠããŸãã
Platfromãã©ã«ããŒ-åãã©ãããã©ãŒã ã®ç¹å®ã®å®è£ ã ãããŸã§ã®ãšãããããã¯Androidã®ã¿ã§ãã
MTProto.Core
ããã¯ãããã³ã«ã®å®è£ ã§ãã ãããã³ã«å šäœã¯ãé«åºŠãªæå·åãšãã³ã³ããã®åœ¢æããã¡ã€ã«ã®æççãªéåä¿¡ãªã©ã®è¿œå æ©èœãåããRPCã§ãã ãããã£ãŠãIMã¯ã©ã€ã¢ã³ããå®è£ ããã«ã¯ãRPCèŠæ±ã®å®è¡æ¹æ³ãå¿çã®åä¿¡æ¹æ³ãããã³çä¿¡ã·ã¹ãã ã¡ãã»ãŒãžãšã¹ããŒã¿ã¹æŽæ°ã®åŠçæ¹æ³ãåŠç¿ããå¿ èŠããããŸãã
æ©èœã®ãã¹ãŠïŒéåæãšããŒã¿ãããŒã
ãã£ãšéåæ
CïŒ5.0ã§ã¯ãã¿ã¹ã¯éåæãã¿ãŒã³ïŒTAPïŒã«åºã¥ãéåæã³ãŒãã®éçºãå€§å¹ ã«ç°¡çŽ åããããã€ãã®ããŒã¯ãŒããå°å ¥ãããŸããã ãããã¯MSDNã§éåžžã«ãã説æãããŠããŸã ã
ãã¹ãŠã®IOæäœã¯éåæã§ããå¿ èŠããããæãã®ããã§ããå¿ èŠããããŸãã
public async Task RunAsync() { await _cl.LoadSettings().ConfigureAwait(false); if (await _cl.CheckAndGenerateAuth().ConfigureAwait(false)) { await _cl.RunAsync().ConfigureAwait(false); } if ((_cl.Settings.DataCenters == null) || (_cl.Settings.DataCenters.Count == 0)) { await _cl.GetConfig().ConfigureAwait(false); } _db = await TalksDatabase.GetDatabase().ConfigureAwait(false); _ldm = new LocalDataManager(_db); _cl.ProcessUpdateAsync = ProcessUpdateAsync; }
CïŒã§ã®éåæããã°ã©ãã³ã°ã®æåãªå°é家ã®1人ã§ããStephen Clearyã¯ã async-awaitã䜿çšããããã®ããã€ãã®ååãæžããŸããã èªãã§ããªãå Žåã¯ããå§ãããŸãã
ããã¹ãŠéåæãã¢ãããŒãã®æ¬è³ªã¯ãã³ãŒã«ããªãŒäžã®ãã¹ãŠã®ã¡ãœãããéåæã§ãããã€ãã³ãã§éå§ããIOæäœã§çŽæ¥çµäºããããšã§ãïŒãã®å ŽåïŒã
ããšãã°ããã¿ã³ã®ã¯ãªãã¯ãéåæã§åŠçããå¿ èŠãããå ŽåïŒ
async void button_Click(object sender, EventArgs e) { _button.Enabled = false; await _presenter.SendMessage(); }
次ã«ãPresenterã®åŒã³åºãããªãŒã®ãã¹ãŠã®ã¡ãœãããéåæã«ããŸãã
ã³ãŒã
ãããŠã³ã¢ã§ïŒ
public Task<bool> SendMessage() { return SendMessageToUser(); }
public async Task<bool> SendMessageToUser() { ... try { _imv.AddMineMessage(msg); string msgText = _imv.PendingMessage; _imv.PendingMessage = ""; // messages.sendMessage#4cde0aab peer:InputPeer message:string random_id:long = messages.SentMessage; var result = await _model.PerformRpcCall("messages.sendMessage", InputPeerFactory.CreatePeer(_model, PeerType.inputPeerContact, _imv.ChatId), msgText, LongRandom(r)); if (result.Success) { // messages.sentMessage#d1f4d35c id:int date:int pts:int seq:int = messages.SentMessage; msg.Id = result.Answer.ExtractValue<int>("id"); ... msg.State = BL.Messages.MessageState.Sent; _imv.IvalidateList(); await _model.ProcessSentMessage(result.Answer, _imv.ChatId, msg); return true; } else { msg.State = BL.Messages.MessageState.Failed; _imv.SendSmallMessage("Problem sending message: " + result.Error.ToString()); return false; } } catch (Exception ex) { ... } }
ãããŠã³ã¢ã§ïŒ
public Task<RpcAnswer> PerformRpcCall(string combinatorName, params object[] pars) { return _cl.PerformRpcCall(combinatorName, pars); }
public async Task<RpcAnswer> PerformRpcCall(string combinatorName, params object[] pars) { try { /*...*/ var confirm = CreateConfirm(); // WriteOnceBlock<RpcAnswer> answer = new WriteOnceBlock<RpcAnswer>(e => e); IOutputCombinator oc; if (confirm != null) { var cntrn = new MsgContainer(); cntrn.Add(rpccall); // RPC Call cntrn.Add(confirm); cntrn.Combinator = _tlc.Decompose(0x73f1f8dc); // oc = new OutputMsgContainer(uniqueId, cntrn); } else // { oc = new OutputTLCombinatorInstance(uniqueId, rpccall); } var uhoo = await SendRpcCallAsync(oc).ConfigureAwait(false); _inputAnswersBuffer.LinkTo(answer, new DataflowLinkOptions { MaxMessages = 1 }, i => i.SessionId == _em.SessionId); return await answer.ReceiveAsync(TimeSpan.FromSeconds(60)).ConfigureAwait(false); // } catch (Exception ex) { ... } }
ã芧ã®ãšããããã¹ãŠã®ã¡ãœãããasync-awaitããŒã¯ãŒãã§ããŒã¯ãããŠããããã§ã¯ãããŸããã äžè¬çãªæ¹æ³ã¯æ¬¡ã®ãšããã§ããéåæåŒã³åºãã®åŸã«äœãããå¿ èŠããªããéåæåŒã³åºãã1ã€ããå Žåã¯ãã¡ãœããããåçŽã«ã¿ã¹ã¯ãšããŠè¿ãã®ãçã«ããªã£ãŠããŸãã
å¥ã®ãã©ã¯ãã£ã¹ïŒClearyã®èšäºã§ã説æãããŠããŸãïŒã¯ãã©ã€ãã©ãªå ã®éåæã¡ãœãããã³ã³ããã¹ãããã£ããã£ããŠå®è¡åŸã«ââã³ã³ããã¹ãã«æ»ããªãããã«ããããšã§ãã ã€ãŸã ãã¹ãŠã®éåæåŒã³åºãã«ã¯
.ConfigureAwait(false)
ãå«ãŸããŠããå¿ èŠããããŸãã
.ConfigureAwait(false)
ããããããã¯ãé²ãããã«è¡ãããŸãã 詳现ã«ã€ããŠã¯ãäžèšã®èšäºãã芧ãã ããã
ããŒã¿ãããŒ
TPL.Dataflowã¯ãããŒã¿ãããŒãã¶ã€ã³ãã¿ãŒã³ãŸãã¯åŠçãã€ãã©ã€ã³ãå®è£ ããããã«èšèšãããã©ã€ãã©ãªã§ãã ã©ã€ãã©ãªã®ãœãŒã¹ã³ãŒãã¯githubã§å ¥æã§ããã¢ãã€ã«ããã€ã¹ã§äœ¿çšã§ããŸãã ãã®ã©ã€ãã©ãªã®æ©èœã®è©³çŽ°ã«ã€ããŠã¯ã MSDNã«éä¿¡ããŠãã ããã
ç°¡åã«èšããšãã©ã€ãã©ãªã䜿çšãããšãããŒã¿ã¹ãã¬ãŒãžãŸãã¯åŠçãŠãããã§æ§æããããã€ãã©ã€ã³ãæ§ç¯ããããæ¡ä»¶äžã§ãªã³ã¯ããããšãã§ããŸãã åœåãç§ã®ãããžã§ã¯ãã«ã¯ãå ¥åãã±ãããšåºåãã±ããçšã®2ã€ã®ãã€ãã©ã€ã³ããããŸããã ãªãã¡ã¯ã¿ãªã³ã°åŸãçä¿¡ããã±ãŒãžçšã«1ã€ã ãæ®ãããšã«ããŸããã
次ã®ããã«ãªããŸãã

äœæããã»ã¹ã¯æ¬¡ã®ããã«ãªããŸãã
BufferBlock<byte[]> _inputBufferBytes = new BufferBlock<byte[]>(); BufferBlock<InputTLCombinatorInstance> _inputBuffer = new BufferBlock<InputTLCombinatorInstance>(); ActionBlock<byte[]> _inputBufferParcer; ActionBlock<TLCombinatorInstance> _inputUpdates; ActionBlock<TLCombinatorInstance> _inputSystemMessages; TransformBlock<InputTLCombinatorInstance, RpcAnswer> _inputAnswers; BufferBlock<RpcAnswer> _inputAnswersBuffer = new BufferBlock<RpcAnswer>(); BufferBlock<RpcAnswer> _inputRejectedBuffer = new BufferBlock<RpcAnswer>(); BufferBlock<InputTLCombinatorInstance> _inputUnsorted = new BufferBlock<InputTLCombinatorInstance>(); // -- // _inputBufferParcer = new ActionBlock<byte[]>(bytes => ProcessInputBuffer(bytes)); _inputSystemMessages = new ActionBlock<TLCombinatorInstance>(tlci => ProcessSystemMessage(tlci)); _inputUpdates = new ActionBlock<TLCombinatorInstance>(tlci => ProcessUpdateAsync(tlci)); _inputAnswers = new TransformBlock<InputTLCombinatorInstance, RpcAnswer>(tlci => ProcessRpcAnswer(tlci)); // from [_inputBufferBytes] to [_inputBufferTransformer] _inputBufferBytes.LinkTo(_inputBufferParcer); // from [_inputBufferTransformer] to [_inputBuffer] //_inputBufferTransformer.LinkTo(_inputBuffer); // if System then from [_inputBuffer] to [_inputSystemMessages] _inputBuffer.LinkTo(_inputSystemMessages, tlciw => _systemCalls.Contains(tlciw.Combinator.Name)); // if Updates then from [_inputBuffer] to [_inputUpdates] _inputBuffer.LinkTo(_inputUpdates, tlciw => tlciw.Combinator.ValueType.Equals("Updates")); // if rpc_result then from [_inputBuffer] to [_inputRpcAnswers] _inputBuffer.LinkTo(_inputAnswers, tlciw => tlciw.Combinator.Name.Equals("rpc_result")); // if rpc_result then from [_inputBuffer] to [_inputRpcAnswers] //_inputBuffer.LinkTo(_inputUnsorted); // and store it [_inputAnswers] to [_inputAnswersBuffer] to process it _inputAnswers.LinkTo(_inputAnswersBuffer); _inputRejectedBuffer.LinkTo(_inputAnswersBuffer);
ã芧ã®ãšãããå ¥åãã€ãé åã¯è§£æãããåé¡ããããããã¡ã«é 眮ãããå¿ èŠã«å¿ããŠãããã解æãããŸãã ç¹ã«ãæŽæ°ãšsystemMessagesã¯ActionBlockã«å°çãããšããã«åŠçãããrpcAnswersã¯æåã«
TransformBlock
ã䜿çšããŠ
TransformBlock
ã次ã«
BufferBlock
è¿œå ãã
BufferBlock
ã ãã±ããã¿ã€ãã®åé¡ã¯ããããã¯ãã€ã³ãæ¡ä»¶ã«åºã¥ããŠ
BufferBlock
å ã§è¡ãããŸãã
ã¡ãœãããåŒã³åºããçŽåŸã«ãWriteOnceBlock-1ã€ã®å€ã®ã¿ãæžã蟌ãããšãã§ãããããã¯ãäœæããŸãã
WriteOnceBlock<RpcAnswer> answer = new WriteOnceBlock<RpcAnswer>(e => e);
ãããŠãRPCå¿çãããã¡ãŒã«ãªã³ã¯ããŸãã
_inputAnswersBuffer.LinkTo(answer, new DataflowLinkOptions { MaxMessages = 1 }, i => i.SessionId == _em.SessionId);
ãããŠãçããæ¥ããŸã§éåæã«åŸ æ©ããŸãã
return await answer.ReceiveAsync(TimeSpan.FromSeconds(60)).ConfigureAwait(false); //
ãŸããçŸæç¹ã§ã¯ãAndroidåãã®ã³ãŒãã1è¡ãæžããŠããªãããšã«ã泚æããŠãã ããã ãã¹ãŠã®éçºãšãã¹ãã¯ã.NET 4.5ã§ã®éåžžã®ã¢ã»ã³ããªã«å¯ŸããŠå®è¡ãããŸãã
Talks.Backend
顧客ããã¯ãšã³ãã IoCã䜿çšããŠMVPèšèšãã¿ãŒã³ã«åŸã£ãŠã¯ã©ã€ã¢ã³ããå®è£ ããããšã決å®ããæåã¯ãã¥ãŒã«ããžãã¯ãå«ãŸããŠããªãããã·ããã¥ãŒããªãšãŒã·ã§ã³ãç®æããŸããããæçµçã«ã¯ç£èŠã³ã³ãããŒã©ãŒãã¯ããã«åªããåäœããããšããçµè«ã«éããŸããã
ããã¯ãšã³ãã®äœææã«ã©ã®ãããªåé¡ããããŸãããïŒ ããŒãããã¯ãžã®ã¢ã¯ã»ã¹ãããŒã¿ããŒã¹ãžã®ã¢ã¯ã»ã¹ããã¡ã€ã«ã·ã¹ãã ãžã®ã¢ã¯ã»ã¹ïŒåçãä¿åããããïŒã ããã¯ãšã³ãã®æ®ãã¯ãäžè¬çãªMVPå®è£ ã§ãïŒãã¬ãŒã³ã¿ãŒãšIViewã®ã»ãã
ããŒãããã¯ãžã®ã¢ã¯ã»ã¹
ããŒãããã¯ã«ã¢ã¯ã»ã¹ããããã«ãXamarinããŒã ã¯ãã§ã«ãã¹ãŠãçšæããŠããŸãã 圌ãã¯ãã¢ãã€ã«ããã€ã¹äžã®äžé£ã®æ©èœïŒããŒãããã¯ãGPSãã«ã¡ã©ïŒãã¯ãã¹ãã©ãããã©ãŒã ã§ã«ãã»ã«åããXamarin.Mobileã©ã€ãã©ãªãéçºããŸããã ãŸããasync-awaitãå®å šã«ãµããŒãããŠããŸãã
ãããã£ãŠãããŒãããã¯ãžã®ã¢ã¯ã»ã¹ã¯éåžžã«ç°¡åã§ãã
#if __ANDROID__ public async Task GetAddressbook(Android.Content.Context context) { contacts = new AddressBook(context); #else public async Task GetAddressbook() { contacts = new AddressBook(); #endif if (!await contacts.RequestPermission()) { Trace.WriteLineIf(clientSwitch.TraceInfo, "Permission for contacts denied", "[ContactsPresenter.PopulateAddressbook]"); _view.SendSmallMessage("CONTACTS PERMISSON DENIED"); return; } else { _icv.PlainContacts = new ListItemCollection<ListItemValue>( (from c in contacts where (c.Phones.Count() > 0) select new ListItemValue(c)).ToList()); } }
__ANDROID__
ã³ã³ãã€ã«
__ANDROID__
å°å ¥ãããã®ã¯ãã³ã³ããã¹ãã§ã¯Androidã®é£çµ¡å ã®ãªã¹ããå¿ èŠã§ãããä»ã®OSã§ã¯å¿ èŠãªãããã§ãã
ããã§ã¯ãã¯ãã¹ãã©ãããã©ãŒã ãœãªã¥ãŒã·ã§ã³ã®ããã·ããã¥ãŒã®æ¬ ç¹ã®1ã€ãèŠãããšãã§ããŸãã å²ãåœãŠæã«ã¯ãå§ã®æåã®æåã§é£çµ¡å ãã°ã«ãŒãåããå¿ èŠããããŸããã Androidã®å Žåãããã¯ã°ã«ãŒãåãå®è£ ããListItemCollectionã¯ã©ã¹ãäœæããããšã§è¡ãããŸãããã®å žåçãªäŸã¯ã€ã³ã¿ãŒãããã§å ¥æã§ããŸãã iOSã§ã¯ããã®ãããªã°ã«ãŒãåãäœæããããã®ãŸã£ããç°ãªãã¢ãããŒãã¯ãWinPhoneã®ã¢ãããŒãã§ã-ããããŸããã ãã®ãããViewã§çŽæ¥é£çµ¡å ãåä¿¡ããŠââã°ã«ãŒãåããããšãé©åã§ãã
ç§ã®æèŠã§ã¯ãããããã€ããªããã¯ãã¹ãã©ãããã©ãŒã éçºã®äž»ãªåé¡ã§ãã ãã©ãããã©ãŒã ããæœè±¡åããå¿ èŠãããå Žæãšã䟡å€ããªãå Žæãæ確ã«ç解ããå¿ èŠããããŸãã çµéšããããšæããŸãã
ããŒã¿ããŒã¹ãžã®ã¢ã¯ã»ã¹
Xamarinã¯ãåçŽãªORM SQLite.Netãä»ããŠããŒã¿ããŒã¹ã«ã¢ã¯ã»ã¹ããããšããå§ãããŸãã ãããã®æšå¥šäºé ãç¡èŠããŠããã©ã€ããŒãä»ããŠããŒã¿ããŒã¹ãçŽæ¥æäœããããšãããšãæçµçã«ã¯ãçµéšè±å¯ãªéçºè ã®ã¢ããã€ã¹ãèãæ¹ãè¯ãããšã«æ°ä»ããŸããã
SQLite.Netã®æäœæ¹æ³ã説æããããšã«ã¯ããŸãæå³ããããŸãããSQlite.Netãæ¥ç¶ãããç¶æ ã§ã¢ã»ã³ããªããã¹ãããã«ã¯ãå ¬åŒWebãµã€ãwww.sqlite.org/download.htmlã§å ¥æã§ãããããžã§ã¯ãã«sqliteãã€ããªãå¿ èŠã§ãã
å¥ã«ãSQLite.Netã¯TAPãšasync-awaitãå®å šã«ãµããŒãããŠããããšã«æ³šæããŠãã ããã
ããŒã¿ããŒã¹ãžã®ã¢ã¯ã»ã¹ãç°¡çŽ åããããã«ãSQLite.SQLiteAsyncConnectionã¯ã©ã¹ãäžé£ã®æ±çšã¯ã©ã¹ã§æ¡åŒµããããšããå§ãããŸãã
#region Public Methods public Task<List<T>> GetItemsAsync<T>() where T : IBusinessEntity, new() { return Table<T>().ToListAsync(); } public Task<T> GetItemAsync<T>(int id) where T : IBusinessEntity, new() { return GetAsync<T>(id); } public async Task<bool> CheckRowExistAsync<T>(int id) where T : IBusinessEntity, new() { string tblName = typeof(T).Name; return await ExecuteScalarAsync<int>("select 1 from " + tblName + " where Id = ?", id).ConfigureAwait(false) == 1; } public async Task<int> SaveItemAsync<T>(T item) where T : IBusinessEntity, new() { if (await CheckRowExistAsync<T>(item.Id)) { return await base.UpdateAsync(item).ConfigureAwait(false); } else { return await base.InsertAsync(item).ConfigureAwait(false); } } public Task<int> DeleteItemAsync<T>(int id) where T : IBusinessEntity, new() { return DeleteAsync(new T() { Id = id }); } #endregion
ãŸããåOSã®ãã¡ã€ã«ã·ã¹ãã ã«ã¢ã¯ã»ã¹ããããã®ã«ãŒã«ãç°ãªãããšãèŠããŠããå¿ èŠããããŸãã
ãããã£ãŠãããŒã¿ããŒã¹ãžã®ãã¹ã¯æ¬¡ã®ããã«ããŠååŸã§ããŸãã
public static string DatabaseFilePath { get { var sqliteFilename = "TalksDb.db3"; #if SILVERLIGHT // Windows Phone expects a local path, not absolute var path = sqliteFilename; #else #if __ANDROID__ // Just use whatever directory SpecialFolder.Personal returns string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); #else // we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms // (they don't want non-user-generated data in Documents) string documentsPath= Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); // Documents folder string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder #endif var path = Path.Combine(libraryPath, sqliteFilename); #endif return path; } }
ãã¡ã€ã«ã·ã¹ãã ã¢ã¯ã»ã¹
ã³ã³ãã¹ãã®ç®çã®1ã€ã¯ãåçãå ¥æããŠä¿åããããšã§ããã ãã®åé¡ã解決ããããã«ãããããã¯ãã¹ãã©ãããã©ãŒã ã¯ã©ã¹ã®ãã£ã¹ã¯ãã£ãã·ã¥ãååŸããŠå®æãããŸãã ã äžè¬ã«ããã¹ãŠã®OSã§ãã¡ã€ã«ãæäœããããã®èŠä»¶ã¯ç°ãªãããããã¡ã€ã«ã·ã¹ãã ã®æäœã¯æã移æ€æ§ã®äœãéšåã®1ã€ã§ãã éšåçã«ããã¡ã€ã«ã·ã¹ãã ã®æ©èœã¯å ¬åŒã®Xamarinããã¯ã§èª¬æãããŠããŸãã
Talks.Droid
ã¢ããªã±ãŒã·ã§ã³ã®AndroidããŒãžã§ã³ã çæ³çã«ã¯ãç¹å®ã®ãã©ãããã©ãŒã ã§ãããžã§ã¯ããäœæãããŸã§ã«ãå®å šã«æ©èœãããã¹ããããããã¯ãšã³ããååšããå¯èœæ§ããããŸãã ç§ã®ããŒãžã§ã³ã§ã¯ãããã¯ããŸããããŸããã§ããããå°æ¥çã«ã¯åªåããŸãã
äž»ãªå°é£ã¯ããããå§ãŸããŸãã
ã¢ããªã±ãŒã·ã§ã³ã¯ãAppã¯ã©ã¹ããã€ã³ããããŠãããã€ã³ãããããµãŒãã¹ïŒãã¢ããªã±ãŒã·ã§ã³ããå®è£ ããã·ã³ã°ã«ãã³ïŒã«åºã¥ããŠããŸãã ããã¯ãã¢ã¯ãã£ããã£ã
App.Current.MainService
ã䜿çšããŠãµãŒãã¹ã«ã¢ã¯ã»ã¹ã§ããããã«ããããã«è¡ãããŸãã
ãµãŒãã¹å ã§ã¯ãã¢ãã«ã¯å¥ã®ã¹ã¬ããã§äœæãããŸãããŸããã¢ã¯ãã£ããã£ããã¬ãŒã³ã¿ãŒãããã¯ã¢ããããã¯ã©ã¹ããããŸãã次ã®ãããªãã®ã§ãã
_presenter = App.Current.MainService.CreatePresenter<ChatListPresenter>(typeof(ChatListPresenter), this);
Xamarinã¯AndroidManifestãç¬ç«ããŠåœ¢æããçŽæ¥ç·šéããããšã¯ã§ããŸããã ãã¹ãŠã®ã¢ã¯ãã£ããã£ãã©ã¡ãŒã¿ã¯å±æ§ãšããŠèšé²ãããŸãã
[Activity(Label = "Settings", Theme = "@style/Theme.TalksTheme")] [MetaData("android.support.PARENT_ACTIVITY", Value = "talks.ChatListActivity")] public class SettingsActivity : SherlockActivity, IView
åºæ¬çã«ãã¢ã¯ãã£ããã£ã³ãŒãã¯JavaããŒãžã§ã³ã®CamelCaseãšããã»ã©å€ããããäžéšã®ã²ãã¿ãŒ/ã»ãã¿ãŒã¯ããããã£ã§ã©ãããããŸã
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); // Set our view from the "main" layout resource SetContentView(Resource.Layout.MessagesScreen); AndroidUtils.SetRobotoFont(this, (ViewGroup)Window.DecorView); _presenter = App.Current.MainService.CreatePresenter<MessagePresenter>(typeof(MessagePresenter), this); _presenter.PlatformSpecificImageResize = AndroidResizeImage; this.ChatId = Intent.GetIntExtra("userid", 0); userName = Intent.GetStringExtra("username"); _button = FindViewById<ImageButton>(Resource.Id.bSendMessage); _button.Click += button_Click; _button.Enabled = false; _message = FindViewById<EditText>(Resource.Id.etMessageToSend); _message.TextChanged += message_TextChanged; _lv = FindViewById<ListView>(Resource.Id.lvMessages); _lv.Adapter = new Adapters.MessagesScreenAdapter(this, this.Messages); }
ãã°ã€ã³ã¢ã¯ãã£ããã£
ãã°ã€ã³ã¢ã¯ãã£ããã£ã«ã¯ãé»è©±ã®å ¥åãã³ãŒãã®åä¿¡ãšå ¥åãç»é²ã®3ç¹ãå«ãŸããŠããå¿ èŠããããŸãã 䟿å®äžãããã¯ãã©ã°ã¡ã³ãã䜿çšããŠè¡ãããŸãã

ãããè¡ãæãç°¡åãªæ¹æ³ã¯ããã©ã°ã¡ã³ãã䜿çšããããšã§ãã ãã ãã ãã©ã°ã¡ã³ããšMVPã®äœ¿çšã¯å®å šã«èªæã§ã¯ãããŸããã
ãã®çµæã1ã€ã®ãã¬ãŒã³ã¿ãŒãäœæãããšããçµè«ã«éããLoginActivityã¯IViewãã©ã°ã¡ã³ãã®å®è£ ãã©ããããã ãã§ãã
PhoneFragment _pf = null; CodeFragment _cf = null; SignUpFragment _suf = null; public string PhoneNumber { get { if (_pf != null) { return _pf.Phone; } else { return ""; } } } public string AuthCode { get { return _cf.Code; } } public string Name { get { return _suf.FirstName; } } public string Surname { get { return _suf.Surname; } }
åç/ãããªã®æ®åœ±
èå³æ·±ãç¹ã¯æããã§ã¯ãããŸããã ã¿ã¹ã¯ã®1ã€ã¯ãã«ã¡ã©ããåç/ãããªãåä¿¡ããŠââä»ã®äººã«éä¿¡ããããã¢ãã¿ãŒãšããŠèšå®ãããããããšã§ããã
ããã¯ãXamarin.Mobileã䜿çšããŠã¡ãã¥ãŒããå®è¡ãããŸãã
public override bool OnMenuItemSelected(int featureId, Xamarin.ActionbarSherlockBinding.Views.IMenuItem item) { switch (item.ItemId) { // Respond to the action bar's Up/Home button case Android.Resource.Id.Home: NavUtils.NavigateUpFromSameTask(this); return true; case Resource.Id.messages_action_takephoto: _presenter.TakePhoto(this); return true; case Resource.Id.messages_action_gallery: _presenter.PickPhoto(this); return true; case Resource.Id.messages_action_video: _presenter.TakeVideo(this); return true; } return base.OnMenuItemSelected(featureId, item); }
ãã ããã¡ãã¥ãŒé ç®ãéžæããã€ãã³ãã¯ããŒã«å€ãè¿ããããasync-awaitã³ã³ã¹ãã©ã¯ããé©çšã§ããŸããã ããã¯éåžžã«ç°¡åã«è§£æ±ºãããŸããasync-awaitã¯ãæçµçã«åãContinuationãçæããåãªãæ§æäžã®ç ç³ã§ããããšã«æ³šæããŠãã ããã ãããŠã以åã®ããã«ãããæžãããšã劚ãããã®ã¯ãããŸããïŒ
#if __ANDROID__ /// <summary> /// /// </summary> /// <param name="context"></param> /// <returns></returns> public bool TakePhoto(Android.Content.Context context) { var picker = new MediaPicker(context); #else public bool TakePhoto() { var picker = new MediaPicker(); #endif if (picker.IsCameraAvailable) { picker.TakePhotoAsync(new StoreCameraMediaOptions { Name = String.Format("{0:dd_MM_yyyy_HH_mm}.jpg", DateTime.Now), Directory = "TalksPictures" }) .ContinueWith((prevTask) => { if (prevTask.IsCanceled) { _imv.SendSmallMessage("User canceled"); return; } if (PlatformSpecificImageResize != null) { string path = PlatformSpecificImageResize(prevTask.Result); // DomainModel.Message msg = new DomainModel.Message(r.Next(Int32.MaxValue), 0, _imv.ChatId, _imv.PendingMessage, "", 0); _imv.AddMineMessage(msg); } }) .ContinueWith((prevTask) => { if (!prevTask.IsCanceled) { Console.WriteLine("User ok"); } }, TaskScheduler.FromCurrentSynchronizationContext()); return true; } return false; }
Xamarinã¯ãã¯ãã¹ãã©ãããã©ãŒã ã«å ããŠãã³ã³ããŒãã³ãã¹ãã¢ãæäŸããŠããŸããããã«ã¯ã人æ°ã®Androidããã³/ãŸãã¯iOSã©ã€ãã©ãªãšã³ã³ããŒãã³ãã®ããŒãããããç¡æãšææã®äž¡æ¹ããããŸãã ç¹ã«ãActionBar.Scherlokãããã«ãããAndroid.Support.v7ãæè¿ç»å ŽããŸãããã³ã³ããŒãã³ãã¯NuGetã®ããã«ãç°å¢ããçŽæ¥ã€ã³ã¹ããŒã«ã§ããŸããããã¯éåžžã«äŸ¿å©ã§ãã

ãããã£ãŠã2åã¯ãªãã¯ããã ãã§ãAndroid 2.3以éãæèŒããããã€ã¹ã§ActionBarããµããŒãã§ããŸãã
転èš
ã¢ããªã±ãŒã·ã§ã³ã®å ¬éã¯ãæ¿èªãããGoogleã¹ããŒã ã«åŸã£ãŠå®è¡ãããŸã

ããã«ã¯ãããªãã®æ°ã®ã¢ã¯ã·ã§ã³ãå«ãŸããŸãã ããããç¹ã«ç§ãã¡ã«ãšã£ãŠãXamarinã®ããŒã ã¯ãVSã«çµã¿èŸŒãŸãããŠã£ã¶ãŒããäœæããŸãããããã«ãããæ°ã¹ãããã§å ¬éçšã®ã¢ããªã±ãŒã·ã§ã³ãæºåã§ããŸãã

ãããŠå®äº

確ãã«ããã®ãŠã£ã¶ãŒãã䜿çšããŠããŒã¹ãã¢ãããã«äœæã§ããŸããã§ããã ããŒã®å¯¿åœãé·ãã£ãã ãã³ãäœæããå¿ èŠããããŸããã
ãã¹ãäž
ãã¹ãã«é¢ããå°ããªã¡ã¢ã ãšãã¥ã¬ãŒã¿ã§ã®ãã¹ãã¯ã²ã©ãäžå¯èœã§ãã ããã¯ã§ããã ãæ©ãç Žæ£ããå¿ èŠããããŸãã æãå®ãã¢ã³ããã€ãã¯çŸåš3000ã«ãŒãã«ã§ãäžåœã®ã¿ãã¬ããã¯åæ§ã®äŸ¡æ Œã§èŠã€ããããšãã§ããŸãã 競äºã®å§ãã«ãç§ã¯ããã«åŠ»FlyãAndroid 4.0.1ã§è³Œå ¥ããŸããã 2.3ã®å€ãHTCãããããŸããã§ããã
iOSã®ãã¹ããšéçºã«é¢ããŠã¯ãããã¯ããå°é£ã§ãã ãã¡ãããæè¯ã®ãªãã·ã§ã³ã¯ãæãå®ãmacbookãåãããšã§ããããã§ååã§ãã
ãããããã¹ãçšã«iPhoneãšiPADã®ãã¢ãè³Œå ¥ããããšã¯...ããããŸããããæè¯ã®éžæè¢ã§ã¯ãããŸããã çŸåšã MacInCloudã®å¯èœæ§ãæ€èšããŠããŸãããã¹ãŠãããŸãããã°ãããã»ã¹å šäœã詳现ã«èª¬æããŸãã
ãŸãšã
èŠçŽããã®ã¯é£ããã§ãã éçºããã»ã¹äžã«ãAndroidãã©ãããã©ãŒã ã®æ©èœãããç 究ãã
åªããããã¹ãã§ã«ããŒãããããããŠæãéèŠãªã¯ãã¹ãã©ãããã©ãŒã ããã¯ãšã³ããéçºããŸããã
圌ãã¯ãä»åŸã¯WinPhoneãšiPadã®ç«¶äºã«ãªããšèšã£ãŠããŸãã ãŸããç§ã¯ã€ã³ã¿ãŒãã§ã€ã¹ã®ã¿ãæãããšãã§ããŸãã
ãšã©ãŒåŠç
圌ããèšãããã«ãèªå·±ãžã®æ³šæãã ç§ãééã£ãããšãããããšã®æªæ¥ã«ã€ããŠã³ã¡ã³ãããŠãã ããã
1.ãã¶ã€ã³ã®æ¬ åŠã MTProto.Coreãã»ãŒ2åãªãã¡ã¯ã¿ãªã³ã°ããŸããã ãã®çç±ã¯ãç§ãäžæã®çŽã«è °ãäžãããããã®ã³ã¢ãã©ã®ããã«èŠããã¹ãããå®å šã«æããŠããªãããã§ãã å€ãã®æ±ºå®ãèªç¶ã«ããããŠå°æ¥ãèæ ®ããã«è¡ãããŸããã
2. Androidãã©ãããã©ãŒã ã®äžååãªç解ã é·ãéãAndroidãµãŒãã¹ãšã®ããåããæŽçããæ¹æ³ãç解ããããšããŸããã ççŽã«èšã£ãŠãç§ã¯ãŸã ãã®çžäºäœçšã確å®ã«ããæè¯ã®æ¹æ³ãç¥ããŸããã d.android.comã¬ã€ãã¯ããã§ã¯åœ¹ã«ç«ããªãããšãã¢ã³ããã€ãã®ãµãŒãã¹ã¯ç¹å®ã®ãã®ã§ããããšãç解ããå¿ èŠããããŸããããã©ãããã©ãŒã ãåãé€ããã¯ãã¹ãã©ãããã©ãŒã ã§äœããããå¿ èŠããããŸãã
3.é åºããšæ¬²ã ç§ã¯å¥ã®ããã°ã©ããŒãæ¹ãã€ããæ©äŒããããããããäžç·ã«ãªã£ãŠããè¯ãçµæã瀺ããã§ãããã ããããç§èªèº«ããã¹ãŠèªåã§ã