使用 Xamarin 構建跨平台應用程序:Android 開發人員的視角
已發表: 2022-03-11一次編寫代碼並在多個平台上使用它一直是許多軟件開發人員的夢想。 雖然這已經有一段時間了,但它總是以可維護性、易於測試甚至更糟糕的用戶體驗為代價。
使用原生 SDK 開發移動應用程序可能是所有紮根於桌面應用程序開發領域的開發人員的起點。 編程語言會成為一些人的障礙:如果有人在開發 Java 桌面或後端應用程序方面有經驗,那麼轉到移動應用程序開發公司並使用 Android 會比在 iOS 上從頭開始使用 Objective-C 容易得多。
我一直對跨平台應用程序開發持懷疑態度。 當性能很重要時,像 Sencha、Cordova、Titanium 等基於 JavaScript 的框架永遠不會被證明是一個明智的選擇。 這些框架缺乏 API 和古怪的用戶體驗。
但是後來,我遇到了 Xamarin。
在本文中,您將了解如何使用 Xamarin 在多個平台之間共享代碼,而不會影響移動應用程序開發的任何其他方面。 本文將特別關注 Android 和 iOS,但您可以使用類似的方法添加對 Xamarin 支持的任何其他平台的支持。
什麼是 Xamarin?
Xamarin 是一個開發平台,可讓您使用 C# 和 .NET 為 iOS、Android 和 Windows Phone 編寫跨平台但本機的應用程序。
Xamarin 提供與本機 Android 和 iOS API 的 C# 綁定。 這使您能夠使用所有 Android 和 iOS 的本機用戶界面、通知、圖形、動畫和其他電話功能——所有這些都使用 C#。
每個新版本的 Android 和 iOS 都與 Xamarin 相匹配,新版本包含對其新 API 的綁定。
Xamarin 的 .NET 端口包括數據類型、泛型、垃圾回收、語言集成查詢 (LINQ)、異步編程模式、委託和 Windows Communication Foundation (WCF) 的子集等功能。 庫是通過逗留來管理的,以僅包含引用的組件。
Xamarin.Forms 是其他 UI 綁定和 Windows Phone API 之上的一個層,它提供了一個完全跨平台的用戶界面庫。
編寫跨平台應用程序
為了使用 Xamarin 編寫跨平台應用程序,開發人員需要選擇以下兩種可用項目類型之一:
- 可移植類庫 (PCL)
- 共享項目
PCL 允許您編寫可在多個平台之間共享的代碼,但有一個限制。 由於並非所有 .NET API 在所有平台上都可用,因此對於 PCL 項目,您將限制它在其目標平台上運行。
下表顯示了哪些 API 在哪些平台上可用:
特徵 | .NET 框架 | Windows 應用商店應用 | 銀光 | 視窗電話 | 賽馬林 |
---|---|---|---|---|---|
核 | 是 | 是 | 是 | 是 | 是 |
LINQ | 是 | 是 | 是 | 是 | 是 |
可查詢的 | 是 | 是 | 是 | 7.5+ | 是 |
序列化 | 是 | 是 | 是 | 是 | 是 |
數據註釋 | 4.0.3+ | 是 | 是 | 是 | 是 |
在構建過程中,PCL 被編譯成單獨的 DLL,並在運行時由 Mono 加載。 可以在運行時提供同一接口的不同實現。
另一方面,共享項目允許您為要支持的每個平台編寫特定於平台的代碼,從而為您提供更多控制權。 共享項目中的代碼可以包含編譯器指令,這些指令將啟用或禁用代碼段,具體取決於哪個應用程序項目正在使用該代碼。
與 PCL 不同,共享項目不生成任何 DLL。 代碼直接包含在最終項目中。
使用 MvvmCross 為您的跨平台代碼提供結構
可重用的代碼可以為開發團隊節省金錢和時間。 但是,結構良好的代碼使開發人員的生活更加輕鬆。 沒有人比開發人員更欣賞編寫良好的無錯誤代碼。
Xamarin 本身提供了一種機制,使編寫可重用的跨平台代碼變得更加容易。
移動開發人員熟悉必須編寫兩次或更多次相同邏輯才能支持 iOS、Android 和其他平台的場景。 但是使用 Xamarin,如前一章所述,很容易重用為一個平台編寫的代碼,也可以用於其他一些平台。
那麼 MvvmCross 在哪裡出現呢?
顧名思義,MvvmCross 使得在 Xamarin 應用程序中使用 MVVM 模式成為可能。 它帶有一堆庫、API 和實用程序,在跨平台應用程序開發中非常方便。
MvvmCross 可以顯著減少您在任何其他應用程序開發方法中編寫的樣板代碼量(有時用不同的語言多次編寫)。
MvvmCross 解決方案的結構
MvvmCross 社區推薦了一種非常簡單有效的方式來構建 MvvmCross 解決方案:
<ProjectName>.Core <ProjectName>.UI.Droid <ProjectName>.UI.iOS
MvvmCross 解決方案中的核心項目與可重用代碼有關。 Core 項目是一個 Xamarin PCL 項目,主要關注可重用性。
任何用 Core 編寫的代碼都應該盡可能地與平台無關。 它應該只包含可以在所有平台上重用的邏輯。 Core 項目不得使用任何 Android 或 iOS API,也不得訪問任何特定於任何平台的內容。
業務邏輯層、數據層和後端通信都是包含在 Core 項目中的完美候選。 通過視圖層次結構(活動、片段等)的導航將在核心中實現。
在繼續之前,有必要了解一種架構設計模式,這對於理解 MvvmCross 及其工作原理至關重要。 從名字就可以看出,MvvmCross 很大程度上依賴於 MVVM 模式。
MVVM 是一種架構設計模式,有助於將圖形用戶界面與業務邏輯和後端數據分離。
這種模式在 MvvmCross 中是如何使用的?
好吧,既然我們想要實現代碼的高度可重用性,我們希望在我們的 Core 中擁有盡可能多的內容,這是一個 PCL 項目。 由於視圖是代碼中唯一不同平台的部分,我們不能跨平台重用它們。 該部分在與平台相關的項目中實現。
MvvmCross 使我們能夠使用 ViewModels 從核心編排應用程序導航。
了解了基礎知識和技術細節後,讓我們通過創建自己的 MvvmCross Core 項目開始使用 Xamarin:
創建 MvvmCross 核心項目
打開 Xamarin Studio 並創建一個名為ToptalExampleSolution
的解決方案:
由於我們正在創建一個 Core 項目,因此堅持命名約定是一個好主意。 確保將Core
後綴添加到項目名稱中。
為了獲得 MvvmCross 支持,需要將 MvvmCross 庫添加到我們的項目中。 要補充一點,我們可以在 Xamarin Studio 中使用對 NuGet 的內置支持。
要添加庫,請右鍵單擊 Packages 文件夾並選擇Add Packages...選項。
在搜索欄,我們可以搜索 MvvmCross,它會過濾掉與 MvvmCross 相關的結果,如下圖:
單擊“添加包”按鈕會將其添加到項目中。
將 MvvmCross 添加到我們的項目後,我們就可以編寫核心代碼了。
讓我們定義我們的第一個 ViewModel。 為了創建一個,創建文件夾的層次結構,如下所示:
以下是每個文件夾的內容:
- 模型:代表真實狀態內容的領域模型
- 服務:保存我們的服務(業務邏輯、數據庫等)的文件夾
- ViewModel:我們與模型通信的方式
我們的第一個 ViewModel 叫做FirstViewModel.cs
public class FirstViewModel : MvxViewModel { private string _firstName; private string _lastName; private string _fullName; public string FirstName { get { return _firstName; } set { _lastName = value; RaisePropertyChanged(); } } public string LastName { get { return _lastName; } set { _lastName = value; RaisePropertyChanged(); } } public string FullName { get { return _fullName; } set { _fullName = value; RaisePropertyChanged(); } } public IMvxCommand ConcatNameCommand { get { return new MvxCommand(() => { FullName = $"{FirstName} {LastName}"; }); } public IMvxCommand NavigateToSecondViewModelCommand { get { return new MvxCommand(() => { ShowViewModel<SecondViewModel>(); }); } } }
現在我們有了第一個 ViewModel,我們可以創建第一個視圖並將事物綁定在一起。
安卓用戶界面
為了顯示 ViewModel 的內容,我們需要創建一個 UI。
創建 Android UI 的第一步是在當前解決方案中創建一個 Android 項目。 為此,右鍵單擊解決方案名稱並選擇Add -> Add New Project... 。 在嚮導中,選擇 Android 應用程序並確保將項目命名為ToptalExample.UI.Droid
。
如前所述,我們現在需要為 Android 添加 MvvmCross 依賴項。 為此,請按照與 Core 項目相同的步驟添加 NuGet 依賴項。
添加 MvvmCross 依賴項後,需要添加對 Core 項目的引用,以便我們可以使用那裡編寫的代碼。 要添加對 PCL 項目的引用,請右鍵單擊 References 文件夾並選擇Edit References...選項。 在 Projects 選項卡上,選擇之前創建的 Core 項目並單擊 OK。
下一部分可能有點難以理解。
現在我們必須告訴 MvvmCross 它應該如何設置我們的應用程序。 為此,我們必須創建一個Setup
類:
namespace ToptalExample.UI.Droid { public class Setup : MvxAndroidSetup { public Setup(Context context) : base(context) { } protected override IMvxApplication CreateApp() { return new Core.App(); } } }
從類中可以看出,我們是根據Core.App
實現告訴MvvmCross CreateApp
,這是Core中定義的一個類,如下圖所示:
public class App : MvxApplication { public override void Initialize() { RegisterAppStart(new AppStart()); } } public class AppStart : MvxNavigatingObject, IMvxAppStart { public void Start(object hint = null) { ShowViewModel<FirstViewModel>(); } }
在App
類中,我們正在創建AppStart
的一個實例,它將顯示我們的第一個 ViewModel。

現在唯一剩下的就是創建一個將被 MvvmCross 綁定的 Android Layout 文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro xmlns:local="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FirstName" /> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text LastName" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FullName" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click ConcatNameCommand" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click NavigateToSecondViewModelCommand" /> </LinearLayout>
在佈局文件中,我們有 MvvmCross 自動解析的綁定。 對於EditText
,我們正在為 Text 屬性創建一個綁定,這將是一個雙向綁定。 從 ViewModel 端調用的任何更改都將自動反映在視圖上,反之亦然。
View
類可以是一個活動或一個片段。 為簡單起見,我們使用一個加載給定佈局的活動:
[Activity(Label = "ToptalExample.UI.Droid", Theme = "@style/Theme.AppCompat", MainLauncher = true, Icon = "@mipmap/icon")] public class MainActivity : MvxAppCompatActivity<FirstViewModel> { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); } }
對於第一個按鈕,我們有一個命令綁定,這意味著當我們單擊按鈕時,MvvmCross 將從 ViewModel 調用ContactNameCommand
。
對於第二個按鈕,我們將展示另一個 ViewModel。
iOS 用戶界面
創建一個 iOS 項目與創建一個 Android 項目並沒有什麼不同。 您需要按照類似的步驟來添加新項目,只是這一次,而不是 Android,只需創建一個 iOS 項目。 只要確保你保持命名約定一致。
添加iOS項目後,需要添加MvvmCross iOS的依賴。 步驟與 Core 和 Android 完全相同(在您的 iOS 項目中右鍵單擊 References 並單擊Add References... )。
現在,就像我們為 Android 所做的那樣,需要創建一個Setup
類,它將告訴 MvvmCross 如何設置我們的應用程序。
public class Setup : MvxIosSetup { public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter) : base(appDelegate, presenter) { } protected override MvvmCross.Core.ViewModels.IMvxApplication CreateApp() { return new App(); } }
請注意, Setup
類現在擴展了 MvxIosSetup ,對於 Android,它正在擴展MvxAndroidSetup 。
這裡的一個補充是我們必須改變我們的AppDelegate
類。
iOS 上的AppDelegate
負責啟動用戶界面,因此我們必須說明視圖將如何在 iOS 上呈現。 您可以在此處了解有關演示者的更多信息。
[Register("AppDelegate")] public class AppDelegate : MvxApplicationDelegate { // class-level declarations public override UIWindow Window { get; set; } public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Window = new UIWindow(UIScreen.MainScreen.Bounds); var presenter = new MvxIosViewPresenter(this, Window); var setup = new Setup(this, presenter); setup.Initialize(); var startup = Mvx.Resolve<IMvxAppStart>(); startup.Start(); Window.MakeKeyAndVisible(); return true; } }
為了展示我們的 VIewModel,我們需要創建一個視圖。 對於這種情況,讓我們通過右鍵單擊項目並選擇Add -> New File並從 iOS 部分選擇 ViewController 創建一個 ViewController,我們將其命名為 FirstViewController。
Xamarin 創建了三個文件,我們將在其中定義綁定的內容。 與 Android 不同,對於 iOS,我們必須通過代碼以不同的方式定義我們的綁定(儘管我們也可以在 Android 上這樣做,並且在某些情況下,需要這樣做)。
當需要在視圖之間導航時,它是通過 ViewModel 完成的。 在命令NavigateToSecondViewModelCommand
中,方法ShowViewModel<SecondViewModel>()
將找到適當的視圖並導航到它。
但是,MVVMCross 是如何知道要加載哪個視圖的呢?
這沒有任何魔法。 當我們為 Android(Activity 或 Fragment)創建視圖時,我們正在擴展具有類型參數的基類之一( MvxAppCompatActivity<VM>
)。 當我們調用ShowViewMolel<VM>
時,MvvmCross 會查找一個View
,該 View 使用類型參數VM
擴展了Activity
或Fragment
類。 這就是為什麼不允許同一個 ViewModel 有兩個視圖類的原因。
控制反轉
由於 Xamarin 僅提供圍繞本機 API 的 C# 包裝器,因此它不提供任何形式的依賴注入 (DI) 或控制反轉 (IoC) 機制。
如果沒有運行時依賴注入或編譯時注入,就不容易創建鬆散耦合、可重用、可測試和易於維護的組件。 IoC 和 DI 的概念早已為人所知。 關於 IoC 的詳細信息可以在許多在線文章中找到。 您可以從 Martin Fowler 的介紹性文章中了解有關這些模式的更多信息。
IoC 從 MvvmCrosses 的早期版本開始就可用,它允許在應用程序啟動時和需要時在運行時注入依賴項。
為了獲得松耦合的組件,我們永遠不應該要求類的具體實現。 要求具體實現限制了在運行時更改實現行為的能力(您不能用另一個實現替換它)。 這使得測試這些組件變得困難。
出於這個原因,我們將聲明一個接口,我們將為其提供一個具體的實現。
public interface IPasswordGeneratorService { string Generate(int length); }
和實施:
public class PasswordGeneratorService : IPasswordGeneratorService { public string Generate(int length) { var val; var res = new StringBuilder(); var rnd = new Random(); while (0 < length--) { res.Append(valid[rnd.Next(valid.Length)]); } return res.ToString(); } }
我們的 ViewModel 現在可以要求我們負責提供的接口IPasswordGenerationService
的實例。
為了讓 MvvmCross 在運行時注入PasswordGeneratorService
實現,我們需要告訴 MvvmCross 使用哪個實現。 如果我們想在兩個平台上使用一個實現,我們可以在App.cs
中註冊實現,在應用程序註冊之後:
public override void Initialize() { RegisterAppStart(new AppStart()); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, PasswordGeneratorService>(); }
上面對靜態方法LazyConstructAndRegisterSingleton<TInterface, TType>
的調用註冊了要注入的實現。 此方法註冊適當的實現,但不創建對象。
該對象僅在需要時創建,並且僅在它註冊為單例後才創建一次。
如果我們想立即創建一個單例對象,可以通過調用Mvx.RegisterSingleton<TInterface>()
來實現。
在某些情況下,我們不想在我們的應用程序中只有單例。 我們的對象可能不是線程安全的,或者我們希望始終擁有一個新實例可能有其他原因。 如果是這種情況,MvvmCross 提供了方法Mvx.RegisterType<TInterface,TType>()
,它可以用於註冊實現,以便在需要時實例化一個新實例。
如果您需要為每個平台提供單獨的具體實現,您始終可以在特定於平台的項目中這樣做:
public class DroidPasswodGeneratorService : IPasswordGeneratorService { public string Generate(int length) { return "DroidPasswordGenerator"; } }
而我們實現的註冊是在 Droid 項目下的Setup.cs
類中完成的:
protected override void InitializePlatformServices() { base.InitializePlatformServices(); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, DroidPasswodGeneratorService>(); }
PCL 代碼初始化後,MvvmCross 將調用InitializePlatformServices
並註冊我們平台特定的服務實現。
當我們註冊多個單例實現時,MvvmCross 將只使用最後註冊的實現。 所有其他註冊都將被丟棄。
使用 Xamarin 構建跨平台應用程序
在本文中,您已了解 Xamarin 如何允許您跨不同平台共享代碼,同時仍保持應用程序的本機感覺和性能。
MvvmCross 提供了另一個抽象層,進一步增強了使用 Xamarin 構建跨平台應用程序的體驗。 MVVM 模式提供了一種創建所有平台通用的導航和用戶交互流的方法,使您需要編寫的特定於平台的代碼數量僅限於視圖。
我希望這篇文章給了你一個了解 Xamarin 的理由,並激勵你用它構建你的下一個跨平台應用程序。