[Xamarin]我的Xamarin填坑之旅(二)


  上一篇交代了我Xamarin填坑的背景,大概聊了聊第一步環境配置,第二步創建項目和開發框架選擇。如果有一個可用的梯子,這部分基本不會出錯。


 

  接下來就具體聊一聊寫代碼的過程中遇到的一些事兒。

  第三步是碼代碼:

  ①Http相關:

  我做的項目是一個校園助手,目前提供的功能絕大多數是查詢功能。或者說,就是簡單的爬蟲,從校園服務器上爬取相關網頁。因此,結合校園網站以及我的自身需求,我寫了一個簡單的用於發送Http請求的服務類HttpService,封裝了一些Request方法:

public async Task<string> SendRequst(string uri, HttpMethod method, IDictionary<string, string> dic = null, string referUri = "", CancellationToken cancellation = new CancellationToken())
        {
            HttpResponseMessage response = null;
            Encoding encoding = Encoding.UTF8;
            try
            {
                if (!string.IsNullOrEmpty(referUri))
                {
                    _client.DefaultRequestHeaders.Referrer = new Uri(referUri);
                }

                if (method == HttpMethod.Get)
                {
                    response = await _client.GetAsync(uri, cancellation);
                }
                else
                {
                    FormUrlEncodedContent content = new FormUrlEncodedContent(dic);

                    response = await _client.PostAsync(uri, content, cancellation);
                }
                var mediaTypeHeaderValue = response.Content.Headers.ContentType;
                if (mediaTypeHeaderValue != null && mediaTypeHeaderValue.CharSet != null)
                {
                    if (mediaTypeHeaderValue.CharSet.Contains("gb2312"))
                    {
                        encoding = Encoding.GetEncoding("gb2312");
                    }
                }
                using (var stream = await response.Content.ReadAsStreamAsync())
                {
                    byte[] buffer = new byte[stream.Length];
                    await stream.ReadAsync(buffer, 0, buffer.Length);
                    var str = encoding.GetString(buffer,0,buffer.Length);

                    return str;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                response?.Dispose();
            }
        }
View Code

這段代碼本身沒有問題,但是在Xamarin中有個坑。由於需要用到gb2312編碼方式,但是我在調試安卓項目的時候,卻遇到類似“not support 936 code page”錯誤,解決的辦法就是在安卓項目屬性中添加CJK編碼方式支持。

  這兒有一個最佳實踐,分享自周岳老師

一般所有的Http請求通過HttpClient發起,每一個HttpClient的對象都幫我們維護了Http請求的一些基本信息,包括本地緩存,Cookie等。那默認的,如果Xamarin中直接使用HttpClient,它的實現完全是.net的實現方式。在這兒,可以通過ModernHttpClient來完成一些優化。

在創建HttpClient的時候,可以傳入一個ModernHttpClient.NativeMessageHandler實例,作為默認的Handler。這樣在Android平台會使用OKHttp來執行一些Http請求,細節可以看作者的Github

HttpClient client=new HttpClient(new ModernHttpClient.NativeMessageHandler() );

 

  Nuget:ModernHttpClient

  GitHub:ModernHttpClient

  在Http請求這塊兒,除了上面這個最佳實踐,其他部分可以100%重用我UWP項目中的代碼。基本無需任何修改。

 

  ②HTML Parse:

  因為校內網站很少有直接提供REST API服務,所以必須自己從各個頁面解析數據。對於Dom的分析,我選擇使用AngleSharp這個工具。這個工具現在已經可以支持Xamarin了。

  值得高興的是,個別站點使用了JSON傳送數據,針對JSON的解析,目前.Net平台最權威的就是Newtonsoft.Json這個庫了,配合Newtonsoft.Json以及.Net的dynamic動態類型,很容易能完成Json的解析。Xamarin完全支持!所以這部分的代碼也是100%重用。

     官網:AngleSharp

 

  ③XXXService:

  在上面介紹的Http相關請求中,為了使用方便,我封裝了一個基本的HttpBaseService類,主要就是這層各種XXXService獲取數據。校園助手目前是以查詢為主,包括查詢成績,課表等教務相關的信息,查詢一卡通余額,消費,解/掛失等簡單的個人信息。於是針對不同的功能大類別,我封裝了諸如EduService,InfoService等類,在類中實現了一些方法,用來完成獲取數據,解析數據的功能。對這些方法的調用,都是由ViewModel層來完成,所以這部分代碼和UWP項目中的也是所差不多。

 

  ④ViewModel:

  根據MVVM的特點,原則上是一個View對應一個ViewModel。ViewModel層和我UWP項目中的一樣,變動不多。但是,在UWP項目中,我使用的是MVVMLight這個框架,而在Xamarin中選擇了微軟自家的Prism,所以在消息通知,頁面導航等方面會有一些不同。下面列舉一些使用Prism MVVM時的一些內容。

  •   關於頁面導航:Prism在頁面導航方面,提供了一個接口Prism.Navigation.INavigationService,采用構造函數注入的方式。
  •   關於加載數據的問題:每當創建一個ViewModel,我們希望去獲取一些數據,可能是存儲在本地的,也可能要從網絡上獲取的。但是考慮到性能問題,這部分數據不能放在構造函數里面。

      1.比較好的辦法就是當導航到ViewModel對應的View的時候,再去加載數據,Prism框架提供了這樣一個接口來實現相關的功能

public interface INavigationAware
{
    void OnNavigatedFrom(NavigationParameters parameters);//從當前頁面離開時
    void OnNavigatedTo(NavigationParameters parameters);//導航到當前頁面時
}

      可以通過實現該接口,然后在OnNavigationTo方法中去加載一些數據。當然這個接口的作用不限於此,主要作用還是處理頁面導航時傳遞的參數。

      2.還有一個我在UWP中常用的辦法,就是自定義一個OnLoad方法,然后綁定到View的Loaded事件上面。但這兒有一個很尷尬的問題,Xamarin的Page中並沒有一個Loaded事件,相對變通的是它有一個Appearing事件,可以當作Loaded來用。具體的綁定方法如下:

public partial class XXXPage : ContentPage
    {
        public XXXPage ()
        {
            
            InitializeComponent();
            this.Appearing += XXXPage _Appearing;
        }

        private void XXXPage _Appearing(object sender, System.EventArgs e)
        {
            LoadedCommand?.Execute(null);
        }

        public static readonly BindableProperty LoadedCommandProperty = BindableProperty.Create("LoadedCommand", typeof(ICommand), typeof(CampusCardPage), defaultBindingMode: BindingMode.OneWay);//用於綁定的依賴屬性
        public ICommand LoadedCommand
        {
            get { return (ICommand)GetValue(LoadedCommandProperty); }
            set { SetValue(LoadedCommandProperty, value); }
        }
    }
View Code

      上述代碼大致在Page中實現一個自定義的LoadedCommand,然后在頁面觸發Apearing事件的時候,調用該命令的Excute方法,命令則通過數據綁定進行賦值,與ViewModel中的LoadedCommand相相綁定,這也是為什么把LoadedCommand定義為依賴屬性的原因。

    通過上面提到的1,2兩點,就可以在導航到頁面以后加載一些數據了,如果配合.net的async/await異步編程模型,就能更加流暢的實現數據加載了。

 

  ④Views:

  針對View的編寫是這次踏坑Xamarin最耗時的部分。雖然Xamarin.Forms可以用Xaml來編寫頁面,但是和UWP的XAML比起來,功能上差不多,體驗上卻很不好,尤其是自動補全和智能感知等方面。所以寫代碼會寫的很累。這些其實還好解決,畢竟熟悉一段時間后就基本沒有障礙了。唯一欠缺的就是針對XAML代碼的Previewer了,雖然今年的Connect()2016大會上,Xamarin Studio里面已經集成了初步的Previewer,但目前離正式發布還有一段距離,尤其是在VS里面。針對這點,給出一個稍微便捷的應對辦法。使用工具Gorilla-Player,在真機上預覽。具體使用可以看他的WIKI,下面展示一個使用該工具的截圖

當然目前這個工具問題還挺多,比如不支持靜態資源的引用等。在吐槽的同時,還是需要靜靜的等待官方的previewer正式發布。

  針對View也有一個最佳實踐:

因為現在XAML編輯器還不能很好的提供智能感知和自動補全等功能,所以在自己寫一些屬性的時候,很容易出現拼寫錯誤的問題。往往這種錯誤在編譯的時候,並不會被編譯器檢查到,於是錯誤就會發生在運行時,往往耗時耗力。Xamarin為我們提供了一個特性,叫做XamlCompilationAttribute,用來提供編譯程序集時候的XAML拼寫檢查等任務。他可以應用到整個程序集,也可以之應用在單個的View上。只需要指定其參數為XamlCompilationOptions.Compile即可。

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]//用於整個程序集
[XamlCompilation(XamlCompilationOptions.Compile)]//只用於單個Page/View

當然XamlCompilation怎么可能會只提供這么一點特性呢?它可以提升整個XAML的性能,通過預編譯為IL代碼的方式,減少加載XAML的時間。具體看官方文檔:

XamlCompilation(https://developer.xamarin.com/guides/xamarin-forms/xaml/xamlc/)

  

  ⑤數據存儲:

  一個完整的App,必須有本地存儲,無論是緩存一些文件,還是存儲一些必要配置信息。Xamarin跨平台提供了統一的數據存儲方式,但是在不同的平台上,具體實現是不同的。數據存儲方面,我選擇了SQLite這個輕量級的數據庫,這也是移動開發本地存儲最適合的數據庫之一了。好在現在也有比較成熟的ORM工具來支持SQLite了。
  Xamarin官方實例(https://developer.xamarin.com/guides/xamarin-forms/working-with/databases/)
  nuget:sqlite-net-pcl(https://www.nuget.org/packages/sqlite-net-pcl)

 


  補幾張圖結束本篇

         

  在碼代碼的過程中,還遇到過一些其他的坑,以后慢慢更,因為這是連載系列......

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM