上一篇交代了我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, , buffer.Length);
var str = encoding.GetString(buffer,,buffer.Length); return str;
}
}
catch
{
throw;
}
finally
{
response?.Dispose();
}
}

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

      上述代码大致在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)


  补几张图结束本篇

         

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

[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. 安装Oracle客户端寻找配置文件tnsnames.ora

    # tnsnames.ora Network Configuration File: D:\app\Administrator\product\11.2.0\dbhome_1\network\admi ...

  2. DLL另類劫持注入法

    // Win32Project2.cpp : 定义 DLL 应用程序的导出函数. // //////////////////////////////////////////////////////// ...

  3. centos7之iptables与firewalld

    保障数据的安全性是继保障数据的可用性之后最为重要的一项工作.防火墙作为公网 与内网之间的保护屏障,在保障数据的安全性方面起着至关重要的作用. firewalld与iptables iptables f ...

  4. Vue.js 生命周期的应用

    生命周期示意图 值得注意的几个钩子函数 activated 类型:Function 详细: keep-alive 组件激活时调用. 该钩子在服务器端渲染期间不被调用. 参考: 构建组件 - keep- ...

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

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

  6. Jmeter参数化HTTP request中Send Files With The Request的文件路径和文件名

  7. 还在手工制作APP规范文档?这款设计神器你不容错过

    之前写了一些关于APP原型文档的文章:一款APP的交互文档从撰写到交付 这次想写下关于APP设计规范文档的内容,规范文档这个东西,实际上大部分中小型公司没有这方面的需求,也没精力去制作这样一个系统性的 ...

  8. 使用JMX监控Storm的nimbus、supervisor、woker

    可以通过在storm.yaml中增加如下样例的配置, 启动JMX来监控storm的各个角色. 其中对于Worker的监控,因为一个节点上可以有多个work,为了防止端口号重复导致启动失败,所以用动态代 ...

  9. 编译 link

    --generating dsym file change the appropriate one from 'DWARF with dSYM file' to just 'DWARF',This s ...

  10. devart 放大招了

    前面我纪念BDE 的文章里面说过,devart 会在今后在数据库存取技术上会有更大的 进步,没想到很快devart 放大招了.在最新的unidac 和sdac 中,devart 支持在非Windows ...