Using Azure IOT SDK In Android

Azure is a great IoT platform. They have SDK’s in all major languages. At OnRue Technology Solutions  when we integrated Azure with Android for our product DataWheels , we faced a lot of surprises. I am highlighting key changes we did for using Azure SDK seamlessly and successfully in Android OS. When we started, we did not think that we need to make changes as Android uses Java and Azure Java SDK works fine independently. However, the problem is that Azure SDK’s were developed using Java8 features, namely Lambda expressions and Android did not support Java 8 (atleast till August 2016 when we developed). Note that we used the master branch from Azure SDK git hub Here

We used SDK for both registering a device as well sending a message to Azure IOT Hub. Read below to know what we did. We used Android API version 18 as minimum SDK version and 23 as target SDK version and tested in KitKat device(Samsung Galaxy S3) 

    1. Changing Lambda Expressions : Lambda expressions is a key feature included in Java8. In the iothub-java-services-client project, following classes and methods uses Lambda expressions.                                                     a. ClassName:com.microsoft.azure.iot.service.sdk.ServiceClient                          methods:openAsync,closeAsync,sendAsync                                                 b. ClassName:com.microsoft.azure.iot.service.sdk.FeedbackReceiver                 methods:addDeviceAsync,getDeviceAsync,getDevicesAsync,                                  updateDevice,updateDeviceAsync,removeDevice,                                     getStatistics,exportDevicesAsync,importDevicesAsync,                                  getJobAsync                                                                                                                                                                                                                                  In order to fix it, logic which was in Lambda expression was put in another method. For example, below code ServiceClient.openAsync()                         public CompletableFuture<Void> openAsync()
      {
      // Codes_SRS_SERVICE_SDK_JAVA_SERVICECLIENT_12_014: [The function shall create an async wrapper around the open() function call]
      final CompletableFuture<Void> future = new CompletableFuture<>();
      executor.submit(() -> {
      try
      {
      open();
      future.complete(null);
      } catch (IOException e)
      {
      future.completeExceptionally(e);
      }
      });
      return future;
      }  
                                                                                                                                               is converted as follows                                                                                  public CompletableFuture<Void> openAsync()
      {
      // Codes_SRS_SERVICE_SDK_JAVA_SERVICECLIENT_12_014: [The function shall create an async wrapper around the open() function call]
      final CompletableFuture<Void> future = new CompletableFuture<>();
      OpenAsyncClass callback=new OpenAsyncClass(this, future);                         executor.submit(callback);
      return future;
      }   
      Below is the code for OpenAsyncClass
      public class OpenAsyncClass implements Runnable {private ServiceClient client=null;
      private CompletableFuture<Void> future=null;
      public OpenAsyncClass(ServiceClient client,CompletableFuture<Void> future){
      this.client=client;
      this.future=future;
      }
      @Override
      public void run() {
      // TODO Auto-generated method stub
      try {
      client.open();
      future.complete(null);
      } catch (IOException e) {
      // TODO Auto-generated catch block
      //e.printStackTrace();
      future.completeExceptionally(e);
      }


      }

      I tried to see common implementation, but could not do as the need was different in different places. Instead of having the new class as a seperate file, you can also use inline classes. But i am not a great fan of that approach.
    2. CharSet :  Class IotHubServiceSasToken uses StandardCharsets.UTF_8 . This is available only with Java 8 and since Android does not support java 8(at the time of development in August 2016), we used Charset.availableCharsets().get(“UTF-8”).name() instead of StandardCharsets.UTF_8  Without doing the fix, we were not able to register the devices. Another strange error what we got was that Android was not able to find org.apache.commons.codec.binary.Base64.encodeBase64String method inspite of having the correct jar(which has has correct class and correct method). As a workaround, we cloned the commons package from repository, created a new class by copying Base64 and use the new class.
    3. Endurance testing failed for App : This might not be a problem for all. But if you are posting messages to IoT Hub in a regular interval (e.g once in 3 seconds) using MQTT protocol (again using Azure SDK’s), you might encounter this problem.                                                                                    In our case, As recommended by Android, logic for sending the message was insulated from UI activity by encapsulating it into a service. Service was called once in 5 seconds. We noticed that after 4-5 sequences, the services are not executed. As per android, new execution of a service will not start if previous one was pending.  And we used single instance of client across multiple message posting ( this was recommended by Azure).  We were posting messages using sendEventAsync(msg, callback, lockobj)method of client. This method adds the messages to a list. The list was picked up a thread at  regular intervals.                                                                             On further debugging, we found that messages were posted using sendEventMethod of MQTTIoTHubConnection. This method shares a lock with open and connectionLost  methods in them. To add messages to temporary list ,sendEvent method calls addMessage method of the class which implements IotHubTransportInterface.  The addMessage  method of transport shares a lock with sendMessage method of transport. Since sendMessage calls sendEventMethod of MQTTIoTHubConnection.  If there is intermittent network error, then connection class is busy in sharing locks between open and close methods and messages which needs to be posted are waiting for the lock to release. These waiting messages in-turn hold lock in transport class which prevents new messgaes being added to the list. So as long as the messages are not added to the list, service remains active and is not completed and waits for a longer time.                                                                                                      Inorder to overcome this, we have added new methods to DeviceClient and Transport classes so that messages are sent immediately. Incase of any connection lost or close, and if messages are not sent, then an exception is thrown and we store the message in local store. This will be sent during synchronization.  We also changed open method in DeviceClient. I know that a design diagram would have helped a great here. But apologies me. Due to time constraints, I could not create one.
    4. High CPU: When using Azure default SDK’s for posting messages, I had observed that my app was consuming lot of CPU. It was found to be due to default implementation of DeviceClient which adds the messages to a list and a scheduled thread will run at specfic duration (once in 101 millis). This kind of looping and a ever present background thread added overhead. As part of fix for services not getting executed, we moved from delayed posting to real time posting and it indirectly solved this issue. The design developed by Azure is great and their Async posting of messages in perfect and glitch free. But if you are using it in a resource and networked crunched system, you might need to tweak it a bit like how we did. Drop a comment if you have any questions.

 

 

2 thoughts on “Using Azure IOT SDK In Android”

  1. Thank you so much for this post. I was facing the same issue. It’s a relief to know that someone has achieved this and is willing to share their ways with others :)

Leave a Reply

Your email address will not be published. Required fields are marked *