Android Programming Press on the image to return to the main documentation page.

Threading

Written by Andrew Graham

Basic4Android applications have only one thread, the main or GUI (Graphical User Interface)
thread that, not surprisingly, handles the user interface (and everything else).
Using this library you can create additional threads that operate (apparently)
in parallel alongside each other and the GUI thread. Threads can be useful to keep
the user interface alive while performing a long sequence of computations or while
waiting for something to happen.

Such threads are not allowed to manipulate GUI elements directly but this can be
achieved by the Thread.RunOnGuiThread method that runs a Sub on the main GUI thread.

This library contains a Thread object that runs the Basic4Android Sub on a separate
thread, a Lock object to facilite orderly use of resources by different threads and
can also be used to synchronise threads and an Exception object to give more
flexibility in handling exceptions than is native to Basic4Android.

List of types:

ExceptionEx
Lock
Thread

ExceptionEx

This is the Exception object that provides facilites for handling exceptions.
As Basic4Android has a more basic Exception object this one is called ExceptionEx
to avoid a name conflict.

Events:

None

Members:


  Cause As Exception [read only]

  Initialize (message As String)

  IsInitialized As Boolean

  Message As String [read only]

  Name As String [read only]

  StackTrace As String [read only]

  StackTraceElement (index As Int) As String

  StackTraceElementFile (index As Int) As String

  StackTraceElementLine (index As Int) As Int

  StackTraceElementMethod (index As Int) As String

  StackTraceLength As Int [read only]

  Throw As String

  ToString As String

  Version As Double [read only]

Members description:

Cause As Exception [read only]
Returns the cause of this Exception, or Null if there is no cause.
Initialize (message As String)
Constructs a new ExceptionEx with the current stack trace and
the specified message.
IsInitialized As Boolean
Message As String [read only]
Returns the information message which was provided when this
Exception was created. Returns null if no message was provided at
creation time.
Name As String [read only]
Returns the name of the class of this Exception which identifies
the type of the Exception.
StackTrace As String [read only]
Returns a string containing the entire stack trace for this Exception.
StackTraceElement (index As Int) As String
Returns a string describing the stack trace element at position
index in the stack trace for this Exception. Index 0 is the one
at the top of the stack and identifies the method and line number
in the Java source code at which this Exception was thrown.
StackTraceElementFile (index As Int) As String
Returns the name of the Java source file containing the method belonging
to this StackTraceElement.
StackTraceElementLine (index As Int) As Int
Returns the line number in the Java source for the method belonging
to this StackTraceElement. Only useful for the gurus amongst us!
StackTraceElementMethod (index As Int) As String
Returns the name of the method belonging to this StackTraceElement.
StackTraceLength As Int [read only]
Returns the number of stack trace entries in the stack trace for
this Exception.
Throw As String
Throws this Exception.
ToString As String
Returns a string containing both the name of this Exception and the
message, if any, provided when it was created.
Version As Double [read only]
Returns the version number of the library.

Lock

This is the Lock object that is a thin wrapper over an Android Semaphore object.

Events:

None

Members:


  Initialize (locked As Boolean)

  IsInitialized As Boolean

  Lock

  LockState As Boolean [read only]

  Unlock

  Version As Double [read only]

  Wait

  WaitFor (timeout As Int) As Boolean

Members description:

Initialize (locked As Boolean)
Makes a new locked Semaphore, with zero permits, or an unlocked Semaphore
with one permit. A Semaphore initialised unlocked can be used to control
access to a resource. To acquire a resource the sequence for a thread is
Wait() - use resource - UnLock(). A Semaphore initialised locked can be
used to flag events across threads. The sequence is for the waiting thread
to call Wait() and the invoking thread to call Unlock().
IsInitialized As Boolean
Lock
Sets the lock on this Semaphore. Any thread now trying to wait on it
will halt waiting for some other thread to unlock it.
LockState As Boolean [read only]
Returns the state of this Lock, True if locked, False otherwise.
Note that in some situations this might become invalid as soon
as it is read if another thread uses this Lock.
Unlock
Frees the lock allowing one other thread waiting on it to proceed.
If no threads are waiting then the next thread that tries to wait
will proceed immediately setting the lock as it does.
Version As Double [read only]
Returns the version number of the library.
Wait
Causes the calling thread to wait on this lock until it becomes free.
If this is locked then the calling thread will halt until another thread
frees it, the waiting thread will then proceed and the lock is imediately
set again. If it is already unlocked then the thread does not wait but
proceeds immediately setting the lock as it does so.
WaitFor (timeout As Int) As Boolean
Causes the calling thread to wait on this lock until it becomes free or until
the timeout expires. If the specified waiting time elapses without the lock
becoming free then the value False is returned. If the time is less than or equal
to zero, the method will not wait at all. If this is locked then the calling thread
will halt until another thread frees it or the timeout expires. If during the wait
the lock is freed then the waiting thread will proceed and and this method returns
True and the lock is immediately set again. If it is already unlocked then the
thread does not wait but proceeds immediately returning True and setting the lock
as it does so.

Thread

This is the Thread object that actually runs a Basic4Android Sub on a separate thread.
Normally Threads should be created as Process objects so they will persist as long as
the application exists. This will make their behaviour easier to predict and control
rather them being Activity objects which will need recreating when an activity resumes
which might leave orphan threads running that could cause unexpected problems.

Events:

Ended(endedOK As Boolean, error As String) 'The thread has terminated. If endedOK is False error holds the reason for failure

Members:


  Error As String

  Exception As Exception

  Initialise (eventname As String)

  Interrupt

  IsInitialized As Boolean

  IsInterrupted As Boolean [read only]

  Join (mSecs As Int) As Boolean

  MaxPriority As Int [read only]

  MinPriority As Int [read only]

  Name As String

  NormalPriority As Int [read only]

  Priority As Int

  Running As Boolean [read only]

  RunOnGuiThread (sub As String, params() As Object) As Boolean

  Sleep (mSecs As Int)

  Start (classinstance As Object, sub As String, args() As Object) As Boolean

  Version As Double [read only]

  Yield

Members description:

Error As String
Because Exceptions in a separate thread are not passed back to the main thread
and can cause the application to hang any Exceptions are trapped in the Thread
object Start method and the Exception message saved in the Error property and
the thread Ended event raised on the main GUI thread. Reading this property
gets the description of any exception caused by the thread code and is the same
string that is passed to the Ended event.
Exception As Exception
Because Exceptions in a separate thread are not passed back to the main thread
and can cause the application to hang any Exceptions are trapped in the Thread
object Start method and the Exception object saved in the Exception property
and the thread Ended event raised on the main GUI thread. Reading this property
returns the saved Exception object.
Initialise (eventname As String)
Initialises the thread object with the given event name.
Interrupt
Posts an interrupt request to this thread. The thread is only interrupted
if the thread is doing I/O or is in a wait-state (having called wait() on
a LockFlag. If the thread is blocked in a join() or sleep() it will be
woken up, its interrupt status will be cleared, and it will receive an
InterruptedException. If the thread is blocked in an I/O operation of an
InterruptibleChannel it will have its interrupt status set and receive a
ClosedByInterruptException and the channel will be closed.
If the thread Sub code is running freely without waiting or blocking and
it knows that it might be expected to stop on an Interrupt then it should
periodically check the IsInterrupted property and exit gracefully if it is True.
Otherwise the recommended way of stopping a thread is for it to inspect
a global variable whose value will tell it to stop or perform some other action.
IsInitialized As Boolean
IsInterrupted As Boolean [read only]
Returns a boolean indicating whether the thread has a pending interrupt request
(True) or not (False). It also has the side-effect of clearing the flag.
Join (mSecs As Int) As Boolean
Blocks the calling thread until the called thread finishes its execution
and dies or the specified timeout expires, whichever happens first.
Passing a value of zero or less blocks the calling thread until the called
thread finishes its execution and dies.
MaxPriority As Int [read only]
The maximum priority value allowed for this thread.
MinPriority As Int [read only]
The minimum priority value allowed for a thread.
Name As String
Sets or returns the name of the Thread. This name is displayed in DDMS while the
thread is running. The default name for a thread is the event name passed to Initialise.
NormalPriority As Int [read only]
The normal (default) priority value assigned to threads.
Priority As Int
Sets or returns the priority of the Thread.
Running As Boolean [read only]
Returns True if the thread has already been started and still runs code
(hasn't died yet). Returns False either if the thread hasn't been started yet
or if it has already started and run to completion and died.
RunOnGuiThread (sub As String, params() As Object) As Boolean
Causes the specified Sub to be scheduled for execution on the main GUI thread.
This mechanism is the only way a thread can access GUI items. This call returns
immediately. If it is necessary for the thread to wait for the result of the
GUI operation then it should wait on a Lock that will be reset by the called Sub
after it haas completed the GUI operation. Sometimes it seems that if the user
presses the back button, and maybe in other scenarions, then Android loses the
message and the GUI thread doesn't get it and the Sub isn't run and the Lock
isn't freed so it may be advisable in this case to use WaitFor with a reasonably
long timeout and retry until it returns True.
Sleep (mSecs As Int)
Note that this method affects the caller and not this Thread object.
Causes the thread to sleep for the specified number of milliseconds suspending
the thread before rescheduling it for execution. The precision of the length of
sleep is not guaranteed, the Thread may sleep for more or less time than requested
Start (classinstance As Object, sub As String, args() As Object) As Boolean
Starts the new thread of execution. The specified Sub will be called by the newly
started thread and passed the provided parameters. Returns True if the thread was
started, False if the thread is already running and a new one could not be started.
Threads are always started as daemon threads which are threads that will be killed
when the main application thread terminates. Because Exceptions in a separate thread
are not passed back to the main thread and can cause the application to hang any
uncaught Exceptions thrown in the thread Sub are trapped and saved in the threads
Error property. The Thread ends when the thread Sub returns or throws an uncaught Exception.
When the thread terminates, for whatever reason, the Ended event is raised. If the thread
terminated normally then the Ended event endedOK parameter is True otherwise endedOK is
False and the error parameter contains the reason which is the same string returned by
reading the Error property. If a Sub includes a Try - Catch it can always rethrow the
exception using an ExceptionEx object. The actual Exception is saved in the Exception property
and can be examined by assigning it to an ExceptionEx object.

For a Sub declared in a Class an object instance is required on which to invoke the thread Sub.
This can be Me if Start is called within a class or a reference to the class instance if Start
is called outside the Class.

For Subs declared in Activities and Services, which are actually static methods, an instance
is not required and null can be passed.

Note that the arguments are passed to the Sub as an Object array. This has an effect on how the
Basic4android compiler treats any object that derives from AbsObjectWrapper. When passed as an
Object the compiler unwraps the object and passes the underlying wrapped object. For this reason
the called Sub should expect the unwrapped object type and not the wrapper type by declaring the
parameter as type Object. To access the passed object assign it to a variable of the correct wrapper
type. A simple but somewhat contrived example is shown below.

...
Dim MyMap as Map
MyMap.Initialize
Thread1.Start(Me, "AsyncSetMap", Array As Object(myMap))
...

Private Sub AsyncSetMap(MapObj As Object)
Dim Map1 As Map
Map1 = MapObj
'Add some Data to the map
Map1.Put("A Key", "Some Data")
End Sub
Version As Double [read only]
Returns the version number of the library.
Yield
Note that this method affects the caller and not this Thread object.
This causes the calling thread to sleep for zero milliseconds which has the
effect of suspending the thread and immediately rescheduling it for execution.
Top