Cross-platform threading (Android, iOS, Windows Store, Windows Phone 8)

Document created by gregatnintex on Nov 9, 2014
Version 1Show Document
  • View in full screen mode

In the mobile team we implement the same functionality across 4 different platforms, all with their own different syntaxes and nuances.

 

Here is a brief comparison of basic threading and synchronization considerations when working on each platform - hope it is of some use to some cross-platform devs out there!

 

Synchronization

  • Prevent two threads from entering a code block or blocks simultaneously

Android

  • Has a semaphore wrapper to keep it simple
  • Can lock on a static or instance member or a method
    • By class:

synchronized (DatabaseStorageHelper.class)

{…}

  • By object:

synchronized (this._isRecycled)

{…}

  • By method:

public synchronized void onActionButtonClick(int index, FileItem item)

{…}

 

iOS

  • Has a semaphore wrapper to keep it simple
  • Can lock by any object - static or instance

@synchronized(self)

{…}

  • In iOS we have created macros to log entry and exit of synchronization blocks to help find deadlocks:

LoggedSemaphoreStart(SEMAPHORE, MSG)

LoggedSemaphoreEnd(MSG)

 

 

WT/WP

  • No nice wrapper, so use semaphores directly
  • Note that WaitOne can either wait indefinitely (same as synchronize on iOS and Android) or wait on a time period.  Waiting on time period of 0 implies just checking if the semaphore is set without waiting.

 

private Semaphore __queueSemaphore;

 

  1. this.__queueSemaphore= new Semaphore(1, 1, "Nintex.Liberado.….DebugDefaultLogger");

 

        public void ProcessDebugQueue()

        {

            if (!this._queueSemaphore.WaitOne(0))

                return;

 

System.Threading.Tasks.Task.Run(async () =>

            {

                try

                {

                    …

                }

                finally

                {

                    this._queueSemaphore.Release();

                }

            });

        }

 

  • Note that the same semaphore can be used in multiple places to ensure turn taking

 

Multithreading

Important to note the difference between queuing a work item to be executed on a thread pool, and just creating a new thread.   You could safely queue a thousand work items at once, but kicking off a thousand new threads would be bad.

 

Fire and forget thread

Android

  • Just create a new thread and start work on it. Timing unknown

 

new Thread(new Runnable() {

            @Override

            public void run() {

}).start();

iOS

  • Dispatch a unit of work to the global queue to be executed on the next available background thread.  Note that this is different to the Android process of just kicking off a thread.

 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

… }

 

 

Interactive thread

Android

  • Extend AsyncTask - eg DownloadImageTask
  • Can pass params
  • Can run something in background and then have something execute when complete with onPostExecute

new GetTasksAsyncTask().execute(false);

 

class GetTasksAsyncTask extends AsyncTask<Boolean, Void, Void>

    {

  1. a. onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
  2. b.       doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also usepublishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
  3. c. onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
  4. d.       onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

 

From <http://developer.android.com/reference/android/os/AsyncTask.html>

    }

 

iOS

  • Two primary ways:
    1. 1. Register a class to manage a protocol, then dispatch a package of work to a background thread, and pass in the class instance as a protocol.  Then in the package of work, call back to the protocol as required to perform pre/post/progress updates.
    2. 2. Dispatch a package of work to a background thread, passing in a block

 

WT/WP

  • We have the whole async/await thing.  Basically everything is an interactive background thread!

 

Running on UI thread

  • Queue a packet of work to run on the UI thread. Required for APIs that operate on UI.
  • This can also be handy even if we are already on the UI thread if we want to defer our operation to happen after other UI work.

 

Android

  • When activity available
  1. this.getActivity().runOnUiThread(new Runnable() {

@Override

public void run()

{

List<ControlLayoutModel> controlsInLayout = baseForm.getDefaultFormLayout().getControlLayoutsForRender();

for (ControlLayoutModel controlLayout : controlsInLayout)

addControlToForm(controlLayout);

}

});

 

  • When no activity available

new Handler(Looper.getMainLooper()).post(new Runnable() {

@Override

public void run()

{

List<ControlLayoutModel> controlsInLayout = baseForm.getDefaultFormLayout().getControlLayoutsForRender();

for (ControlLayoutModel controlLayout : controlsInLayout)

addControlToForm(controlLayout);

}

});

 

iOS

dispatch_async(dispatch_get_main_queue(), ^(void) {

… }

 

  • Note that we have written a global function runOnMainQueueWithoutDeadlocking that will check if we are on the UI thread or not and run a UI block safely:

 

void runOnMainQueueWithoutDeadlocking(void (^block)(void))

{

    if ([NSThread isMainThread])

        block();

    else

dispatch_sync(dispatch_get_main_queue(), block);

}

 

WT

From your UI thread, execute:

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;

From your background (non UI thread)

if (dispatcher.HasThreadAccess)
UIUpdateMethod();
else [await] dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => UIUpdateMethod(););

From <http://stackoverflow.com/questions/10579027/run-code-on-ui-thread-in-winrt>

From <http://stackoverflow.com/questions/11983929/how-do-i-determine-if-i-need-to-dispatch-to-ui-thread-in-winrt-metro>

 

  • Note that you can choose to await (wait until done) or not await (fire and forget) the RunAsync

 

WP

if (Dispatcher.CheckAccess() == false)

    {

Dispatcher.BeginInvoke(() => txtStatus.Text = "Something happened...");

    }

else

    {

// update the UI directly

txtStatus.Text = "Something happened..."

    }

 

From <http://weimenglee.blogspot.com.au/2013/07/windows-phone-tip-updating-ui-from.html>

 

 

 

Attachments

    Outcomes