BlackBerry class equivalent to AsyncTask?
Asked Answered
D

2

6

My requirement is to have a thread that maintains a socket connection between a BlackBerry device and a server and exchanges commands, similar to request and response.

My problem is that I need to have this thread running in the background all the time and keep the UI available to the user. So, when there is a command from the server, this thread parses it and updates the UI and also if there's an action from the BlackBerry user, it sends it to the server and the server in turn handles it.

I developed the same application in Android using AsyncTask and it's working well. But in BlackBerry, as there's no such class, I used the invokeLater() option. The communication works fine between the server and the BB device, but the UI is frozen on the BlackBerry.

Anyone have any idea how to get this right?

Dartmouth answered 19/10, 2012 at 20:31 Comment(3)
I would just use a plain old java thread...Kinny
Consumer/Producer problem: en.wikipedia.org/wiki/Producer-consumer_problemRatliff
I actually really like this approach. By attempting to mimic the API, I find that more of my code can be reusable between Android and BlackBerry. I've done this with a few common classes.Chlorobenzene
C
12

Vishal is on the right track, but a little more is needed to match Android's AsyncTask. Since enums and generics aren't available with Java 1.3 on BlackBerry, you can't match the Android API perfectly.

But, you could do something like this (not tested ... this is just a starting point for you):

import net.rim.device.api.ui.UiApplication;

public abstract class AsyncTask {

    public static final int FINISHED = 0;
    public static final int PENDING = 1;
    public static final int RUNNING = 2;

    private int _status = PENDING;
    private boolean _cancelled = false;
    private Thread _worker;

    /** subclasses MUST implement this method */
    public abstract Object doInBackground(Object[] params);

    protected void onPreExecute() {
        // default implementation does nothing
    }
    protected void onPostExecute(Object result) {
        // default implementation does nothing
    }
    protected void onProgressUpdate(Object[] values) {
        // default implementation does nothing
    }
    protected void onCancelled() {
        // default implementation does nothing
    }
    protected void onCancelled(Object result) {
        onCancelled();
    }

    public final int getStatus() {
        return _status;
    }

    public final boolean isCancelled() {
        return _cancelled;
    }

    public final boolean cancel(boolean mayInterruptIfRunning) {
        if (_status == FINISHED || _cancelled) {
            return false;
        } else {
            _cancelled = true;
            if (mayInterruptIfRunning && _status == RUNNING) {
                // NOTE: calling Thread.interrupt() usually doesn't work
                //   well, unless you don't care what state the background
                //   processing is left in.  I'm not 100% sure that this is how
                //   Android's AsyncTask implements cancel(true), but I 
                //   normally just cancel background tasks by letting the
                //   doInBackground() method check isCancelled() at multiple
                //   points in its processing.
                _worker.interrupt();
            }
            return true;
        }
    }

    protected final void publishProgress(final Object[] values) {
        // call back onProgressUpdate on the UI thread
        UiApplication.getUiApplication().invokeLater(new Runnable() {
            public void run() {
                onProgressUpdate(values);
            }
        });
    }

    private void completeTask(final Object result) {
        // transmit the result back to the UI thread
        UiApplication.getUiApplication().invokeLater(new Runnable() {
            public void run() {
                if (isCancelled()) {
                    onCancelled(result);
                } else {
                    onPostExecute(result);
                }
                // TODO: not sure if status should be FINISHED before or after onPostExecute()
                _status = FINISHED;
            }
        }); 
    }

    public AsyncTask execute(final Object[] params) throws IllegalStateException {
        if (getStatus() != PENDING) {
            throw new IllegalStateException("An AsyncTask can only be executed once!");
        } else {
            try {
                onPreExecute();

                _worker = new Thread(new Runnable() {
                    public void run() {
                        try {
                            // run background work on this worker thread
                            final Object result = doInBackground(params);
                            completeTask(result);
                        } catch (Exception e) {
                            // I believe if Thread.interrupt() is called, we'll arrive here
                            completeTask(null);
                        }
                    }
                });
                _status = RUNNING;
                _worker.start();
            } catch (Exception e) {
                // TODO: handle this exception
            }
        }

        return this;
    }

}

Also, it's important to keep in mind the Threading Rules for Android's AsyncTask, which apply to the above implementation, too:

Threading rules There are a few threading rules that must be followed for this class to work properly:

  • The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.

  • The task instance must be created on the UI thread.

  • execute(Params...) must be invoked on the UI thread.

  • Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.

  • The task can be executed only once (an exception will be thrown if a second execution is attempted.)

Chlorobenzene answered 22/10, 2012 at 11:12 Comment(1)
@VishalVyas, no problem. You definitely had the right idea. And, now that I wrote something for this answer, I'll start using it in BlackBerry apps :)Chlorobenzene
P
4

You can create a Class that extends my implementation of class AsyncTask. Good Luck :)

Here the methods onPreExecute, onPostExecute are executed on UI thread and doInBackground is called on worker thread. Since onPreExecute, onPostExecute are abstract you can override them and provide your implementation like showing and dismissing progress dialog.

The sequence in which methods get's executed is 1) onPreExecute 2) doInBackground 3) onPostExecute

import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;

public abstract class AsyncTask {
    Runnable runnable;
    Thread threadToRun;

    public abstract void onPreExecute();

    public abstract void onPostExecute();

    public abstract void doInBackground();

    public void execute() {
    try {
        runnable = new Runnable() {

            public void run() {
                // TODO Auto-generated method stub
                UiApplication.getUiApplication().invokeLater(
                        new Runnable() {

                            public void run() {
                                // TODO Auto-generated method stub
                                onPreExecute();
                            }
                        });

                doInBackground();

                UiApplication.getUiApplication().invokeLater(
                        new Runnable() {

                            public void run() {
                                // TODO Auto-generated method stub
                                onPostExecute();
                            }
                        });

            }
        };

        threadToRun = new Thread(runnable);
        threadToRun.start();
    } catch (Exception e) {
        // TODO: handle exception
        Dialog.alert("Async Error Occured. " + e.toString());
    }
}
}
Prudery answered 19/10, 2012 at 21:15 Comment(5)
Although this is a nice start, there's a couple issues. (1) onPreExecute() above is not guaranteed to run before doInBackground() in your implementation, and almost certainly will not start before doInBackground(). (2) Android's AsyncTask also includes a method to publishProgress() during the background processing.Chlorobenzene
@Chlorobenzene It would be great if you can please explain why it's not guaranteed that onPreExecute() will not be invoked before doInBackground() as it will help me to improve this implementation. I have used this implementation in many of my blackberry projects for showing a progress indicator and dismissing it when the process completes.Prudery
and regarding publishProgress() yes you are correct.. ;) so I have to think on it.Prudery
You could also add parameters. Here we can't use generics, but you can pass Objects. Safe cancellation would be an interesting feature.Ratliff
@VishalVyas, I can't summarize in a couple hundred characters, but check out the code I posted in another answer, that's based on what you started. Only doInBackground() is actually abstract, and the methods in Android can take parameters, as Mister Smith said. Also, I believe my implementation will enforce the order of calls that I referred to in my previous comment.Chlorobenzene

© 2022 - 2024 — McMap. All rights reserved.