Android Threads, Handlers, handleMessage: Updating UI thread from runnable threads within external class files

StackOverflow https://stackoverflow.com/questions/17818939

Question

Background: I am trying to convert a Java (Non GUI) program to run on Android. For simplicity lets say this program has the following files:

DSGen.java, SInfer.java, Main.java

In the Main() method, objects for each of the classes DSGen and SInfer are initialized and passed into new Threads and started. Also each of the classes DSGen and SInfer implement Runnable with a public void run() method and both run methods output text to system.out.println().

Thread 1. Generates 100 values of random data Thread 2. Makes calculations on the 100 values of generated data (outputs 100 results)

In the Android version of the program I know that the UI thread handles all the UI updates. So in my MainActivity.java file i am trying to declare a Handler to receive messages from "Thread 2 with the output data" that can be used to update a TextView in the MainActivity UI thread. The app starts executing when I press a start button on the Android UI.

Problem: When I run this app my TextView does not update until the processing has ended. I also noticed that not all output is updated to my TextView and only 40/100 calculations actually make it to the TextView.

The actual code for the program is quite long and I thought it best to include only the pieces that relate to the threads, handler and run method for the SInfer class.

I am hoping that someone can give me a pseudo code example or solution for dealing with multiple threads where runnable methods are declared within 2 different class files (NOT inner classes within MainActivity) and where the UI can be updated by passing a message from one of the threads back to the UI thread.

Thank you in advance!

public class MainActivity extends Activity {

    /* UI Elements */
    private TextView output;

    /* Thread Handler */
    private Handler uiHandler;

    /* Handle message override */
    public class UIHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            output.append(msg.obj.toString());
            super.handleMessage(msg);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_run_fsi);

        output = (TextView) findViewById(R.id.displayTextView);
        uiHandler = new UIHandler();

        setStartButtonClickListener();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_run_fsi, menu);
        return true;
    }


    private void setStartButtonClickListener() {
        Button startButton = (Button) findViewById(R.id.startButton);
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                runFSI();

            }
        });
    }

    private void runFSI() {
        // initialize SInfer and pass uiHandler

        SInfer inferer = SInfer.getInstance(/*other variables*/, uiHandler);


        // initialize DSGen
        DSGenerator dsGen;

        Thread t1 = new Thread(dsGen);
        Thread t2 = new Thread(inferer);
        t1.start();
        t1.yield();
        t2.start();
        t2.yield();

        try {
            Thread.sleep(10000);
        } catch (Exception e) {
        }

        inferer.stop();
        dsGen.stop();

    }

}

The run method defined inside SInfer is as follows (the system.out code is left over from the original Java code)

@Override
public void run() {

    // System.out.println("Inference: Thread started...");

    while (stopFlag == false) {
        try {
            // System.out.println("SInference: run.........");
            Thread.sleep(200);
            inferenceResult = doInference();

            /* Send result to UI thread */

            Message msg = Message.obtain(uiHandler);
            msg.obj = inferenceResult.toString();
            uiHandler.sendMessage(msg);

        } catch (InterruptedException e) {
            // System.out.println(e.getMessage());
        }
    }
}
Was it helpful?

Solution

You are running Thread.sleep(10000); in the main thread (OnClick is called in the UI thread), so nothing could be updated in the UI during this 10 seconds. Try calling your runFSI method in a separate thread.

OTHER TIPS

Look into using an AsyncTask

override doInBackground() to handle all of your background calculations

override onProgressUpdate() to talk back to the UI thread while the thread is running usingthe publishProgress() method.

and override onPostExecute() to do any final things on the UI thread.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top