Android Tutorial [java] Libraries and multithreading

Discussion in 'Libraries developers questions' started by Erel, Nov 20, 2011.

  1. Erel

    Erel Administrator Staff Member Licensed User

    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:
    Code:
    BA.submitRunnable
    public [B]static[/B] Future<?> submitRunnable(Runnable runnable, Object container, int TaskId)
    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.
    Code:
    public Object raiseEventFromDifferentThread(final Object sender,
             final Object container, final int TaskId, 
             final 
    String event,
             final boolean throwErrorIfMissingSub, final Object[] params)
    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:
    Code:
    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:
    Code:
    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,
                         
    null0, 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);
     
    DonManfred, wendays and Peter Simpson like this.
  2. Erel

    Erel Administrator Staff Member Licensed User

    There is a new helper method that you can use to simplify the task of running tasks in the background.

    It is currently available in B4J 1.00 (beta 5). It will be added to B4A in the next update.

    The method signature is:
    Code:
    public static void runAsync(final BA ba, final Object SenderString FullEventName,
           final Object[] errorResult, final Callable<Object[]> callable)
    ba - The standard ba object.
    Sender - This will set the sender. In most cases it should be 'this'
    FullEventName - The event prefix together with the event itself. Note that the case is not important here.
    errorResult - This will be passed to the event in case of an error.
    Callable<Object[]> - Similar to Runnable. This is the code that will be executed in the background.

    For example in the Excel library there is an Initialize method that opens the file:
    Code:
    public void Initialize(String Dir, String FileName) throws IOException, BiffException {
         
    InputStream in = File.OpenInput(Dir, FileName).getObject();
         Workbook w = Workbook.getWorkbook(
    in, getDefaultSettings(Encoding));
         
    in.close();
         setObject(w);
       
    }
    Now this task can be slow so I wanted to provide an option to run it asynchronously:
    Code:
    public void InitializeAsync(BA ba, String EventName, final String Dir, final String FileName) {
         BA.runAsync(ba, this, EventName + 
    "_ready", new Object[] {false}, new Callable<Object[]>() {
           @Override
           public Object[] call() throws Exception {
             Initialize(Dir, FileName);
             return new Object[] {true};
           }
         });
       }
    The event declaration is:
    @Events(values={"Ready (Success As Boolean)"})
     
  3. thedesolatesoul

    thedesolatesoul Expert Licensed User

    Looks great thanks!
    Is it possible to add a handler to this somehow to post back the progress or that has to be done separately?
     
  4. Erel

    Erel Administrator Staff Member Licensed User

    Just to make sure that it is clear, the event is raised when the task completes.

    You can use ba.raiseEventFromDifferentThread if you want to raise intermediate events.
     
    thedesolatesoul likes this.
  5. somed3v3loper

    somed3v3loper Well-Known Member Licensed User

    Can you please explain which argument for which ?
    And I am trying to return anywheresoftware.b4a.objects.collections.List , How should I do it ?
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    The arguments are explained here: https://www.b4x.com/android/forum/threads/java-libraries-and-multithreading.12697/#post-204943

    Post your Java code if it is not clear.

    There is nothing special with this object.
    Code:
    anywheresoftware.b4a.objects.collections.List list1 = new anywheresoftware.b4a.objects.collections.List ();
    list1.Initialize();
    return list1;
     
  7. somed3v3loper

    somed3v3loper Well-Known Member Licensed User

  8. somed3v3loper

    somed3v3loper Well-Known Member Licensed User

    And this

    Code:
    public anywheresoftware.b4a.objects.collections.List call() throws Exception {
     
  9. DonManfred

    DonManfred Expert Licensed User

    ???
     
    Informatix likes this.
  10. Erel

    Erel Administrator Staff Member Licensed User

    No.

    Can you post the non asynchronous code?
     
  11. somed3v3loper

    somed3v3loper Well-Known Member Licensed User

    Yeah I know :D I am just entering this world

    I fixed it thanks and sorry for my stupid questions :)

    Code:
    public void do_task(final String dir ,final String zfile)    throws IOException {
           
            ba.runAsync(ba, this, eventName + 
    "_done",  new Object[] {false}, new Callable<Object[]>() {
           @Override
           public Object[] call() throws Exception {
            // I was trying to get a list from the line below like list1= return_list(dir,zfile)
            return new Object[] { return_list(dir,zfile)};
           
           }
         });
       
        
        }
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice