vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

iPhoneでAndroid Wearと連携してみたメモ

はじめに

いきなりですが、メイン端末を iPhone にしました。

とはいってもまだ新しいものを買ったわけではなく、以前使っていた iPhone5s です。

iPhone にしようかなと思った理由は Google 先生が日本で Pixel2 が出てくれないし・・・というのもありますが、
iPhoneX で Prime sense の技術がとかいう話が、今更ながら気になってきたためです。

ただ、今まで Android 端末で過ごしてきただけに、
急に切り替えるとあれもできないこれもできない/(^o^)\とならないかと心配になったので、
とりあえず iPhone5s で試してみることにしました。

で、特に気になっていた Android Wear (Zenwatch2) が使えるのか問題を試してみたので、メモっておきます。

Zenwatch2との連携

Zenwatch2 には限りませんが、 Android Wear との連携は Android Wear アプリを使うことで可能です。

ただ、Android でやるように、ただ Bluetooth を On にすれば良いのではなく、
アプリを立ち上げて明示的に連携してやる必要があります。

Appleのアプリとの連携

それではまず iPhone に標準で入っているアプリとの連携について。

電話

電話がかかってきたことが表示され、受ける・または切ることができます。

SMS

メッセージをみることはできますが、返信をすることはできません。

Music、Podcast

曲名が表示され、再生・停止・スキップを行うことができます。

Clock

アラームが鳴っても通知されず、止めることもできません。

他のアプリとの連携

基本的にプッシュ通知は受けることができ、内容を見ることができます。

Hangout

テキストメッセージを見ることはできますが、画像のプレビューはできません。

Google Home(というか OK google)

反応しませんでした。

これができれば iOS アプリの Cortana と合わせて混とんとした感じになって面白かったのですが。。。
(どんな願望や。。)

Google Fit、Zenfit

iOS アプリがないので無理。。。かと思いきや、
実はこれらのアプリは Watch 単体で動作するため記録を取ることができます。
(GPS 情報は iPhone から取得)

iPhone は標準でトラッキングアプリが入っていないようだったのでどうしようかなと思っていたのですが、
これは助かりました。

おわりに

というわけで、絶望的かなと思われた Android Wear との連携ですが、
特に Android Wear の OS が 2.0 になったこともあって結構問題ありませんでした。

ただ他はともかくアラームが Watch でコントロールできないのは、
二度寝防止に朝大量にセットしている私としては結構痛い。。。

ま、今すぐ無いと困るってわけでもないので、 Xamarin の学習がてら自作するのも良いかもなぁ~などと思ってもいます。

あと、4年も前の端末が曲がりなりにもちゃんと使えるってのはすごいですねぇ。 開発者としてはさっさと替えて!って気もしなくもないですが苦笑。

コードを書く時間を確保したい話

はじめに

この記事は 子育てエンジニア・クリエイター Advent Calendar 2017 の11日目の記事です。

adventar.org

このブログに書き残している通り、プライベートの時間を使ってあれこれコードを書いてみたり試してみています。

が、子どもが生まれ、大きくなってくるにつれて時間を確保するのが難しくなってきました。
遊んで~と言ってくることももちろんですが、パソコン自体にも興味があるようで、 特に電源がついている状態だとキーボードを触りまくったりラップトップをパカパカ開いたり閉じたりし始めます/(^o^)\

そんな中で私がどのように時間確保しているかを書き残しておきます。

本当は最新のサービスとか、すごい技術とかで時間短縮してます!とかの方が良いのでしょうが、
いたって普通の方法に終止しています。期待した人残念でした。

子どもを寝かしつけたあとにする

前まではこの方法でした。

子どもが寝た後ならゆっくり時間もとれるし( ´∀`)bグッ!

と思っていたのですが、高確率で寝落ちしちゃうんですよね。。。orz

また、寝たばかりだとまだ眠りが浅く、結局起きてしまうことも。。。

ということで、(たまにはやりますが)基本的には夜遅くにやる、というのは諦めました。

早起きする

じゃあ逆に早起きすりゃいいんじゃね?というのが現在の方法です。

夜は子どもと一緒にさっさと寝る。

で、朝5時ごろに起きて、洗濯物を洗ったり出発の準備をしつつコードを書く、と。

最近は布団のやつがなかなか私を放してくれないので、6時ごろになってきてしまっていますが。。。

これだと出発の時間が決まっているので、ある程度集中してコードを書くことができる(気がする)
というメリットもあります。

大学卒業くらいまでは夜中まで起きてて昼まで寝てるみたいな生活していたのに、
変わるものだなぁ、と大学生当時の自分が見たら思うかもしれませんね。

その他

その他便利だなぁ~と思っているものとしては、電子書籍ですね。
それもスマホで見られるもの。

どうしても子どもと出かけるとなれば荷物は多いわけで、
そこに大きな本を追加するのはツラい。。。

紙の本の方が見やすかったりするメリットもわかるのですが。

電子書籍アプリの中でのお気に入りとしては、 Google Play Books です。

特に良いところが2つあって、1つ目が英語などを翻訳するとき、
単語だけではなく2,3単語組み合わせた熟語も翻訳ができることです。

そういうのは翻訳アプリに任せる、というのも手ですが、行ったり来たりしなくて済むならそちらの方が助かるので。

2つ目が、クレジットカードなどを登録しなくてもマルチプラットフォームに対応してくれているところです。
外ではスマホで良くても、家などではもっと大きな画面で見たい。

Google Play Books だと Web ブラウザでも見られるので助かります。

以上ステマでしたw

あとは勉強会に行く時間がとりづらいというところで、
Podcast や Channel 9 などの動画配信にも助けられています。

日本の勉強会でも、ストリーミング配信していたり、
VR 空間上や Twitter 上で行われるものがあったりしてすごいです。

直接会ったり聴いたりできる勉強会の良さはもちろんあるのですが、
地方在住だったり自由な時間が取りづらかったりする身としては大変助かります。

あと時間確保の上記以外の方法としては、
仕事帰りにもくもくする、というのがあります。

和歌山にも少ないながらもカフェがあります。

幸か不幸か残業代は見込み時間で支払われ、残業自体も多いので1時間くらいなら妻にバレることもありません(白目)

。。。嫌なオチになってしまった(´・ω・`)

おわりに

子どもがもう少し大きくなったら、あまり私とも遊んではくれなくなるんだろうなぁ、
と思うと今一緒に遊べる時間を大事にしたい。

とは思いつつ、やっぱり自分自身の興味に任せてあれこれ試したり学んだりする時間も欲しい。

ということで、両者を上手くバランス取れるようになれたらなぁ、と思っています。

妻との今年、そして来年の話

はじめに

この記事は 妻・夫を #愛してる ITエンジニア Advent Calendar 2017 - ADVENTAR の四日目の記事です。

adventar.org

妻とのこれまでや日常生活については去年も書いたので、これまでの話はしません。

mslgt.hatenablog.com

ここ一年から来年に向けての話をだらだらと書いてみたいと思います。

子どもとの生活

やっぱり良くも悪くも子どもが生活の中心になっちゃいますね。

休日など、どこへ行くにも3人。

もしくはたまには一人でゆっくり過ごしてねってことで、私と子どもの2人でとか。

私自身はそれでも良いかな、と思うのですが、妻としては私と2人でたまには、
とか思ってたりしないかな?と少し不安には思っています。

まぁもう少し子どもが大きくなってきたらそういう時間もとれるかな?とは思っているのですが。

正直子どもがわんぱくになってきたこともあり、
恋人同士というよりは戦友、といった意識になってきているところもありますw

恋愛感情云々はともかく、お互いを助け合って問題を乗り越えていく、
という点では共通項もあるのかなと。

おわりに

もう少しあれこれ書こうかな?と思っていたはずなのですが、
実際に書き始めるとあまり出てこなかったのでいったん締めます。
(後日書き足したりするかも)

最後に妻へ。

いつも帰りが遅くなってしまったりして、
大変な仕事を任せきりにしてしまってごめん。

来年は家族がもう一人増えてさらに大変になると思うけど、
僕もできるだけ時間を取るようにするので、一緒に頑張りましょう。

愛してます。

Xamarin.FormsとMVVMに触れてみたい話

はじめに

この記事は [初心者さん・学生さん大歓迎!] Xamarin その1 Advent Calendar 2017 - Qiita の四日目の記事です。

qiita.com

前回に引き続き Xamarin.Forms に触ってみたお話ですが、今回は MVVM にも入門してみた話を中心にまとめます。

MVVMについて

Xamarin について調べると必ずと言っていいほど登場する MVVM について。

詳しくは参照サイトを見ていただくとして超おおざっぱにまとめると、
プログラムを下記の3つに分けて設計しましょう、という話ですね。

  1. View: 画面に表示する部分を担当する。ボタンクリックなどのイベントを検知して ViewModel に伝える。
  2. ViewModel: 1.View と、3.Model とをつなぎ合わせる。通常 1.View と一対の関係となる。
  3. Model: 計算処理などを行う。処理の内容によってクラス数は増減する。

  4. 1.View -> 2.ViewModel -> 3.Model の順に呼び出され、矢印の先にあるクラスのことを知ることはできるが、
    逆はできない(例: 1.View は 2.ViewModel を知っていて呼び出すことができるが、逆は×)

なるほどね。完全に理解した(わかってない)。という感じなのですが、特に気になったことが2つありました。

Viewをコントロールするものは誰か

実はこの MVVM を Unity でも試してみたのですが、その時に迷ったのがコレ。

  1. View: uGUI の Canvas
  2. ViewModel: ViewModel の機能を担当するクラス
  3. Model: Model の機能を担当するクラス

とすると、誰が 1.View の表示・非表示を切り替えるの?また、ページを表示した直後の処理って誰がするの?という話です。

よくよく見てみると、 Xamarin.Forms で View を担当するのは、 (例えば) MainPage.xaml だけではなく、
MainPage.xaml.cs がいます。

Android でみると Activity ですね。

またページ遷移を担うのはまた別のクラス(例えば NavigationPage に関連するクラス)です。

これらをすべて View としてまとめてしまって良いのか疑問はありますが、
CanvasXaml の表示・非表示やページを開いた時の処理を実行するクラスは別にある、ということですね。

状態は誰が持つのか

表示だけを担う View はともかく、 ViewModel と Model の内、誰が状態を持つのか、
というのが2つ目の疑問でした。

結論としては、 View に関連する状態は ViewModel が、 Model の各処理に関連する状態はそれぞれのクラスが持つ、
ということのようです。

考えてみれば処理の中心となるクラスが情報を持ち、それ以外のクラスへは必要な情報だけを渡す、
というシンプルな考え方と言えそうです。

INotifyPropertyChangedによる通知

さて、 2.ViewModel は 1.View を知ることはできない、と書きましたが、
例えば クリックイベント発火 -> 処理 -> 完了後に表示を切り替え としたい場合は、
View に処理が完了したことを伝えたくなります。

View で Obervable を使うなどなど方法は色々ありますが、
INotifyPropertyChanged を使う方法も便利だと思いました。

呼ぶ側 (View)

private SubPageOneViewModel viewModel;

public partial class SubPageOne : ContentPage
{
    viewModel = new SubPageOneViewModel();

    // ViewModelからの通知を購読.
    viewModel.PropertyChanged += (_, e) => {
        // 処理完了後に何かする.
    };
}
publci void OnDoSomethingButtonClicked(object sender, EventArgs e)
{
    viewModel.DoSomething();
}

呼ばれる側 (ViewModel)

private string calcResult;
public string CalcResult
{
    get => calcResult;
    set
    {
        // 値をセットするときに購読者に通知を送る.
        calcResult = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CalcResult)));
    }
}
public void DoSomething(){
    // 何か計算処理.

    // CalcResult の set が実行される.
    CalcResult = "計算結果";
}
  • 上記では CalcResult に全く同じ値を代入した場合も通知が送られるため、
    set で必要に応じて不要な通知が送られないようにします。
  • ViewModel 側では プロパティではなく変数 calcResult に値を入れることもできますが、
    その場合通知が送られないので注意が必要です。

DataBindingってみる

MVVM によるクラス同士の疎結合化を助けてくれる機能の一つに DataBinding があります。

xaml.cs クラスでボタンなどのインスタンスを持つことなく、
Model で変更された値をそのまま View (xaml) に反映したり、クリックなどのイベントを直接取得することができます。

SubPageOne.xaml

< ?xml version="1.0" encoding="utf-8" ?>
< ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinSample.View.SubPageOne">
    < ContentPage.Content>
        < StackLayout BackgroundColor="#0078d7">
            < Label Text="{Binding CalcResult}" />
            < Button BackgroundColor="Yellow" Command="{Binding CalcCommand}">< /Button>
        < /StackLayout>
    < /ContentPage.Content>
< /ContentPage>
  • クリックイベントとして、 Clicked= に DataBinding を設定することはできず、
    ICommand を使ってコマンドとして登録する必要があります(コマンドについては後述)。

SubPageOne.xaml.cs

using Xamarin.Forms;
using XamarinSample.ViewModel;

namespace XamarinSample.View
{
    public partial class SubPageOne : ContentPage
    {
        public SubPageOne()
        {
            InitializeComponent();
            // ViewModel クラスを DataBinding として設定.
            BindingContext = new SubPageOneViewModel();
        }
    }
}

と、ここまでは良かったのですが。。。

失敗

最初、 DataBinding する値を下記のように書いていました。

SubPageOneViewModel.cs

using System.Windows.Input;
using Xamarin.Forms;

namespace XamarinSample.ViewModel
{
    public class SubPageOneViewModel
    {
        public ICommand CalcCommand { get; private set; }
        public string CalcResult{ get; set; }
        
        public SubPageOneViewModel()
        {
            CalcResult = "Starts";
            // ボタンに Binding しているコマンド.
            CalcCommand = new Command(() =>
            {
                var subtraction = DependencyService.Get();
                CalcResult = subtraction.Calc(0, 1).ToString();
            });
            CalcResult = "Start2";
        }
    }
}

実行してみるとエラーは発生せず、ラベルには「Start2」と表示され、
ボタンを押すと CalcCommand の中の処理が実行されます。

しかし、なぜか CalcCommand の中にある「CalcResult = subtraction.Calc(0, 1).ToString();」がラベルに反映されないorz..
(なお「CalcResult = "Start2";」は実行されているのもよくわからず。。。)

Android などではイベントが別スレッドで発火するため、
下記のようにメインスレッドで実行してみては...?と思いましたが、うまくいかず。

// これでも反映されず.
CalcCommand = new Command(() =>
{
    Device.BeginInvokeOnMainThread(() =>
    {
        var subtraction = DependencyService.Get();
        CalcResult = subtraction.Calc(0, 1).ToString();
    });    
});

実は、 DataBinding で変更された値を View に反映するためには、
INotifyPropertyChanged で変更を通知する必要があるのでした。

SubPageOneViewModel.cs

using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace XamarinSample.ViewModel
{
    public class SubPageOneViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public ICommand CalcCommand { get; private set; }

        private string calcResult;

        public string CalcResult
        {
            get => calcResult;
            set
            {
                calcResult = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CalcResult)));
            }
        }

        public SubPageOneViewModel()
        {
            CalcResult = "Starts";

            CalcCommand = new Command(() =>
            {   
                var subtraction = DependencyService.Get();
                CalcResult = subtraction.Calc(0, 1).ToString();
            });

            CalcResult = "Start2";
        }
    }
}

また、 PropertyChanged を実行するときの引数「PropertyChangedEventArgs(nameof(CalcResult))」は Binding しているプロパティ名を渡す必要があり、
例えばローカル変数である「PropertyChangedEventArgs(nameof(calcResult))」などでは反映されないようなので注意が必要です。

DataBinding を使うことで、 xaml.cs クラスからほとんど処理を省くことができ、
表示する部分と処理を実行する部分とがより簡単に分離できるようになりました。

おわりに

まだ慣れないせいか、 xamlxaml.cs と実行される処理が(自分で書いたコード上では)切り離されているのは、
どこかふわふわして不安な感じもあります。

ただ、だからこそ仕様の変更に強いコードになる、ということだとは思うので、
ゆっくり内容を理解していきつつ、使いどころや効果的な使い方を模索していきたいと思います。

参照

Xamarin

MVVM

DataBinding

【C#】処理の委譲で迷った話

はじめに

この記事は C# Advent Calendar 2017 の一日目の記事です。

qiita.com

「継承より委譲を」という言葉を、Java開発者の方々を中心に(と思う)よく目にします。

  • クラスの継承をすると、親クラスの変更に子クラスが大きな影響を受けるので変更がしづらくなる
  • 委譲の場合、外部から呼び出しできるメソッド・そうでないメソッドの区別をつけやすい
  • 一般的には子クラスが親クラスの特別な種類である場合(is-a関係)に継承し、
    親クラスが子クラスを含んでいる(例えば子クラスが持つ機能を親クラスが持っている状態。 has-a関係)場合は委譲を用いる

といったところがその理由のようです。

OK。じゃあ処理を委譲しましょう。

と思ったのですが、どのように設計すれば委譲したことになるの?というところで迷ったので、
その辺りをまとめます。

delegateについて

C# 委譲」といったキーワードで検索すると、 delegate についての記事が見つかります。

delegate はごく簡単にまとめるとメソッドを引数として渡せるものです。

// 引数、戻り値が同じメソッドを変数として扱うことができる.
public delegate void CallNoArgMethod();

public class MainController
{
    public MainController()
    {
        var noArgMethod1 = new CallNoArgMethod(NoArgMethod);
        var noArgMethod2 = new CallNoArgMethod(StaticNoArgMethod);

        // NoArgMethod()が実行される.
        noArgMethod1();
        // StaticNoArgMethod()が実行される.
        noArgMethod2();

        // delegateメソッドをひとまとめにすることもできる.
        var mixedMethod = noArgMethod1;
        mixedMethod += noArgMethod2;

        // NoArgMethod()とStaticNoArgMethod()が実行される.
        mixedMethod();

        // delegateを自分で定義せずに Action 、Func (戻り値あり)を使うこともできる.
        var action = new Action(NoArgMethod);
        // 戻り値の型を指定する必要がある.
        var funcWuLongTea = new Func(CallHasReturnValueMethod);
    }
    // delegateを作るメソッド
    public void NoArgMethod()
    {
        Debug.WriteLine("世界さん、ちーっす");
    }
    // staticメソッドでも同じように扱うことができる.
    public static void StaticNoArgMethod()
    {
        Debug.WriteLine("staticだよ");
    }
    public string CallHasReturnValueMethod()
    {
        return "Hello";
    }
}

一般的には処理が完了したあと呼び出し元に通知するためのコールバック、ボタン押下などのイベント、
Linqなどで用いられる無名関数(通常ラムダ式が使われますが)などで使用されます。

var nums = new List {1, 2, 3, 4, 5};
// delegateを使って書く.
var evenDelegate = nums.Select(delegate(int num) { return num % 2 == 0; });
// ラムダ式を使って書く.
var evenLambda = nums.Select(num => num % 2 == 0);

で、これを使ってどうやって委譲するんです?というか、処理を委譲するのにdelegateを使う必要ってあるんです?

はい。ここで迷いました...orz

前述の通り、委譲というのは(例えば)そのクラスが持つ機能を別のクラスに分割する(委譲する)ことらしい。

ただ、これって別に delegate を使う必要はなくて、
処理を別クラスに分けて、メソッドを呼び出してあげれば良いのでは......?

と思ったのですが、あまりに自明すぎるのか、誰も気にしていないのか、この辺りに言及する資料は見つけられませんでした
(日本語で検索した限りでは)。

元の言葉を調べてみよう

ところで、Java界隈で継承と委譲の話が良く出てくる理由に、 Effective Java があるようです。

では、英語でも検索するにあたって、英語版の Effective Java ではどう表現されているかを見てみることにしました。

「Favor composition over inheritance」

ふむふむ。「inheritance」が「継承」なので、委譲にあたる言葉は「composition」のはずですね。

「composition」の意味を調べると「組み立て、合成」など(compositionの意味 - 英和辞典 Weblio辞書)と出てきます。

ほうほう。

......。

「継承より委譲を」の時の「委譲」って「委譲」じゃないんじゃないですか???

......少々取り乱しました。

結局、やるべきことは一つのクラスに対象物が持っている性質や機能をひとまとめにするのではなく、
複数クラスに切り分けて親となるクラスから呼び出すようにしましょう、ということで、
その方法についてはメソッド呼び出しでも delegate でも良い、という理解で良さそうです。

delegateで遊んでみる

せっかくなのでここからは delegate を使ってもう少し遊んでみることにします。

コールバック

何かの処理を行った後、それが完了したことを呼び出し元に伝えてほしい場合があります。
delegateを使うことで、呼び出された側が呼び出し元のクラスインスタンスを持たなくても通知できるようになります。

public void StartControlling()
{
    ExecuteSomeOperationWithCallback(() =>
    {
        // 完了後の処理.
    });
}
private void ExecuteSomeOperationWithCallback(Action callback)
{
    // 何か処理して終わったらdelegateメソッド呼び出し..
    callback();
}

呼び出せる処理を限定する

delegate でメソッドを変数として扱うには、クラスのインスタンスが必要になります。
ただ、クラスのインスタンス自身は、delegate メソッドの変数(メンバー変数として作成)を生成するときにローカル変数として生成しても、
その後で delegate メソッドがGCで空になる、といったことは無いようです。

そのため、呼び出し元のクラスで呼び出すメソッドを限定することができます。

public class MainController
{
    private CallStringArgMethod callMethod;
    public MainController()
    {
        var delegateMethodManager = new DelegateMethodManager();
        callMethod = new CallStringArgMethod(delegateMethodManager.PrintMessage);
    }
    public void StartControlling()
    {
        // このクラスで呼べるのはPrintMessageだけ.
        callMethod("Hello");
    }
}
public delegate void CallStringArgMethod(string message);
public class DelegateMethodManager
{
    public void NoArgMethod()
    {
        Debug.WriteLine("Hello No Arg");
    }
    public void PrintMessage(string message)
    {
        Debug.WriteLine(message);
    }
}

その他

例えばUnityで3Dモデルを動かす、という処理を複数クラスで共通化させた場合。
そのままの名前で呼び出すのではなく、 moveCube 、 carModel など対象物に特化させた名前を付けると、
意図がよりわかりやすくなるかもしれません。

また、メソッド自体は private にしておきたいが、特定の条件でのみ操作したい、といった場合、
intなどの変数でやるように Getter / Setter を使うことができます。

おわりに

一般的に浸透している(と思う)言葉でも、語源に近い言語で調べてみるの大事ですね(苦笑)。

まぁそれはそれとして、 delegate もコールバック関数やLinqで使う以外にも、
使い方によってはなかなか便利そうです。

もちろん乱用は厳禁ですが(;'∀')

明日は neuecc さんです。よろしくお願いいたします(..)_

参照

Xamarin.Formsに触れてみた話

はじめに

この記事は [初心者さん・学生さん大歓迎!] Xamarin その1 Advent Calendar 2017 - Qiita の一日目の記事です。

qiita.com

なんとなく気になりつつも触れていなかった Xamarin に触れてみた、という内容です。

なお Xamarin には AndroidiOS の薄いラッパーである Xamarin.Android / Xamarin.iOS もありますが、
今回は Xamarin.Forms を試してみることにしました。

理由としては、同じように C# で書けてマルチプラットフォームに対応する Unity との使い分けができると良いな、と思ったためです。

Xamarinプログラミング入門をベースに自分の興味に任せて試した内容をまとめてみたいと思います。

準備

まずは準備から。 Visual Studio は 2017.15.4.4 Community Edition を使っています。

インストールは Visual Studio 本体のインストール時か、メニューの ツール > ツールと機能を取得 から、
.Netによるモバイル開発 にチェックを入れると可能です。

簡単ですね。

Xamarin Live Playerが有効にならない

Xamarin には Xamarin Live Player という機能があり、
AndroidiPhone に同名のアプリをインストールして Visual Studio とリンクしておくと、
同一ネットワークにつながっている場合はUSBケーブルでPCに接続しなくても実機でのデバッグを行うことができます。
(iPhoneMac が必要)

使い方は例えば Android なら、プロジェクトを Android に切り替えて、
バイスを選択するところから Xamarin Live Player を選択・・・

できませんでした/(^o^)\

Visual Studio のバージョンは問題ないし、 Xamarin も最新といっているし・・・

と思っていたら、公式サイトに載っていました。

どうやらデフォルトでは有効になっていないらしく、
ツール > オプション > Xamarin > その他 から、 Xamarin Live Playerを有効にする にチェックを入れる必要があるそうです。

f:id:mslGt:20171130233638j:plain

f:id:mslGt:20171130233822j:plain

ちゃんと説明は読みましょう、というお話でしたorz

なお有効にしたあと、テザリング環境でも試してみましたが、
Androidについてはテザリング環境でも問題なく接続できました。

確認したい端末が複数台ある場合など、結構便利なのではないでしょうか。

デフォルトのプロジェクトを見てみる

テンプレートを Blank App 、 Code Sharing Strategy を PCL にして Xamarin プロジェクトを作成すると、
以下の4つのプロジェクトを持つソリューションが生成されます。
(プロジェクト名は XamarinSample としました)

  • XamarinSample (移植可能)
  • XamarinSample.Android
  • XamarinSample.iOS
  • XamarinSample.UWP

View は XamarinSample (移植可能) のものを使います。

が、ほかのプロジェクトを見てみると、それぞれ MainPage.xaml(UWP) や MainActivity.cs(Android) など、
スタートアップやメインページの表示に関わりそうなファイルが見つかります。

とりわけ気になったのが UWP 。
MainPage.xaml と App.xaml って、 XamarinSample (移植可能) と一緒じゃないのという。

試しに UWP の MainPage.xaml にボタンなどを追加してみましたが、
少なくともデフォルトでは反映されませんでした。

また、 MainPage.xaml を削除するとエラーが発生しました。

基本的に各プロジェクトのコードはスタートアップのために存在するもののようで、
UWP については MainPage.xaml.cs や App.xaml.cs のコードビハインドが必要なので空の Xaml ファイルがある、
ということのようです。

xamlxaml.csについて

XamarinSample (移植可能) の MainPage.xaml と MainPage.xaml.cs ですが、
デフォルトではプロジェクト直下にあります。

これを View というディレクトリを作ってその中に移動し、
MainPage.xaml.cs の namespace を合わせて XamarinSample.View のように変更してやると、エラーになります。

これは MainPage.xaml や App.xaml.cs で MainPageクラスを呼んでいるためで、
合わせて変更してあげる必要があります。

MainPage.xaml

< ?xml version="1.0" encoding="utf-8" ?>
< ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinSample.View.MainPage"> 
~省略~
< /ContentPage>

App.xaml.cs

~省略~
public App()
{
    InitializeComponent();
    MainPage = new XamarinSample.View.MainPage(); 
}
~省略~

イベントとナビゲーションの追加

それでは、画面にボタンを追加して別のページに遷移する、というのを試してみたいと思います。

まずは SubPageOne.xaml というページを追加しておきます。

ナビゲーション

Xamarin.Forms では、 NavigationPage を使用することで、
比較的簡単にページ遷移が実装できるようになります。

NavigationPage を使用するためには、
XamarinSample (移植可能) の App.xaml.cs を変更する必要があります。

Before

~省略~
public App()
{
    InitializeComponent();
    MainPage = new XamarinSample.View.MainPage();
}
~省略~

After

~省略~
public App()
{
    InitializeComponent();
    MainPage = new NavigationPage(new XamarinSample.View.MainPage());
}
~省略~

あとは下記のように Navigation.PushAsync を使えばOKです。 (なお遷移後のページには戻るボタンが自動で表示されます)

MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XamarinSample.View
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
        async void OpenSubPageOneButtonClicked(object sender, EventArgs e)
        {
            // 遷移後のページ(SubPageOne.xaml.cs を指定する).
            await Navigation.PushAsync(new SubPageOne());
        }
    }
}

遷移前

f:id:mslGt:20171130234051j:plain

遷移後

f:id:mslGt:20171130234115j:plain

イベント

MainPage.xaml にボタンを追加して、 MainPage.xaml.cs の OpenSubPageOneButtonClicked をイベント関数としてセットします。

MainPage.xaml

< ?xml version="1.0" encoding="utf-8" ?>
< ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinSample.View.MainPage">
    < ContentPage.Content>
        < AbsoluteLayout BackgroundColor="Yellow">
            < Button AbsoluteLayout.LayoutBounds="20,20,200,30" Clicked="OpenSubPageOneButtonClicked" BackgroundColor="#0078d7">SubPage1< /Button>
        < /AbsoluteLayout>
    < /ContentPage.Content>
< /ContentPage>

プラットフォーム固有のクラスを呼ぶ

さて、基本的には各プラットフォーム共通の処理を XamarinSample (移植可能) に追加していくわけですが、
中にはプラットフォームごとに処理を分ける必要がある場合もあります。

その場合の方法はいくつかあるようですが、
今回は DependencyService を使うことにしました。

XamarinSample (移植可能) にインターフェースを作っておき、
各プラットフォームのプロジェクトにそのインターフェースを継承したクラスを作り、
そこで固有の処理を書きます。

で、それを DependencyService を使って実行時に該当プラットフォームのクラスを呼ぶ、
という流れのようです。

ICalc.cs

namespace XamarinSample
{
    public interface ICalc
    {
        float Calc(float currentValue, float calcValue);
    }
}

呼ばれる側

Subtraction.cs (Androidプロジェクトに作成)

using Xamarin.Forms;
using XamarinSample.Droid;

// DependencyServiceで呼べるようにする.
[assembly: Dependency(typeof(Subtraction))]
namespace XamarinSample.Droid
{
    public class Subtraction : ICalc
    {
        public float Calc(float currentValue, float calcValue)
        {
            return 4;
        }
    }
}

Subtraction.cs (UWPプロジェクトに作成)

using Xamarin.Forms;
using XamarinSample.UWP;

// DependencyServiceで呼べるようにする.
[assembly: Dependency(typeof(Subtraction))]
namespace XamarinSample.UWP
{
    public class Subtraction : ICalc
    {
        public float Calc(float currentValue, float calcValue)
        {
            return 2;
        }
    }
}

呼び出す側

SubPageOne.xaml.cs

using System.Diagnostics;
using Xamarin.Forms;
using XamarinSample.ViewModel;

namespace XamarinSample.View
{
    public partial class SubPageOne : ContentPage
    {
        public SubPageOne()
        {
            InitializeComponent();
            var subtraction = DependencyService.Get();
            Debug.WriteLine(subtraction.Calc(0, 1).ToString());
        }
    }
}

これで、Android で実行した場合は 4 が、Windows で実行した場合は 2 が返ってきます。 #if ~ で切り分けるよりシンプルで良いですね。

出力する

apk ファイル (Android) や ipa ファイルとしてアプリを書き出したい場合、 ソリューションエクスプローラーのそれぞれのプロジェクト上で 右クリック -> アーカイブ をクリックすればOKのようです。

UWP は HockeyApp または ストア > アプリパッケージの作成 から。
また WindowsiOS を出力する場合は、 MacXcode に接続されている必要があります。

なおストア配信時の設定は下記のような情報を参考に。

おわりに

同じように C# でコードを書き、マルチプラットフォームにアプリを作成できる Unity とはずいぶん違うのだなぁ、というのが率直な感想です。

C#7が使えたり (Unity は Experimental な機能を On にしても C#6 までの対応 )、
独自のお作法ももちろんあるのでしょうが、 WPF など普通の? C# に近い印象を受けました。

もちろん Unity が悪いって話ではなく、ただ違うという話ですよ。念のため。

Xamarin.Forms の XamlGUI エディタがない(っぽい)、
何かいじるとエラーが (大抵実行には問題がなく、エラーの出たプロジェクトをクリーン・リビルドすると直るのですが)、
とまだ発展途上なところも見受けられます。

が、すぐ改善されるだろうと思いますし、できれば自分もそれに寄与できればなぁ、とも思いました。

最後に、今回作ったサンプルは、(ボタン位置などめちゃくちゃですが)黄色をベースに作っていました。

その理由は。。。

♪ We all live in a yellow Xamarin, yellow Xamarin, yellow Xamarin ♫

https://www.youtube.com/watch?v=vefJAtG-ZKI

・・・おあとがよろしいようで。

明日は gnk263 さんです。よろしくお願いいたします(..)_

参照

【C#】Boxing / Unboxing ってどこで使われてるのか調べてみた

はじめに

先日 Effective C# を読んでいたのですが、その中で Boxing / Unboxing (ボックス化 / ボックス化解除)を避けましょう、という話がありました。

Boxing は雑にまとめると int などの値型を Boxing という仕組みを使って object 型にすることで、
参照型として扱えるようにする、ということです( Unboxing は object型から intを取り出す)。

OK。わかりました。値型を object 型の変数に入れないようにします(`・ω・´)ゞ

……( object 型って使わない気がするけど、どこで気を付けたら良いのだろう……?)

ということで、調べてみましたというお話です。

Boxing / Unboxing って何

Boxing / Unboxing をもう少し調べてみます。

  • C# では参照型のすべてのクラスは object クラスを継承しているが、値型の構造体はそうではない。
    これら2種類の値を同様に扱うためにBoxingがある。
  • 値型を参照型と同じように扱いたいとき、元の値を型を持たない参照型のデータにコピーする。
    こうすることで値型のデータを参照型であるかのように扱うことができる。
  • object 型に加えて、 interface 型に変換する場合にも Boxing される( IComparable などジェネリクス版でないもの)
  • Boxing を行うと参照型の変数が新たに生成(コピー)され、またその処理自体も重い(らしい)。
  • Boxing で生成された値はあくまで元の値をコピーしたものであり、別物であることに注意が必要である。
  • なお参照型はデータをヒープ領域へ、値型はデータをスタック領域へとそれぞれメモリーの違った場所に保存されるという違いがある。

ざざっと挙げてみましたが、上記のようなことのようです。

int originalNum = 9;
string originalText = "9";

// Boxingされる.
IComparable sampleComparable = originalNum;
// Boxingされる.
object sampleObject = originalNum;
// Unboxing.
int unboxingNum = (int)sampleObject;

// Boxingされない.
object sampleString = originalText;

誰がobject型を使うのか

さて、Boxing / Unboxing のことが分かったような気になったところで本題。

自分では呼んでいない気がする object 型を、誰が使っているのでしょうか。

通常こういう場合、2つのケースが考えられます。

  1. 昔は存在し、注意が必要だったが、言語の機能改善により見かけることはなくなった
  2. 自分が普段見ないところで使われている

1であれば中の人たちありがとう!めでたしめでたし! という感じなのですが、
2について調べてみることにしました。

string型の場合

例えば string クラスを調べてみます。

すると…

~省略~
public static bool Equals(String a, String b, StringComparison comparisonType);
public static bool Equals(String a, String b);
~省略~

public override bool Equals(object obj);

public bool Equals(String value); public bool Equals(String value, StringComparison comparisonType); ~省略~

ありましたね~、 object 型。

つまり、 string.Equals を使用する場合、引数が string 型の場合は「Equals(String value)」が呼ばれ、
それ以外の型であった場合は 「Equals(object obj)」が呼ばれ、Boxingが発生する、ということですね。

なお今回は string クラスを調べましたが、 値型である float などでも同じように型が異なる場合は object 型として受け取り、
値を確認した後処理を行っています。

Boxingを避けるために

比較などを行う場合、コンパイルエラーが出る・出ないにかかわらず、
前もって型を変換しておくのが良さそうですね。

また今回はあまり取り上げていませんが、IEnumerable ではなく IEnumerable< T> のように、
ジェネリクス版が用意されているものはできるだけそちらを使う、というのも有効です。

int originalNum = 9;
string originalText = "9";
// Boxingされない.
bool result = originalText.Equals( originalNum.ToString());
// Boxingされない.
List< int> nums = new List< int>{ originalNum };

Boxingを見つける

Boxing されているかを調べる方法はないのでしょうか。

実は、IL (中間言語) では Boxing されるときに box と表示されます。

分かりやすい!

なお IL は、 ReSharper を使う場合、メニューのReSharper > Windows > IL Viewer から表示できます。
(事前にビルドしておく必要あり)

f:id:mslGt:20171118131646j:plain

計測する

それでは最後に、遅いと話題の Boxing が本当に遅いか、 Unity の Profiler を使って調べてみます。

計測するコードはこちら。

public Button BoxingButton;
public Button CastButton;

private void Start ()
{
    BoxingButton.onClick.AddListener(() =>
    {
        Profiler.BeginSample("PerformanceSampling Boxing");
        var results = GetResultsWithBoxing();
        Debug.Log(results.Count);
        Profiler.EndSample();
    });
    CastButton.onClick.AddListener(() =>
    {
        Profiler.BeginSample("PerformanceSampling Cast");
        var results = GetResultsWithCasting();
        Debug.Log(results.Count);
        Profiler.EndSample();
    });
}

private List GetResultsWithBoxing()
{
    var results = new List();
    var sampleText = "99";
    var seed = Environment.TickCount;

    for (var i = 0; i <= 100000; i++)
    {
        var random = new System.Random(seed++);
        results.Add(sampleText.Equals(random.Next(1000)));
    }
    return results;
}

private List GetResultsWithCasting()
{
    var results = new List();
    var sampleText = "99";
    var seed = Environment.TickCount;

    for (var i = 0; i <= 100000; i++)
    {
        var random = new System.Random(seed++);
        results.Add(sampleText.Equals(random.Next(1000).ToString()));
    }
    return results;
}

詳しい計測方法はこちら。

で、その結果がこちらです。

f:id:mslGt:20171118131935j:plain

・・・ん? Boxing してる方がむしろ速いんじゃね・・・(。´・ω・)?

どうも、今回のコードでは Boxing にかかるコストより ToString() にかかるコストが大きかったっぽいですね。。

という訳で、やはりパフォーマンスを向上するには計測が必須、ということのようですね(白目)。

おわりに

という訳で、結局何が言いたいのか分からない内容となりましたが、
普段何気なく使っている関数も、どのように実行されるのかをしっかり追いかけるのは重要ですね。

また、パフォーマンス向上のためには、しっかりと計測して対策が有効かどうかを検証するのも大事ですね。

あと、「なんとなく」やっていた処理の裏側をちょっと覗いてみる、というのはとても楽しいものですね(*´ω`)

参照