Tips and tricks for our Android library

Tips and tricks for our Android library

Yoctopuce support often receives questions on how to use our Android library. Indeed, some choices made by Google force developers to pay careful attention to how to perform input/output operations, and more particularly for network access. In this post, we are going to show you our solution to this issue.




The Android examples included in the documentation of Yoctopuce modules are voluntarily very basic. The code contains the bare minimum to enable the use of Yoctopuce modules connected by USB. We made this choice to ensure that documentation stays readable, but if you decide to write an application using a module connected to a YoctoHub, you'll encounter some complications.

You can use our modules through a USB connection, but also through a network connection, for example if they are connected to a YoctoHub. In all other programming languages that we support, you only need to replace the "usb" character string by the IP address of the YoctoHub and the remainder of the code stays as is. For Android, you must take into account a specificity of the OS: all the network connections performed from the main thread are blocked.

This choice aims to guarantee that the main thread, which is responsible for the interface, is not slowed down by network connections. Because of this limitation, the Yoctopuce library can't be used from the main thread. This forces the developer to write more complex code that must manage communications between several threads.

To fully understand the implications of such an architecture, let's imagine an application which must display on a screen the temperature of a Yocto-Meteo when a button is pressed. For the operation to work on a Yocto-Meteo connected to a YoctoHub, this input/output operation must be decomposed into three steps:

  1. The button clickHandler is run from the main thread
  2. A second thread reads the value of the Yocto-Meteo and transmits the value to the main thread
  3. The main thread updates the interface with the value


All the operations using the Yoctopuce library must be performed during step 2, because network operations are forbidden on the main thread. Step 3 is necessary, as you can modify the interface only from the main thread.

Google offers several solutions which ease the writing of this type of sequences. The simplest one and the most common one is using an AsyncTask.

For example, the previous sequence could be written in the following way:



public void onClick(View v)
{
    // creates an AsyncTask that reads a Yocto-Meteo value
    AsyncTask<Void, Void, Double> task = new AsyncTask<Void, Void, Double>(){
        @Override
        protected Double doInBackground(Void... params)
        {
            // part of the code that is executed in the second thread
            try {
                YTemperature ytemp = YTemperature.FindTemperature("");
                double value = ytemp.get_currentValue();
                return value;
            } catch (YAPI_Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        protected void onPostExecute(Double temp)
        {
            // updates display with the result on the main Thread
            if(temp!=null){
                _textView.setText(String.format("%f░",temp));
            }
        }
    };
    task.execute();
}
 



Using AsyncTask frees you from allocating new threads and from the communications between the two threads. The doInBackground method is run in a second thread and the result is passed to the onPostExecute method which is run on the main thread. The programmer doesn't have to manage the second thread.

This solution is however not optimal, the code isn't very compact and you must write a new AsyncTask for each call to the Yoctopuce library. Moreover, all the AsyncTasks of the application are serialized on a single thread, which may raise some performance issues.

There are several other solutions, such as writing your own service or manually managing the communications with a second thread, but we suggest a solution that is relatively simple and efficient, and which allows you to use our library.

We wrote a YoctoActivity class inheriting from the Activity class and which takes care of starting a second thread used to run the code which performs network accesses, that is to say our Android library. This class can be used as parent class for all the Activities of the application using a Yoctopuce module.

This class defines two methods allowing you to run code on the second thread or on the main thread. The postBg method allows you to run code passed as an argument on the second thread, while the postUI method allows you to run code on the main thread. You can call these two methods from any thread without any particular precaution. If you already are in the correct thread, the code is run next, if you are in another thread, the YoctoActivity class takes care to transfer execution to the correct thread.

Using this new superclass, we can rewrite the previous example as follows:



public class MainActivity extends YoctoActivity {

  public void onClick(View v)
  {
    postBg(new BgRunnable() {
      @Override
      public void runBg() throws YAPI_Exception {
        YTemperature ytemp = YTemperature.FindTemperature("");
        final double temp = ytemp.get_currentValue();
        postUI(new Runnable() {
          @Override
          public void run() {
              _textView.setText(String.format("%f░",temp));

          }
        });
      }
    });
  }
}
 



The code stays verbose, but much less so than with AsyncTask. It's even more obvious if you don't need to update the interface. For example, the following piece of code registers the YoctoHub at the 192.168.1.14 IP address:


public void YoctopuceSetup()
{
    postBg(new BgRunnable() {
        @Override
        public void runBg() throws YAPI_Exception {
            YAPI.RegisterHub("192.168.1.14");
        }
    });
}
 



Moreover, the YoctoActivity class uses its own thread to run the code as a background task and is not perturbed by other AsyncTasks.

This solution has the advantage to factorize all the code that must manage communications between threads in a single class.

Let's see what this YoctoActivity class looks like concretely:



public class YoctoActivity extends Activity {
  private Handler _uiHandler;
  private BGHandler _bgHandler;
  private HandlerThread _bgHandlerThread;


  @Override
  protected void onStart() {
    super.onStart();
    _bgHandlerThread = new HandlerThread("DetailHandlerThread");
    _bgHandlerThread.start();
    _uiHandler = new UIHandler(this);
    _bgHandler = new BGHandler(this, _bgHandlerThread.getLooper());
  }

  @Override
  protected void onStop() {
    _bgHandlerThread.quit();
    _bgHandlerThread.interrupt();
    super.onStop();
  }


  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */

  private static class UIHandler extends Handler {
    private final WeakReference<YoctoActivity> _weakReference;

    UIHandler(YoctoActivity activity) {
        _weakReference = new WeakReference<>(activity);
    }
  }


  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */

  static class BGHandler extends Handler {
    private final WeakReference<YoctoActivity> _hubActivityWeakReference;

    BGHandler(YoctoActivity service, Looper looper) {
        super(looper);
        _hubActivityWeakReference = new WeakReference<>(service);
    }


    void post(final BgRunnable bgRunnable) {
        post(new Runnable() {
            @Override
            public void run() {
                try {
                    bgRunnable.runBg();
                } catch (YAPI_Exception e) {
                    e.printStackTrace();
                    YoctoActivity activity = _hubActivityWeakReference.get();
                    if (activity != null) {
                        activity.handleBgIOError(e);
                    }
                }
            }
        });
    }
  }

  interface BgRunnable {
    void runBg() throws YAPI_Exception;
  }


  protected void postBg(BgRunnable bgRunnable) {
    _bgHandler.post(bgRunnable);
  }

  protected void postUI(Runnable fgRunnable) {
    _uiHandler.post(fgRunnable);
  }

  /**
   * This method is called from second thread you cannot access
   * UI form this method.
   *
   * @param e the YAPI_Exception thrown by the bg thread
   */

  private void handleBgIOError(YAPI_Exception e) {
    e.printStackTrace();
  }

}
 



Internally, the YoctoActivity class uses two Handler enabling it to serialize the code execution on the main thread or on the second thread. Explaining how the HandlerThread and the Handler work would require a post on its own, but to make it simple a HandlerThread is a Thread to which you can easily post Runnable objects thanks to a Handler object.

In order to avoid using resources unnecessarily, the thread is started in the onStart() method and is stopped in the onStop() method, that is you can use the thread only when the activity is in the foreground. During this time, the thread waits "intelligently" for some code to run, that is it doesn't use CPU time in an active wait.

If you want to use this solution, you don't have to understand the whole code, you must simply add this class to your project and follow a couple of rules:

  • Make sure your activities inherit from the YoctoActivity class
  • Do not use the postBg() method if the activity is not in the foreground


You now have a solution to easily use a module behind a YoctoHub in an Android application.

Note: When the Yoctopuce module is connected on the USB port of the tablet, you can be tempted to not deal with this issue and use the Yoctopuce module from the main thread. However, it is wiser to apply the same precautions. First, it allows you to keep a reactive interface and fluid animations. Second, it enables you to keep the application compatible with Yoctopuce modules which are accessed via the network, for example to allow users to use cheap tablets which don't have an OTG USB support.

Add a comment No comment yet
Back to blog












Yoctopuce, get your stuff connected.