Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - howto pass data to the Runnable in runOnUiThread?

I need to update some UI and do it inside of the UI thread by using runOnUiThread
Now the data for the UI comes from the other Thread, represented by data here.

How can i pass the data to the Runnable, so tht they can be used to update the UI? Android doesn't seem to allow using data directly. Is there an elegant way to do this?

public void OnNewSensorData(Data data) {

    runOnUiThread(new Runnable() {
        public void run() {
            //use data
        }
    });
}

My solution was creating a fioeld private Data sensordata inside of the runnable, and assigning data to it. This works only, if the original Data data is final.

public void OnNewSensorData(final Data data) {

    runOnUiThread(new Runnable() {
        private Data sensordata = data;
        public void run() {
            //use sensordata which is equal to data
        }
    });
}
like image 353
Skip Avatar asked Oct 13 '11 23:10

Skip


5 Answers

The problem you found is that

Inner classes in Java capture ("close over") the lexical scope in which they are defined. But they only capture variables that are declared "final".

If this is clear as mud, there's a good discussion of the details here: Cannot refer to a non-final variable inside an inner class defined in a different method

But your solution looks fine. In addition, provided that data is final, you could simplify the code to this:

public void OnNewSensorData(final Data data) {
    runOnUiThread(new Runnable() {
        public void run() {
            // use data here
            data.doSomething();
        }
    });
}
like image 55
spatulamania Avatar answered Nov 07 '22 16:11

spatulamania


If you want to avoid using an intermediate final variable (as described by Dan S), you can implement Runnable with an additional method to set Data:

public class MyRunnable implements Runnable {
  private Data data;
  public void setData(Data _data) {
    this.data = _data;
  }

  public void run() {
    // do whatever you want with data
  }
}

You can then call the method like this:

public void OnNewSensorData(Data data) {
  MyRunnable runnable = new MyRunnable();
  runnable.setData(data);
  runOnUiThread(runnable);
}

you could also make MyRunnable's constructor take in the Data instance as an argument:

public class MyRunnable implements Runnable {
  private Data data;
  public MyRunnable(Data _data) {
    this.data = _data;
  }

  public void run() {
    ...
  }
}

and then just say runOnUiThread(new MyRunnable(data));

like image 26
aleph_null Avatar answered Nov 07 '22 15:11

aleph_null


I had a similar problem where I wanted to pass information into the thread. To solve it with the android system, I modifying corsiKa's answer in: Runnable with a parameter?

You can declare a class right in the method and pass the param as shown below:

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    runOnUiThread(new OneShotTask(str));
}
like image 39
James Oravec Avatar answered Nov 07 '22 15:11

James Oravec


You'll need to update every time your program has new Data it wants to show. Your second code listing here is the standard way to accomplish this. There can be some catches if you're continuing to update Data in the thread. If this is the case consider blocking the thread until the UI finishes updating or copying the data to another Data object.

What's happening internally is that the JVM is copying the reference to the Data object for when the anonymous class will run. Data stored inside can still be changed. If your method requires additional changes to Data just use another variable (object reference) such as: final Data finalData = data;. You can also remove the line private Data sensordata = data; and use data directly in your run method.

It may not look elegant but this is the way Java passes object variables to anonymous classes. There is newer syntax in Java Language version 7 but Android is compatible with Java Language version 5 and 6.

like image 34
Dan S Avatar answered Nov 07 '22 16:11

Dan S


Here is a typical case where service callback is called to update a UI status string (TextView textStatus). The service may be threaded.

The sample combines checking if thread redirection is needed and the actual redirection:

   // service callbacks
    public void service_StatusTextChanged(final String s) {
        if( isOnUiThread() ) {
            textStatus.setText(s);
        } else {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    textStatus.setText(s);
                }
            });
        }
    }

    static public boolean isOnUiThread() {
       return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

See also How to check if running on UI thread in Android?

like image 1
user1656671 Avatar answered Nov 07 '22 16:11

user1656671