Niko
Niko

Reputation: 8163

Running code in main thread in background thread while blocking background thread

I am facing some problems trying to call main thread in my background thread.

Based on this post: Running code in main thread from another thread

The solution should be:

private void runOnMainThread() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {

        @Override
        public void run() {
            // Do something
            ottoBus.post(new MyObjectEvent(mMyObject));

            // End do something
            mMyObject = null;
        }
    });
}

However my background thread is still being able to read Bluetooth socket data between "Do something" and "End do something"

What am I missing here? Is it possible to lock the background thread, while "Do something" is being executed?

My code for reading socket data is following:

InputStream stream = null;
InputStreamReader reader = null;
BufferedReader bufferedReader = null;
String data = "";

try {
    stream = mSocket.getInputStream();

    byte[] bytes = new byte[20];

    int numRead = 0;
    while ((numRead = stream.read(bytes)) >= 0) {
        String s = new String(bytes, 0, numRead);
        if (mMyObject != null) {
            fillData(s); // Can cause NPE
        } else {
            mMyObject = new MyObject();
            fillData(s);
        }

        // This should be synchronised call
        runOnMainThread();

Thanks.

Upvotes: 2

Views: 2256

Answers (3)

Daniel Lubarov
Daniel Lubarov

Reputation: 7924

I find CountDownLatch to be the simplest way to accomplish this sort of thing. Here's a reusable method for running Runnables on the main thread and blocking on their completion:

private static final Handler mainHandler = new Handler(Looper.getMainLooper());

private static void runOnMainThreadBlocking(Runnable runnable) throws InterruptedException {
  CountDownLatch completionSignal = new CountDownLatch(1);

  mainHandler.post(new Runnable() {
    @Override public void run() {
      runnable.run();
      completionSignal.countDown();
    }
  });

  completionSignal.await();
}

Upvotes: 2

Gilad Haimov
Gilad Haimov

Reputation: 5867

You will need to use a Java pattern called wait/notify. Simply put: it defines two threads, a producer and a consumer, so that the consumer, after initiating the producer, stops and waits until the producer thread has completed.

It goes like this:

static final object uiActionMonitor = new Object();
transient boolean uiCompleted;

void network_thread_run() {

    int numRead = 0;
    while ((numRead = stream.read(bytes)) >= 0) {
        String s = new String(bytes, 0, numRead);

    // This should be synchronised call
    uiCompleted = false;
    runOnMainThread();

    synchronized(uiActionMonitor) {  //<---------- wait for UI to complete
       while (!uiCompleted) {
            uiActionMonitor.wait();
       }
    }

}

And the UI code:

private void runOnMainThread() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {

        @Override
        public void run() {
            // Do something

            // End do something
            uiCompleted = true;
            synchronized(uiActionMonitor) {  //<---------- release networking thread
                uiActionMonitor.notifyAll();                 
            }
        }
    });
}

Copy the synchronization logic exactly as is. This is where many developers get it wrong.

I must admit I fail to see why you need to block your networking thread while the UI thread is handling your message...

Upvotes: 2

user2641570
user2641570

Reputation: 814

I think you need to use locks or synchronized blocs. You can take a look into the java concurency documentation and more specificaly this and this part. This way you can guaranty that on portion of the code won't be executed muliple times in parallel.

Upvotes: 0

Related Questions