Search This Blog

Sunday, May 22, 2016

Xamarin: Simple Client Service Communication

A very simple example showing how to implement a client-service communication between Xamarin Android client and a .NET service.

The example can be downloaded here.

Introduction

This is a simple example showing how to implement a client-service communication between Android and .NET using Eneter for Xamarin. The service is a simple .NET application which calculates two numbers and the client is a simple Xamarin application which communicates with the service to calculate numbers.



To Run Example

  1. Download this example.
  2. Open the solution in Visual Studio.
  3. If your Visual Studio supports Nuget then just compile projects within the solution and Eneter libraries will be downloaded automatically.
    If the Nuget is not supported then download and unzip Eneter Messaging Framework for .NET platforms and update references in the CalculatorService project to Eneter.Messaging.Framework.dll (for .NET 4.5) and in the AndroidCalculatorClient project to Eneter.Messaging.Framework.AndroidXamarin.dll.
  4. Run CalculatorService.
  5. Run AndroidCalculatorClient - the emulator will start.
The example uses the emulator. If you want to use it on a real device you need to figure out the IP address which is assigned to the computer where you want to run the service.
Then you need to modify the code in both client and service applications and use that IP address instead of currently used which are valid only for the emulator.

Xamarin Client

The client is a very simple Xamarin based Android application which provides UI controls to enter two numbers and the calculation button.
If the button is pressed the application sends the message to the service. Then once the service returns the result it displays it on the screen.
The whole communication is based on Eneter Messaging Framework. The Xamarin code can link Eneter.Messaging.Framework.XamarinAndroid.dll to access the communication functionality provided by Eneter.

The whole code is very simple:
using Android.App;
using Android.OS;
using Android.Widget;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using Eneter.Messaging.Threading.Dispatching;
using System;

namespace AndroidCalculatorClient
{
    // Request message.
    public class RequestMessage
    {
        public int Number1 { get; set; }
        public int Number2 { get; set; }
    }

    // Response message.
    public class ResponseMessage
    {
        public int Result { get; set; }
    }

    [Activity(Label = "AndroidCalculatorClient", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        // UI widgets.
        private EditText myNumber1EditText;
        private EditText myNumber2EditText;
        private TextView myResultTextView;
        private Button myCalculateBtn;

        private Handler myUiThreadDispatcher = new Handler();

        // Communication
        private IDuplexTypedMessageSender<ResponseMessage, RequestMessage> mySender;

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

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

            myNumber1EditText = FindViewById<EditText>(Resource.Id.Number1EditText);
            myNumber2EditText = FindViewById<EditText>(Resource.Id.Number2EditText);
            myResultTextView = FindViewById<TextView>(Resource.Id.ResultTextView);
            myCalculateBtn = FindViewById<Button>(Resource.Id.CalculateBtn);
            myCalculateBtn.Click += OnCalculateBtnClick;

            OpenConnection();
        }

        protected override void OnDestroy()
        {
            CloseConnection();
            base.OnDestroy();
        }

        private void OpenConnection()
        {
            // Create message sender.
            IDuplexTypedMessagesFactory aSenderFactory = new DuplexTypedMessagesFactory();
            mySender = aSenderFactory.CreateDuplexTypedMessageSender<ResponseMessage, RequestMessage>();
            mySender.ResponseReceived += OnResponseReceived;

            // Create TCP messaging for the communication.
            // Note: 10.0.2.2 is a special address for the Android emulator.
            //       If the emulator opens connection to 10.0.2.2 then it opens
            //       the connection to 127.0.0.1 on the machine where the emulator
            //       is running.
            TcpMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory()
            {
                // Ensure incoming response messages are routed into
                // the main UI thread.
                OutputChannelThreading = new AndroidDispatching(myUiThreadDispatcher)
            };
            IDuplexOutputChannel aOutputChannel =
                // When running on a real device please
                // provide the IP address of the service here.
                aMessaging.CreateDuplexOutputChannel("tcp://10.0.2.2:8060/");

            // Atach output channel and be able to send messages and receive responses.
            mySender.AttachDuplexOutputChannel(aOutputChannel);
        }

        private void CloseConnection()
        {
            // Detach the output channel and release the thread listening
            // to response messages.
            mySender.DetachDuplexOutputChannel();
        }

        private void OnCalculateBtnClick(object sender, EventArgs e)
        {
            // Send the calculation request to the service.
            RequestMessage aRequest = new RequestMessage();
            aRequest.Number1 = int.Parse(myNumber1EditText.Text);
            aRequest.Number2 = int.Parse(myNumber2EditText.Text);

            mySender.SendRequestMessage(aRequest);
        }

        // It is called when the result is received from the service.
        private void OnResponseReceived(object sender, TypedResponseReceivedEventArgs<ResponseMessage> e)
        {
            // Display incoming result.
            myResultTextView.Text = e.ResponseMessage.Result.ToString();
        }
    }
}

.NET Service

The service is a simple console application which uses Eneter Messaging Framework to listen for incoming requests. Once a message is received it calculates two numbers and sends back the result.

Here is the whole implementation:
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using System;

namespace CalculatorService
{
    // Request message.
    public class RequestMessage
    {
        public int Number1 { get; set; }
        public int Number2 { get; set; }
    }

    // Response message.
    public class ResponseMessage
    {
        public int Result { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Create message receiver.
            IDuplexTypedMessagesFactory aReceiverFactory = new DuplexTypedMessagesFactory();
            IDuplexTypedMessageReceiver<ResponseMessage, RequestMessage> aReceiver =
                aReceiverFactory.CreateDuplexTypedMessageReceiver<ResponseMessage, RequestMessage>();

            // Subscribe to process request messages.
            aReceiver.MessageReceived += OnMessageReceived;

            // Use TCP for the communication.
            IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
            IDuplexInputChannel anInputChannel =
                // If you use a real android device please provide the IP address
                // which is assigned to your computer within the network here.
                aMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:8060/");

            // Attach the input channel to the receiver and start listening.
            aReceiver.AttachDuplexInputChannel(anInputChannel);

            Console.WriteLine("The calculator service is running. Press ENTER to stop.");
            Console.ReadLine();

            // Detach the input channel to stop listening.
            aReceiver.DetachDuplexInputChannel();
        }

        private static void OnMessageReceived(object sender, TypedRequestReceivedEventArgs<RequestMessage> e)
        {
            // Calculate numbers.
            ResponseMessage aResponseMessage = new ResponseMessage();
            aResponseMessage.Result = e.RequestMessage.Number1 + e.RequestMessage.Number2;

            Console.WriteLine("{0} + {1} = {2}", e.RequestMessage.Number1, e.RequestMessage.Number2, aResponseMessage.Result);

            // Send back the response message.
            var aReceiver = (IDuplexTypedMessageReceiver<ResponseMessage, RequestMessage>)sender;
            aReceiver.SendResponseMessage(e.ResponseReceiverId, aResponseMessage);
        }
    }
}

4 comments:

  1. Interesting article. i am developing under xamarin forms for 3 mobile platforms. is there a possibility to use it in common pcl for all mobile platforms?

    ReplyDelete
    Replies
    1. The Eneter framework is currently not supported by portable class library. What are the platforms you want to use it for?

      Delete
  2. Is it possible to use a hostname or URL in place of a fixed IP address when establishing the connection?

    ReplyDelete
    Replies
    1. Yes, you can use hostname instead of the IP address.

      Delete