Android - howto pass data to the Runnable in runOnUiThread? - android

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
}
});
}

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();
}
});
}

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));

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));
}

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.

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?

public static Activity globalContext = null;
CommonSetting.globalContext = this;// put this in MainACtivity.onCreate()
public void createToastShort(final String message) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(CommonSetting.globalContext, message, Toast.LENGTH_SHORT).show();
}
});
}

Related

Variable in Runnable

I create a Runnable in my class :
private Runnable monRun = new Runnable() {
public void run() {
Log.i("PLAY", "" + play);
if(play) {
/* [...] */
}
}
};
Here, play is an instance variable of my class.
But, when I call my Runnable :
handler.postDelayed(monRun, 100);
the variable play is captured in Runnable and if I changed value in an other method of my class, play will not be changed in my Runnable
Variables in per-object implementations and lambda expressions always get copied, cause in the meanwhile those variables may even get out of scope and just die. Make them copy a reference to an object then.
I suggest you to enclose your variable play in an object, and pass that object to the Runnable:
class ObjectToPass {
boolean play;
public synchronized boolean getPlay() {
return play;
}
public synchronized void setPlay(boolean play) {
this.play = play;
}
}
... // play is a ObjectToPass
private Runnable monRun = new Runnable() {
public void run() {
Log.i("PLAY", "" + play.getVar());
if(play.getVar()) {
/* [...] */
}
}
};
The synchronized keyword is to avoid problems with thread safety on the play member. If the Runnable is called in the same thread you can (and you should) just avoid synchronized.
I also suggest you to pass a reference to the ObjectToPass via the constructor of a class conforming to Runnable, instead of using the per-object implementation. This will help you handle data better:
class MyRunnable implements Runnable {
private ObjectToPass play;
public MyRunnable(ObjectToPass play) {
this.play = play;
}
#Override public void run() {
...
}
}
This is happening because you have play instance variable in your main thread and you are updating this variable in different thread.

Is it right if I read view on other thread,Android UI

Android can't update view direct on non-ui thread,but if I just read/get some information for ui?
For example I have a method updateModel() like
void updateModel() {
dailyReport.log.setValue(editLog.getText().toString());
dailyReport.plan.setValue(editPlan.getText().toString());
dailyReport.question.setValue(editQuestion.getText().toString());
}
Is it a problem if I run this method on non-ui thread.
Example below helped me solve this problem. Hope this will help you too
runOnUiThread(new Runnable() {
#Override
public void run() {
//do your job
}
});
Is it a problem if I run this method on non-ui thread?
With the assumption that dailyPlan is a model class and its methods do not modify the UI, then no, it is not a problem, Android will not complain and you will not receive any runtime errors. However, I would not follow this approach as in general it's a bad practice to access directly one threads data from another thread - you never know who is modifying what, read/write issues can occur and so on. These are usually solved by synchronizing the data, but if you put synchronized code in UI thread you made things even worse!
For your kind of problem, why don't you pass the data from UI controls to the thread that uses above logic? When you create it, pass the 3 strings:
editLog.getText().toString()
editPlan.getText().toString()
editQuestion.getText().toString()
Example:
private EditText editLog;
private EditText editPlan;
private EditText editQuestions;
private void activityMethodThatStartsThread() {
String log = editLog.getText().toString();
String plan = editPlan.getText().toString();
String questions = editQuestions.getText().toString();
DailyReportModel model = new DailyReportModel(log, plan, questions);
model.start();
}
public class DailyReportModel extends Thread {
private String log;
private String plan;
private String questions;
public DailyReportModel(String log, String plan, String questions) {
super();
this.log = log;
this.plan = plan;
this.questions = questions;
}
void updateModel() {
dailyReport.log.setValue(log);
dailyReport.plan.setValue(plan);
dailyReport.question.setValue(questions);
}
}
Yes you can Update the UI from a Non UI thread. There are two ways of doing this.
1) Do this with activity object (easy option get and Update)
2) Do this using Message Handler (a bit difficult and Update only)
Going for the 1st one all you need to do is Pass the Activity into the constructor. Here is Thread snippet
public class MyThread extends Thread {
private Activity _activity;
public MyThread(Activity activity) {
this._activity = activity;
}
#Override
public void run() {
super.run();
//do all what you want. and at the end to Update the Views Do this
if(!this._activity.isFinishing())
{ // executed if the activity is not finishing
this._activity.runOnUiThread(new Runnable() {
#Override
public void run() {
//set the public variables UI Variables like this
dailyReport.log.setValue(this._activity.editLog.getText().toString());
dailyReport.plan.setValue(this._activity.editPlan.getText().toString());
dailyReport.question.setValue(this._activity.editQuestion.getText().toString());
});
}
}
}
Now in the Activity
MyThread thread = new MyThread(this);
thread.start;

Calling a child thread method from parent thread

I have started a child thread from my parent thread (main/UI thread). And from this child thread I start another thread (grandchild?). Now I want to be able to pass a string from my main thread to my grandchild thread so I can call a method in the grandchild thread using this string.
As I am new to Java and threads this confuses me. I have looked at message handlers etc. but don't know if that is the way to go or not as I can't find an example that I understand.
Normally we don’t and cannot pass values among threads in java. We share values and objects among threads. There is a very subtle difference among sharing and passing values among threads. If You somehow pass a value to a Thread then that Thread will have exclusive right over it i.e thread will have its own copy of the value and we don’t need to worry about unsynchronized code or thread safety of that variable. We normally use this type of concept
in concurrent system using message passing.
Please see: https://en.wikipedia.org/wiki/Message_passing
But in Java We normally share values among multiple threads. And there is no relationships among threads i.e is there is no child or grand child threads. There are only deamon and non-deamon threads (http://www.javaworld.com/jw-04-1996/jw-04-threads.html).
So if you have to share some value between grand child and main thread. you have to make a single Object which is available/share among them. Please see the below example :
public class GranDChildThread {
/**
* Please note there are no relation ships among thread.
* Simply put, a thread is a program's path of execution.
* All the three threads have access to shared String
*/
public static String sharedString = new String("its a wonderfull life");
public static void main(String[] args) {
// this is my main Thread
System.out.println("Main Thread: I have access to
sharedString : " + sharedString);
Thread childThread = new Thread(new Runnable() {
#Override
public void run() {
// this is child thread
System.out.println("Child Thread:
I have access to sharedString : " + sharedString);
Thread grandChildThread = new Thread(new Runnable() {
#Override
public void run() {
// this grand Child
System.out.println("Grand Child Thread:
I have access to sharedString : " + sharedString);
}
});
}
});
}
}
Here is the simplest example. Using setters and getters can come handy.
import android.os.Bundle;
import android.util.Log;
public class MyExtendedThread extends Thread{
private String mData;
public MyExtendedThread(String dataStringIwantToPass){
this.mData = dataStringIwantToPass;
}
#Override
public void run() {
//DO SOMETHING WITH mData
//for example:
while(true){
if(mData != null)
Log.i("mData: ", mData);
Thread.sleep(2000);//sleep it few seconds :)
}
}
public String getData() {
return mData;
}
public void setData(String mData) {
this.mData = mData; //you might want to change the value at some point of time
}
}
Here we inherit the Thread class to our custom class that will have setters and getters and argumented constructor. Thats pretty straight forward. We will use the setter to change the value of the string at any time we want.
#Override
public void onCreate(Bundle savedInstanceState ) {
final String data = "this is string";
Thread theThreadStartedFromUIthread = new Thread(new Runnable(){
MyExtendedThread myOtherThread = new MyExtendedThread(data);
#Override
public void run() {
// Do some stuff
myOtherThread.start();// the other (grandchild) thread has started
//here i want to change the value of mData, assuming the thread is still running
myOtherThread.setData("Alright, I've changed you.");
}});
};
}
Is it helpful?

Publish progress from an external class during Async Task?

I have an async task with a doInBackground() method like this:
protected String doInBackground(String... params) {
MyClass session = new MyClass("email", "password");
return session.isAuthorized();
}
While MyClass, which is in a completly different package, is something like this:
private class MyClass {
// fields, constructors, etc
public Boolean isAuthorized() {
// some stuff
log("Action 1...");
// some stuff
log("Action 2...");
// some other stuff
return result;
}
public static void log(String str) {
// HERE I would like to publish progress in the Async Task
// but, until now, it's kinda like:
System.out.println(str);
}
}
The question is: how can I pass log descriptions hold in the log() method, external even to the main Activity "container", to publishProgress() method? I already read this thread: Difficulty in changing the message of progress dialog in async task - but it wasn't a valid source of help, since my method isn't contained in the main class public class MainActivity extends Activity {}.
EDIT #1 -
After some work, I realized that the only way is passing to the external class a referece to the "main" thread, and then implement there a specific method to publish progress. In such a way:
public void log(String str) {
if (mThreadReference==null) {
System.out.println(str);
} else {
mThreadReference.doProgress();
}
}
While mThreadReference points to this AsyncTask:
private class MyClassTask extends AsyncTask<String,String,String> {
#Override
protected String doInBackground(String... params) {
// constructs MyClass instance with a reference and run main method
(new MyClass("email", "password", this)).isAuthorized();
}
public void doProgress(String str) {
publishProgress(str);
}
#Override
protected void onProgressUpdate(String... values) {
// some stuff
}
#Override
protected void onPostExecute(String result) {
}
}
But, obviously, Eclipse is warning me: The method publishProgress() is undefined for the type Activity. How can I write a general and absolute method, in the external class, which I can use in more than one specific AsyncThread?
--> LOGs IN THE LOGIN THREAD 1
/
EXTERNAL CLASS ---> LOGs IN THE LOGIN THREAD 2
\
--> LOGs IN THE LOGIN THREAD 3
I figured out that the only way is importing the istance of the AsyncTask, which has to be public (not the default option!), in the main activity. With this trick, I can invoke the publishProgress method even if it's protected.
// MyClass, in a different package
import MainActivity.MyClassTask mThreadReference = null;
// some stuff...
public void log(String str) {
if (mThreadReference==null) {
System.out.println(str);
} else {
mThreadReference.doProgress("");
}
}
While this is the activity:
public class LoginLanding extends Activity {
// stuff...
public class MyClassTask extends AsyncTask<String,String,String> {
// bla bla bla, some stuff...
public void doProgress(String str) {
// do something
}
}
}
Today I faced similar problem. Previous answer helped be solve problem 50%. It was null pointer exception as mThreadReference is null.
Now you need an instance of the enclosing class in order to instantiate the inner class which is AsyncTask class. Since I had such class defined inside my fragment, what I did was below:
MyFragment fm = new MyFragment();
MyFragment.AsyncTaskClassName aTCN = fm.new AsyncTaskClassName();
After that you can call doProgress method the way Gianlunca has suggested.
My 2 cents !!

Android how to runOnUiThread in other class?

In my application, in MainActivity, there is a thread which works fine. But when I call another class to get data from the server I can't run on a thread. See code example below.
class MainActivity extends Activity implements Runnable {
public void onCreate() {
new Thread(this).start();
}
public void run() {
//here is code for download data from server after completion this and in handler i m call other class in setdata() method....
}
public void setData() {
new CheckData(this);
}
}
class CheckData {
public CheckData(Context context) {
context.runUIonthread(){//cant call as runUIthread............
}
}
See the article Communicating with the UI Thread.
With Context in hand, you can create a Handler in any class. Otherwise, you can call Looper.getMainLooper(), either way, you get the Main UI thread.
For example:
class CheckData{
private final Handler handler;
public Checkdata(Context context){
handler = new Handler(context.getMainLooper());
}
public void someMethod() {
// Do work
runOnUiThread(new Runnable() {
#Override
public void run() {
// Code to run on UI thread
}
});
}
private void runOnUiThread(Runnable r) {
handler.post(r);
}
}
Here's a solution if you don't want to pass the context:
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
// code goes here
}
});
Activity is a class that extends Context. So there is no need to pass both context and activity. You may pass activity as context and then you can use the context to run on UI thread as follows:
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
//Code goes here
}
});
Word of Caution: Only use this when you're sure that context is an activity context, and it's not a good practice to assume that.
class MainActivity extends Activity implements Runnable{
public void oncreate(){
new Thread(this).start();
}
public void run(){
//here is code for download data from server after completion this and in handler i m call other class in setdata() method....
}
public void setdata();
{
new checkData(this,MainActivity.this);
}
}
class checkData{
public void checkdata(Context context,MainActivity mainactivity){
mainactivity.runUIonthread()..is works fine for me.....
}
}
You might want to take a look at AsyncTask. Even though it's not the best solution, it will help you get started.
http://developer.android.com/reference/android/os/AsyncTask.html
EDIT
I don't see why using an AsyncTask is not a solution for you but anyway. You can hold a Handler class that is initialized in the UI thread. Then using this Handler you can post back messages to the UI in the form of a runnable. So all you need to do is instantiate a new Handler object when you are in the UI thread (before you start your new one) and then share that with your other class. When you are done, you can use that instance to post a message back to the UI thread using the post method. Check out the documentation of the Handler class for more details:
http://developer.android.com/reference/android/os/Handler.html
If someone's looking for an Rx based solution:
Observable.just(true)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aBoolean -> {
// cool stuff comes here
});

Categories

Resources