In order to keep Basic4android simple for developers, Basic4android code runs under the main UI thread.
Internally Basic4android has good support for multithreading.
Libraries developers are responsible for understanding and using the internal framework correctly.
Android shows the "application not responding" dialog after the main thread is blocked for more than 5 seconds. This means that lengthy actions such as network actions should be done with secondary threads.
This is indeed the case with Http library and AsyncStreams (and others as well).
Internal thread pool
An internal thread pool is available for handling background tasks. The pool is created on demand.
You can submit Runnables to this pool by calling:
You should pass null and 0 to the second and third parameters.
Note that the pool is limited to 20 concurrent threads. A RejectedExecutionException will be raised if more tasks are submitted.
Raising events on the main thread
In most cases the background task will run some task and when it finishes, it will raise an event. As the user (B4A developer) expects his code to run on the main thread, the library developer should delegate the event to the main thread.
This is done by calling BA.raiseEventFromDifferentThread.
sender - The object assigner to the Sender keyword.
container and TaskId: pass null and 0.
event - lower cased event sub.
throwErrorIfMissingSub - Whether to throw an error if there is on such sub.
params - The event parameters.
BA.raiseEventFromDifferentThread is responsible for delegating the event to the main thread. This is actually more complicated than it may sound as the activity might be paused when the event is raised.
For example, the user can start a download and then go to the home screen.
In that case, this method will store the event in an event queue and the event will be processed right before Activity_Resume.
The code of BA.raiseEventFromDifferentThread:
Example taken from Network.Connect:
Internally Basic4android has good support for multithreading.
Libraries developers are responsible for understanding and using the internal framework correctly.
Android shows the "application not responding" dialog after the main thread is blocked for more than 5 seconds. This means that lengthy actions such as network actions should be done with secondary threads.
This is indeed the case with Http library and AsyncStreams (and others as well).
Internal thread pool
An internal thread pool is available for handling background tasks. The pool is created on demand.
You can submit Runnables to this pool by calling:
B4X:
BA.submitRunnable
public [B]static[/B] Future<?> submitRunnable(Runnable runnable, Object container, int TaskId)
Note that the pool is limited to 20 concurrent threads. A RejectedExecutionException will be raised if more tasks are submitted.
Raising events on the main thread
In most cases the background task will run some task and when it finishes, it will raise an event. As the user (B4A developer) expects his code to run on the main thread, the library developer should delegate the event to the main thread.
This is done by calling BA.raiseEventFromDifferentThread.
B4X:
public Object raiseEventFromDifferentThread(final Object sender,
final Object container, final int TaskId,
final String event,
final boolean throwErrorIfMissingSub, final Object[] params)
container and TaskId: pass null and 0.
event - lower cased event sub.
throwErrorIfMissingSub - Whether to throw an error if there is on such sub.
params - The event parameters.
BA.raiseEventFromDifferentThread is responsible for delegating the event to the main thread. This is actually more complicated than it may sound as the activity might be paused when the event is raised.
For example, the user can start a download and then go to the home screen.
In that case, this method will store the event in an event queue and the event will be processed right before Activity_Resume.
The code of BA.raiseEventFromDifferentThread:
B4X:
if (processBA != null)
return processBA.raiseEventFromDifferentThread(sender, container, TaskId,
event, throwErrorIfMissingSub, params);
Runnable runnable = new B4ARunnable() {
@Override
public void run() {
if (ignoreEventsFromOtherThreadsDuringMsgboxError) {
Log.w("B4A", "Event: " + event + ", was ignored.");
return;
}
if (!isService && activityBA == null) {
Log.v("B4A", "Reposting event: " + event);
handler.post(this); //don't raise event during the activity creation.
}
else if (isActivityPaused) {
if (isService) {
Log.w("B4A", "Ignoring event as service was destroyed.");
}
else {
Log.w("B4A", "sending message to waiting queue.");
if (messagesDuringPaused == null)
messagesDuringPaused = new ArrayList<Runnable>();
if (messagesDuringPaused.size() > 10) {
Log.w("B4A", "Ignoring event (too many queued events: " + event + ")");
}
else {
messagesDuringPaused.add(this);
}
}
}
else {
if (container != null)
markTaskAsFinish(container, TaskId);
raiseEvent2(sender, false, event, throwErrorIfMissingSub, params);
}
}
};
handler.post(runnable);
return null;
}
Example taken from Network.Connect:
B4X:
public void Connect(final BA ba, final String Host, final int Port, final int TimeOut) throws UnknownHostException {
Runnable r = new Runnable() {
@Override
public void run() {
Socket mySocket = socket;
try {
InetAddress Address = InetAddress.getByName(Host);
InetSocketAddress i = new InetSocketAddress(Address, Port);
if (mySocket != socket)
return;
socket.connect(i, TimeOut);
ba.raiseEventFromDifferentThread(SocketWrapper.this,
null, 0, eventName + "_connected", true, new Object[] {true});
} catch (Exception e) {
if (mySocket == socket) {
ba.setLastException(e);
ba.raiseEventFromDifferentThread(SocketWrapper.this,
null, 0, eventName + "_connected", true, new Object[] {false});
}
}
}
};
ba.submitRunnable(r, this, 0);