sobota, 24 stycznia 2015

Kulisy Xamarina - odc. 1: podstawy

Jako długoletni praktyk związany z C# i .NET, pasjonat najnowszych platform Windows, jak również osoba, której przestały być obce platformy Android i iOS, pozwolę sobie otworzyć nowy wątek tematyczny poświęcony aplikacjom Android i iOS pisanym przy użyciu C#. Zdaję sobie sprawę, że wsadzam kij w mrowisko, ale myślę, że mogę obiektywnie na to spojrzeć z różnych stron. Brakuje mi poważnej i merytorycznej dyskusji na ten temat. Ile C#/.NET, a ile SDK Android czy iOS  możemy tutaj użyć? Jakie są zyski, jakie straty? Jak C# wykonuje się na konkurencyjnych platformach i jak może współpracować z zupełnie odmiennymi środowiskami? Czy trzeba mieć Mac-a? Co z emulatorami?  Odpowiedzi m.in na takie pytania zacząłem szukać jakiś tydzień temu mając pod ręką najnowszy Visual Studio 2015 CTP z zainstalowanym Xamarin (w bardziej funkcjonalnej wersji Business dostępnej za darmo przez 30 dni), ale po drodze wyszedł Windows 10 i trochę się to odłożyło w czasie.

Ale przejdźmy do sedna. Na Androidzie mamy doczynienia z dualizmem enginów i platform: Mono rozmawia z wrapperami obiektów Javy z engina Dalvik, a ten ostatni komunikuje się z Mono. Wynika to z liberalizmu Android co do silników pisanych pod niego aplikacji. Na iOS takiej dowolności nie ma, engine Apple jest jedynym słusznym enginem (podobnie jak WinRT dla aplikacji mobilnych na Windows, choć tam panuje większy liberalizm w zakresie stosowanych technologii i języków). Ale i z tym Xamarin sobie poradził, oryginalnymi narzędziami Apple kompiluje do postaci natywnej nie tylko kod naszej aplikacji w C#, ale i dostarczane przez siebie API w tym języku.

Chociaż Xamarin ma swój edytor, to użytkownicy wersji Business z pewnością będą używali Visual Studio. W takiej konfiguracji aplikacje tworzymy jako projekty w Visual Studio, który obecnie ma designery do plików definiujących UI w Android i iOS. W przypadku Android nawiązywana jest łączność z narzędziami z oryginalnego SDK (łączy się też z nimi Android Studio i Eclipse), aplikację możemy uruchamiać/debugować na emulatorach z SDK, emulatorach Android dostarczanych przez Visual Studio 2015 (są wydajniejsze, używają Hyper-V, póki co w tej rozwojowej wersji VS nie udało mi się użyć ich z Xamarinem) i fizycznych urządzeniach. W przypadku iOS Xamarin nie zwalnia nas od posiadania maszyny z systemem OS X, ponieważ używa oryginalnego SDK do kompilowania, debugowania/uruchamiania na emulatorach czy urządzeniach. Aby z tego korzystać, wcześniej na maszynie z systemem Apple’a instalujemy oryginalne narzędzia deweloperskie (iOS SDK / XCode) i Xamarina, po czym dokonujemy tzw. parowania uruchomionego tam hosta z Visual Studio.

Xamarin wydaje się dobrym rozwiązaniem dla deweloperów .NET otwartych na inne platformy, ponieważ z jednej strony dostarcza większą wygodę tworzenia poprzez C# i Visual Studio, a z drugiej strony używa oryginalnych narzędzi z  Android SDK czy iOS. Przy czym nie ma tutaj cudów. Kto zna platformy Android czy iOS, ten szybko odnajdzie się  i doceni odpowiedniki znanych mu funkcjonalności w C#.  Kto ich nie zna, to musi je w jakimś stopniu poznać , sama znajomość C# na pewno nie jest wystarczająca. Można by tutaj postawić pytanie, że jeśli ktoś zna platformy Android czy iOS w oryginalnych podejściach, to po co mu jeszcze eksperymenty z C#?  Oryginalne narzędzia czy języki nie są jakoś specjalnie bardziej trudne (Objective-C ucywilizował się, nie wymaga obecnie zarządzania referencjami, poza tym mamy od niedawna bardzo nowoczesny Swift), może czasami są tylko trochę mniej wygodne w porównaniu do C# (brak pewnych konstrukcji w innych językach np. zdarzeń w Javie czy generyków w Objective-C; mniej wygodne lub inaczej działające narzędzia). Tak, to prawda, ale jest jedna rzecz, którą daje nam Xamarin: możliwość współdzielenia kodu C# pomiędzy Android, iOS i Windows. Jeśli mamy napisać aplikację na trzy platformy, to może warto to rozważyć?  Nie chodzi nawet o oszczędność czasu na napisanie, czy późniejsze utrzymanie ekosystemu. Warto tutaj podkreślić, że współdzielenie kodu w Xamarin nie jest robione jak w Web, gdzie interfejs użytkownika tworzony jest w zupełnym oderwaniu od natywnych komponentów. Xamarin pozwala nam samym decydować, co współdzielimy, a czego nie, pozwala bezpośrednio korzystać z natywnych interfejsów użytkownika (stosunkowo od niedawna oferuje też framework z uniwersalnym XAML mapującym się na natywne elementy poszczególnych platform) oraz nie ogranicza w korzystaniu z natywnego API (obiecują pełne pokrycie i zgodność z wydawanymi na bieżąco wersjami platform, przyjrzymy się temu). Niewątpliwym minusem jest fakt, że za bardziej funkcjonalne wersje musimy zapłacić.

Poniżej, jak zawsze bardziej szczegółowe zapiski.

 

Intro

Xamarin.Android

  • historia:  Mono for Android –> Xamarin.Android
  • platforma
    • Android Kernel  -> Dalvik Runtime –> android.* (np. aktywności) + java.*
    • Android Kernel -> Mono Runtime (+ ACW - Android Called Wrapper, odwołania Dalvik do mono)  –> .NET API + Android Bindings (+MCW - managed called wrappers: wrappery na android.*)
  • Visual Studio: edytor kodu, UI designer (aktywności), kompilacja, debugowanie (możliwa integracja z ADB Server)

Xamarin.iOS

  • historia:  MonoTouch  -> Xamarin.iOS
  • platforma:  iOS (inny runtime niż z Apple nie jest dozwolony) –> [ .NET API + iOS Bindings  + kod aplikacji ] => kompilacja do pełnej aplikacji
  • Visual Studio: edytor kodu, designer UI zgodny z UIKit, obsługuje storyboards!  (dawniej trzeba było za pomocą Xamarin Studio na OS X wygenerować kod C# dla UI), współpraca z Xamarin.iOS.Server na OS X - zdalna kompilacja i debugowanie

Współdzielenie kodu

  • logika biznesowa
  • dostęp do danych / zarządzanie
  • dostęp do serwisów
  • portable class library

Xamarin.Forms

  • uniwersalnie definowane w C# UI dla iOS, Android, Windows Phone z zachowaniem pełnej natywności
  • możliwość definiowania XAML z data bindingami !  (XAML nie jest kompatybilny z dotychczasowymi edytorami VS)
  • predefiniowane rodzaje stron, layouty i kontrolki
  • wsparcie dla własnych kontrolek
  • możliwość stosowania MVVM
  • animacje (podstawowe i niskopoziomowe, wszystkie operacje odwołują się do natywnego API)

 

Xamarin.Android

[Activity(Label = “XXX”, MainLauncher = true, Icon = “@drawable/icon”]

public class MyActivity: Activity

{  

          Button buttonOk;

          ImageView  imageProduct;     

           protected override void OnCreate(Bundle bundle)

           {

                    base.OnCreate(bundle);

 

                    SetContentView(Resource.Layout.Main);

 

                    buttonOk = FindViewById<Button>(Resource.Id.buttonOk);   // @+id/buttonOk

                    imageProduct = FindViewById<ImageView>(Resource.Id.imageProduct);  

 

                    buttonOk.Click += buttonOk_Click;

           }

           void buttonOk_Click(object sender, EventArgs e)

           {

                     imageProduct.SetImageResource(Resource.Drawable.image_01);

           }

}

layout: plik .axml  (Android)

+ alternatywne wersje metod z generykami

+ typowa dla .NET obsługa zdarzeń i propercji

- nie tak rozbudowane kreatory jak w Android Studio

- potrzeba rekompilacji do przegenerowania klas z zasobami

integracja z narzędziami Android SDK

atrybuty zastępują część wartości podawanych w manifeście Android

Udało się uruchomić i debugować aplikację na emulatorze dla Cordovy (tym z SDK). Niestety w obecnej wersji jest jakiś nieznany błąd przy próbie uruchomienia na emulatorze Visual Studio (tym na Hyper-V).

 

Xamarin.iOS

OS X:

  • standardowe narzędzia deweloperskie dla iOS
    • XCode
    • iOS SDK
    • Symulator
  • Xamarin.iOS for OS X (po zalogowaniu pobieramy ze strony Xamarin)
    • Xamarin Studio (generacja kodu UI)
    • Xamarin.iOS Server (zarządzanie kompilacją i debugowaniem, uruchamianie symulatora, podłączenie do urządzenia)
    • Xamarin.iOS Build Host (na liście aplikacji): Pair - parowanie z maszyną z Visual Studio, generuje PIN, który należy podać w Visual Studio podczas kompilacji; po konfiguracji VS na pasku zadań: Options –> Keep in Dock

Windows:

  • Visual Studio
    • Tools –> Options –> Xamarin –> iOS Settings –> Find Mac Build Host (wybieramy i Connect, podajemy PIN, Pair)
    • panel Output –> Mac Server Log
  • Xamarin.iOS
  • Xamarin Bonjour Service (wykrywanie serwisów na OS X, delegacja do Xamarin.iOS Server)

Możemy pisać sobie duże ilości C#…

   public partial class RootViewController: UIViewController
    {

        UIButton button;
        float buttonWidth = 200;
        float buttonHeight = 50;

        
        public RootViewController(IntPtr handle) : base(handle)
        {
        }

        …

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.Frame = UIScreen.MainScreen.Bounds;
            View.BackgroundColor = UIColor.Yellow;
            View.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

            button = UIButton.FromType(UIButtonType.System);

            button.Frame = new RectangleF(
                (float)View.Frame.Width / 2 - buttonWidth / 2,
                (float)View.Frame.Height / 2 - buttonHeight / 2,
                buttonWidth,
                buttonHeight
                );      

            button.SetTitle("OK", UIControlState.Normal);

            button.TouchUpInside += (s, e) =>
            {
                button.SetTitle("XXX", UIControlState.Normal);
            };

            Add(button);

        }

        …

     }

Ale obecnie mamy designer dla storyboard w samym Visual Studio !

image

Umieszczam przycisk. Definiuję sobie warunki na layout (ideowo podobnie jak w XCode daję sobie centrowanie). Ustawiam kolor tła. Klikam przycisk dwukrotnie i mam już podpięty handler. Klasa C# widoku składa się z dwóch części partial. W części designera mamy kod:

    [Register ("RootViewController")]
    partial class RootViewController
    {
        [Action ("UIButton21_TouchUpInside:")]
        [GeneratedCode ("iOS Designer", "1.0")]
        partial void UIButton21_TouchUpInside (UIButton sender);

        void ReleaseDesignerOutlets ()
        {
        }
    }

W części “naszej” wygląda to tak:

    public partial class RootViewController: UIViewController
    {              
        public RootViewController(IntPtr handle) : base(handle)
        {
        }

        …

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

        }

        …

        partial void UIButton21_TouchUpInside(UIButton sender)
        {
            sender.SetTitle("AAA", UIControlState.Normal);
        }

     }

We wcześniejszych wersjach mogliśmy tylko w Xamarin Studio w OS X edytować natywny plik UI (w otwartym solution z VS). Edycja może się odbyć za pomocą XCode (w starszych wersjach jedyna możliwość), w którym robi się to co zwykle w designerze, definiujemy także outlety i akcje poprzez przeciąganie do interfejsu/implementacji kontrolera w Objective-C. Po zapisaniu i zamknięciu okna XCode, Xamarin Studio w partial widoku dla designera tłumaczy kod Objective-C na generowany przez siebie kod C# (można też samemu uruchamiać sychronizację z XCode przez polecenie Tools –> Sync with XCode).

Po uruchomieniu debugujemy:

xamarin.ios

Dodawanie obrazków do projektu Visual Studio:

  • Build Action ma być ustawiony na BundleResource
  • Ustawienie obrazka w ImageView

imageView.Image = UIImage.FromBundle(“image_01”);

Współdzielenie kodu między różnymi platformami

  • Portable Class Library
  • zalecany MVVM
  • w przypadku kompilacji pod iOS  w Configuration Manager aktywną platformę dla solution jest iPhoneSimulator

Międzyplatformowe zarządzanie obrazkami

  • iOS - prosto: nazwa obrazka z dzielonej biblioteki, reszta jak wyżej
  • Android - definiujemy własną klasę mapującą stringi z dzielonej biblioteki na stałe w Resource.Drawable (wyrażenie switch lub refleksja - dla większej wydajności raz odczytane wartości można cachować w słowniku)

Brak komentarzy: