上一篇交代了我Xamarin填坑的背景,大概聊了聊第一步环境配置,第二步创建项目和开发框架选择。如果有一个可用的梯子,这部分基本不会出错。


  接下来就具体聊一聊写代码的过程中遇到的一些事儿。

  第三步是码代码:

  ①Http相关:

  我做的项目是一个校园助手,目前提供的功能绝大多数是查询功能。或者说,就是简单的爬虫,从校园服务器上爬取相关网页。因此,结合校园网站以及我的自身需求,我写了一个简单的用于发送Http请求的服务类HttpService,封装了一些Request方法:

  1. public async Task<string> SendRequst(string uri, HttpMethod method, IDictionary<string, string> dic = null, string referUri = "", CancellationToken cancellation = new CancellationToken())
  2. {
  3. HttpResponseMessage response = null;
  4. Encoding encoding = Encoding.UTF8;
  5. try
  6. {
  7. if (!string.IsNullOrEmpty(referUri))
  8. {
  9. _client.DefaultRequestHeaders.Referrer = new Uri(referUri);
  10. }
  11.  
  12. if (method == HttpMethod.Get)
  13. {
  14. response = await _client.GetAsync(uri, cancellation);
  15. }
  16. else
  17. {
  18. FormUrlEncodedContent content = new FormUrlEncodedContent(dic);
  19.  
  20. response = await _client.PostAsync(uri, content, cancellation);
  21. }
  22. var mediaTypeHeaderValue = response.Content.Headers.ContentType;
  23. if (mediaTypeHeaderValue != null && mediaTypeHeaderValue.CharSet != null)
  24. {
  25. if (mediaTypeHeaderValue.CharSet.Contains("gb2312"))
  26. {
  27. encoding = Encoding.GetEncoding("gb2312");
  28. }
  29. }
  30. using (var stream = await response.Content.ReadAsStreamAsync())
  31. {
  32. byte[] buffer = new byte[stream.Length];
  33. await stream.ReadAsync(buffer, , buffer.Length);
  34. var str = encoding.GetString(buffer,,buffer.Length);
  35.  
  36. return str;
  37. }
  38. }
  39. catch
  40. {
  41. throw;
  42. }
  43. finally
  44. {
  45. response?.Dispose();
  46. }
  47. }

这段代码本身没有问题,但是在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

  1. 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框架提供了这样一个接口来实现相关的功能

  1. public interface INavigationAware
  2. {
  3. void OnNavigatedFrom(NavigationParameters parameters);//从当前页面离开时
  4. void OnNavigatedTo(NavigationParameters parameters);//导航到当前页面时
  5. }

      可以通过实现该接口,然后在OnNavigationTo方法中去加载一些数据。当然这个接口的作用不限于此,主要作用还是处理页面导航时传递的参数。

      2.还有一个我在UWP中常用的办法,就是自定义一个OnLoad方法,然后绑定到View的Loaded事件上面。但这儿有一个很尴尬的问题,Xamarin的Page中并没有一个Loaded事件,相对变通的是它有一个Appearing事件,可以当作Loaded来用。具体的绑定方法如下:

  1. public partial class XXXPage : ContentPage
  2. {
  3. public XXXPage ()
  4. {
  5.  
  6. InitializeComponent();
  7. this.Appearing += XXXPage _Appearing;
  8. }
  9.  
  10. private void XXXPage _Appearing(object sender, System.EventArgs e)
  11. {
  12. LoadedCommand?.Execute(null);
  13. }
  14.  
  15. public static readonly BindableProperty LoadedCommandProperty = BindableProperty.Create("LoadedCommand", typeof(ICommand), typeof(CampusCardPage), defaultBindingMode: BindingMode.OneWay);//用于绑定的依赖属性
  16. public ICommand LoadedCommand
  17. {
  18. get { return (ICommand)GetValue(LoadedCommandProperty); }
  19. set { SetValue(LoadedCommandProperty, value); }
  20. }
  21. }

      上述代码大致在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即可。

  1. [assembly: XamlCompilation(XamlCompilationOptions.Compile)]//用于整个程序集
  2. [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)


  补几张图结束本篇

         

  在码代码的过程中,还遇到过一些其他的坑,以后慢慢更,因为这是连载系列......

[Xamarin]我的Xamarin填坑之旅(二)的更多相关文章

  1. bootstrap-table填坑之旅<二>事件

    接着研究bootstrap-table... ... 这一篇研究bootstrap-table的事件及回调函数 先上一个demo HTML <div class="alert aler ...

  2. Git 深度学习填坑之旅二(文件三种状态、打标签)

    0x01 三种状态 Git 有三种状态,你的文件可能处于其中之一: 已提交(committed).已修改(modified)和已暂存(staged). 已提交表示数据已经安全的保存在本地数据库中. 已 ...

  3. bootstrap-table填坑之旅<一>认识bootstrap-table

    应公司需求,改版公司ERP的数据显示样式.由于前期开发的样式是bootstrap,所以选bootstrap-table理所当然(也是因为看了bootstrap-table官网的example功能强大, ...

  4. React Native填坑之旅--与Native通信之iOS篇

    终于开始新一篇的填坑之旅了.RN厉害的一个地方就是RN可以和Native组件通信.这个Native组件包括native的库和自定义视图,我们今天主要设计的内容是native库方面的只是.自定义视图的使 ...

  5. React Native填坑之旅--Flow篇(番外)

    flow不是React Native必会的技能,但是作为正式的产品开发优势很有必要掌握的技能之一.所以,算是RN填坑之旅系列的番外篇. Flow是一个静态的检查类型检查工具,设计之初的目的就是为了可以 ...

  6. React Native填坑之旅--布局篇

    代码在这里: https://github.com/future-challenger/petshop/tree/master/client/petshop/src/controller 回头看看RN ...

  7. 使用vue开发微信公众号下SPA站点的填坑之旅

    原文发表于本人博客,点击进入使用vue开发微信公众号下SPA站点的填坑之旅 本文为我创业过程中,开发项目的填坑之旅.作为一个技术宅男,我的项目是做一个微信公众号,前后端全部自己搞定,不浪费国家一分钱^ ...

  8. https填坑之旅

    Boss说,我们买了个权威证书,不如做全站式的https吧,让用户打开主页就能看到受信任的绿标.于是我们就开始了填坑之旅. [只上主域好不好?] 不好...console会报出一大堆warning因为 ...

  9. stm32填坑之旅 - stm32f103c8t6点亮板载贴片蓝色LED

    转载请注明:https://www.cnblogs.com/rockyf/p/11691622.html 开篇 开篇一定要精彩,不然路人不理睬!下述是笔者作为arm小白的填坑之旅 没错,这个之前一直从 ...

随机推荐

  1. android SQLiteDatabase数据库使用的时候 常见问题

    用数据库的时候有时候一定要用_id否则会报错 对于Contentprovider的查询用contentResolver.insert()      一般的数据库操作用db.insert();

  2. spring监听与IBM MQ JMS整合

    spring xml 的配置: 文件名:applicationContext-biz-mq.xml <?xml version="1.0" encoding="UT ...

  3. windows文件属性操作 dsofile

    dsofile.dll是com组件,.net程序中引用dsofile.dll文件后,程序集名称会变成“Interop.DSOFile.dll”, com组件需要用regsvr32注册,所以需要注册ds ...

  4. js点击添加

    1.点击变色 <div id="dd" style="width:100px;height: 100px;background-color: #ccc"& ...

  5. Laravel + Vue 之 OPTIONS 请求的处理

    问题: 在 Vue 对后台的请求中,一般采用 axios 对后台进行 Ajax 交互. 交互发生时,axios 一般会发起两次请求,一次为 Options 试探请求,一次为正式请求. 由此带来的问题是 ...

  6. 值得一看!2018年最优秀的9个Android Material Design Apps!

    今年4月,谷歌Gmail推出了全新的设计外观,全新的配色方案,更多的空白区域和精致的图标.也带来了Material Design 的一些改变 – Material Theming (材料主题),旨在自 ...

  7. 为什么const对象只能调用const成员函数,而不能调用非const成员函数?

    在c++中,我们可以用const来定义一个const对象,const对象是不可以调用类中的非const成员函数,这是为什么呢?下面是我总结的一些原理. 假设有一个类,名字为test代码如下: clas ...

  8. PyCharm2017破解版安装

    PyCharm2017破解版安装步骤: 1.右击软件压缩包选择解压到pycharm2017. 2.在解压文件夹里面找到pycharm-professional-171.3780.47,右击打开. 3. ...

  9. Part 5 - Django ORM(17-20)

    https://github.com/sibtc/django-beginners-guide/tree/v0.5-lw from django.conf.urls import url from d ...

  10. VHDL实例化过程

    第二步:建立一个名为MUX_0的乘法器 第三步:在程序中例化,看以下程序. -- 该程序用来实现复数的乘法,端口分别定义的复数的 -- 输入的实部和虚部和输出的实部和虚部 LIBRARY IEEE; ...