読者です 読者をやめる 読者になる 読者になる

ぴよぴよエンジニアの日記

クラウドベンダーに勤める見習いSEの日記です。発言は私自身の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

MvvmCrossが画面遷移回りでParcelableをどうしているのかのぞいてみた

JXUGC #13の記事に@amay077さんからコメントを頂いたのでそれに関して MvvmCross を少しのぞいてみました.

santea.hateblo.jp 以下いただいたコメントです.

Parcelable を JSON 文字列で代替する手法は今回の要件では問題になりませんでしたが、一般的なアプリ開発では好ましくないと思われるでしょう。その点はご注意ください。MvvmCross はこの問題をうまくクリアしているようなので、コードを読まれると良いかもしれません。

MvvmCross は Parcelable を上手くやってるようです.



MvvmCrossの画面遷移

www.buildinsider.net

Build INSIDER の MvvmCrossの画面遷移に関する記事です.

どうやら中身の実装は MvxAndroidViewPresenter クラスにあるようです.

public virtual void Show(MvxViewModelRequest request)
{
    var intent = CreateIntentForRequest(request);
    Show(intent);
}
protected virtual void Show(Intent intent)
{
    var activity = Activity;
    if (activity == null)
    {
        MvxTrace.Warning("Cannot Resolve current top activity");
        return;
    }
    activity.StartActivity(intent);
}
protected virtual Intent CreateIntentForRequest(MvxViewModelRequest request)
{
    var requestTranslator = Mvx.Resolve<IMvxAndroidViewModelRequestTranslator>();
    var intent = requestTranslator.GetIntentFor(request);
    return intent;
}

MvxViewModelRequest から Intent を生成している流れが今回の話題のコアっぽいです.

IMvxAndroidViewModelRequestTranslator の実装部である MvxAndroidViewsContainer について更に見ていきます.

public virtual Intent GetIntentFor(MvxViewModelRequest request)
{
    var viewType = GetViewType(request.ViewModelType);
    if (viewType == null)
    {
        throw new MvxException("View Type not found for " + request.ViewModelType);
    }

    var converter = Mvx.Resolve<IMvxNavigationSerializer>();
    var requestText = converter.Serializer.SerializeObject(request);

    var intent = new Intent(this._applicationContext, viewType);
    intent.PutExtra(ExtrasKey, requestText);

    this.AdjustIntentForPresentation(intent, request);

    return intent;
}

MvxViewsContainer の #GetViewType です.

public Type GetViewType(Type viewModelType)
{
    Type binding;
    if (this._bindingMap.TryGetValue(viewModelType, out binding))
    {
        return binding;
    }

    foreach (var viewFinder in this._secondaryViewFinders)
    {
        binding = viewFinder.GetViewType(viewModelType);
        if (binding != null)
        {
            return binding;
        }
    }

    if (this._lastResortViewFinder != null)
    {
        binding = this._lastResortViewFinder.GetViewType(viewModelType);
        if (binding != null)
        {
            return binding;
        }
     }

     throw new KeyNotFoundException("Could not find view for " + viewModelType);
}

Bindingまわりをどうこうしてるくらいの解釈でいいはず...
問題の箇所は以下の部分ですね.

var converter = Mvx.Resolve<IMvxNavigationSerializer>();
var requestText = converter.Serializer.SerializeObject(request);

IMvxNavigationSerializer とそのメンバ変数である IMvxTextSerializer を更に見ていきます.

namespace MvvmCross.Core.ViewModels
{
    using MvvmCross.Platform.Platform;

    public interface IMvxNavigationSerializer
    {
        IMvxTextSerializer Serializer { get; }
    }
}
namespace MvvmCross.Platform.Platform
{
    using System;

    public interface IMvxTextSerializer
    {
        T DeserializeObject<T>(string inputText);

        string SerializeObject(object toSerialise);

        object DeserializeObject(Type type, string inputText);
    }
}

IMvxTextSerializer の実装部である MvxViewModelRequestCustomTextSerializerメソッドが以下になります.

public string SerializeObject(object toSerialise)
{
    if (toSerialise is MvxViewModelRequest)
        return this.Serialize((MvxViewModelRequest)toSerialise);

    if (toSerialise is IDictionary<string, string>)
        return this.Serialize((IDictionary<string, string>)toSerialise);

    throw new MvxException("This serializer only knows about MvxViewModelRequest and IDictionary<string,string>");
}
protected virtual string Serialize(MvxViewModelRequest toSerialise)
{
    var stringDictionaryWriter = new MvxStringDictionaryWriter();

    var dictionary = new Dictionary<string, string>();
    dictionary["Type"] = this.SerializeViewModelName(toSerialise.ViewModelType);
    var requestedBy = toSerialise.RequestedBy ?? new MvxRequestedBy();
    dictionary["By"] = ((int)requestedBy.Type).ToString();
    dictionary["Info"] = requestedBy.AdditionalInfo;
    dictionary["Params"] = stringDictionaryWriter.Write(toSerialise.ParameterValues);
    dictionary["Pres"] = stringDictionaryWriter.Write(toSerialise.PresentationValues);
    return stringDictionaryWriter.Write(dictionary);
}

返り値を実際に返している MvxStringDictionaryWriterメソッドが以下になります.

public string Write(IDictionary<string, string> dictionary)
{
    if (dictionary == null
        || dictionary.Count == 0)
    {
        return string.Empty;
    }

    var output = new StringBuilder();
    foreach (var kvp in dictionary)
    {
        if (output.Length > 0)
            output.Append(";");

        output.AppendFormat("{0}={1}", this.Quote(kvp.Key), this.Quote(kvp.Value));
    }
    return output.ToString();
}

基本的に String で返していますね.

Key-Value の構造も特殊なわけでもなさそうなので、工夫のポイントは MvxViewModelRequest にあるのかな?


一通り流れを追ったところで今回はここまでにします.

次回に続きます(たぶん).