Xamarin+SignalR开发Android应用

Xamarin可以使用C#语言开发原生的Android和iOS应用,被微软收购后更是热度不减;而SignalR是微软推出的实时通信技术。之前在项目中用过SignalR,而Xamarin则是首次接触,本次是想结合二者开发一个Windows、Android、iOS平台间实时通信的示例。

开发环境说明
操作系统Windows 10 professional
编译器VS 2015 update2
插件Xamarin for VisualStudio
其他Android SDK,Android Emulater

本实例参考了以下文章:

[ How To Use SignalR in iOS and Android Apps ]
[ Running SignalR on Mono]
[ Using SignalR in native Android and iOS apps]
[ Xamarin.Android快速入门]
[Using SignalR in WinForms and WPF]

感谢以上文章的作者!


1.创建服务端

由于之前写过WPF的服务端,所以直接拿来用了。

1.1新建项目ServerApp

选择Windows>WPF应用程序,添加新项目ServerApp。这里框架依然沿用以前的.NET Framework 4.5,并未使用最新的4.6.1 。
这里写图片描述

1.2引用SignalR包

选择“工具>NuGet包管理器>管理解决方案的NuGet程序包”

搜索“SignalR”,相应的包全部安装上(有些包服务端用不上,但是客户端后面会用上)。安装成功后会在项目引用中看到SignalR的几个引用已经添加进来了。
这里写图片描述

1.3添加启动入口和Hub

添加Startup.cs作为程序启动的入口。

using Owin;//必需的程序集引用

namespace ServerApp
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

添加MyHub.cs作为主要服务处理程序。MyHub继承于Hub并实现其接口,IMyHub是自己写的接口,目前只有一个方法“Task SendPlatform(string platform,string message)”。方法SendPlatform就是后面要用到的通信方法。

using Microsoft.AspNet.SignalR;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using System.Windows;
using Model;

namespace ServerApp
{
    public class MyHub:Hub,IMyHub
    {
        public override Task OnConnected()
        {
            return base.OnConnected();
        }
        public override Task OnDisconnected(bool stopCalled)
        {
            return base.OnDisconnected(stopCalled);
        }
        public override Task OnReconnected()
        {
            return base.OnReconnected();
        }

        public Task SendPlatform(string platform,string message)
        {
            return Clients.All.sendplatform(platform, message);
        }
    }
}

1.4添加启动服务方法

在主页面的后台MainWindow.cs添加控制服务启动及停止的方法。

namespace ServerApp
{
    public partial class MainWindow : Window
    {
        #region 通信设置
        public IDisposable SignalR { get; set; }
        static string ServerURI = ConfigurationManager.AppSettings["ServiceUri"]; //全局通信链接,地址为服务端所在主机IP地址,端口号可任意指定
        #endregion
        public MainWindow()
        {
            InitializeComponent();
            StartServer();
        }
        private void StartServer()
        {
            try
            {
                SignalR = WebApp.Start(ServerURI);
            }
            catch (TargetInvocationException)
            {
                WriteToConsole("已存在运行的服务:" + ServerURI);
                this.Dispatcher.Invoke(() => btn_Start.IsEnabled = true);
                return;
            }
            this.Dispatcher.Invoke(() => btn_Stop.IsEnabled = true);
            WriteToConsole("服务正在运行:" + ServerURI);
        }
    }
}

服务URI在配置文件app.config中设置。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <!--通信连接地址-->
    <add key="ServiceUri" value="http://127.0.0.1:46900/" />
  </appSettings>
</configuration>

2创建共享项目ShareMe

选择“Windows>共享项目”,添加项目ShareMe,用于公共方法共享,Android、iOS、Windows客户端都用的到。
这里写图片描述
添加类文件OSClient.cs

using System;
using Microsoft.AspNet.SignalR.Client;
using System.Threading.Tasks;

namespace ShareMe
{
    public class OSClient
    {
        private readonly string _platform;
        private readonly HubConnection _connection;
        private readonly IHubProxy _proxy;

        public event EventHandler<string> OnSendPlatform;

        public OSClient(string platform)
        {
            _platform = platform;
            _connection = new HubConnection("http://127.0.0.1:46900");//url要与服务端的ServerUri保持一致,否则通信不上
            _proxy = _connection.CreateHubProxy("MyHub");//创建Hub,与服务端MyHub类名一致
        }
        public async Task Connect()
        {
            await _connection.Start();
            _proxy.On("sendplatform",(string platform,string message)=> {
                if (OnSendPlatform!=null)
                {
                    OnSendPlatform(this, string.Format("{0}:{1}",platform,message));
                }
            });
            Send("Connected");
        }
        public Task Send(string message)
        {
            return _proxy.Invoke("SendPlatform", _platform, message;//调用方法名与服务端方法名一致
        }
    }
}

3创建安卓客户端AndroidApp

3.1新建项目

选择“Android>Blank App”新建项目AndroidApp。
这里写图片描述

3.2添加相关引用

首先添加共享项目ShareMe的引用
这里写图片描述
然后添加SignalR程序包的引用。在添加SignalR程序包的过程中遇到了比较棘手的问题,使用NuGet为项目添加SignalR引用时总是报错。
这里写图片描述
考虑是由于Mono还不支持的当前SignalR的版本,在网上查阅了相关资料后发现,SignalR是可以用于Mono的,但是需要使用portbale版本,可以在解决方案根目录下“packages\Microsoft.AspNet.SignalR.Client.2.2.0\lib\portable-net45+sl5+netcore45+wp8+wp81”找到,手动添加引用。
这里写图片描述
添加引用后编译会出错,把错误提示中出现的未引用程序包通过NuGet添加下(有Newton.Json及System.Net.Http等)。其中可能会有System.Net.Http.Extensions也需要手动添加引用。所有引用添加完后再次编译提示

这当中还有一个小插曲,就是引用添加完之后,编译总是失败并提示:

"aapt.exe"已退出,代码为“-1073741819”

尝试了N多次,即使新建项目后什么都不做依然会报错,这就比较奇怪了。后来发现了一篇Xamarin作者之一的文章,说到是由于Android SDK Bulid-tools 22.0.1自身问题所致,移除这个版本的编译工具即可。但是我在Android SDK Manager中没有安装这个版本,所以更加疑惑,后来向别人请教说可能是Android SDK Bulid-tools 24有问题。于是把这个版本的也卸载了,问题解决。
这里写图片描述

3.3添加客户端方法

添加完成后的项目文件结构如下:
这里写图片描述
Resources是项目资源所在,其中drawable主要是图片,layout是布局,values是静态资源,这些文件夹都是项目创建时自动生成的。后台处理主要包含在MainActivity.cs中。
打开布局文件“Resources\layout\Main.xml”,在设计器中添加一个EditText和ListView(把原来的删掉,直接从工具箱里拖,然后再属性中修改id)
这里写图片描述
这里写图片描述
添加完成后重新生成一下,不然资源文件在后台找不到。修改完成后的布局文件Main.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/Input" />
    <ListView
        android:minWidth="25px"
        android:minHeight="25px"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/Messages" />
</LinearLayout>

在后台文件MainActivity.cs中添加相应的方法:

using Android.App;
using Android.Widget;
using Android.OS;
using ShareMe;//共享项目引用
using Android.Views.InputMethods;
using System.Collections.Generic;

namespace AndroidApp
{
    [Activity(Label = "AndroidApp", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        int count = 1;

        protected override async void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            var client = new OSClient("Android");

            var input = FindViewById<EditText>(Resource.Id.Input);
            var messages = FindViewById<ListView>(Resource.Id.Messages);
            var inputManager = (InputMethodManager)GetSystemService(InputMethodService);
            var adapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, new List<string>());
            messages.Adapter = adapter;

            await client.Connect();
            input.EditorAction += delegate
            {
                inputManager.HideSoftInputFromWindow(input.WindowToken, HideSoftInputFlags.None);
                if (string.IsNullOrEmpty(input.Text))
                {
                    return;
                }
                client.Send(input.Text);
                input.Text = "";
            };
            client.OnSendPlatform+= (sender, message) => RunOnUiThread(() =>adapter.Add(message));
        }
    }
}

最后启动服务端,在模拟器上运行AndroidApp查看效果。


后记

写这个示例中间不少曲折,但也收获很多。写的过程中查找好多的文章都是几年前的,时效性可能有折扣,还是要多想多看多学多用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值