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;
Related
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?
I've read well enough on the subject to get well and thoroughly confused. I'm doing a workout tracker app such as RunKeeper, which tracks how long and how far the user has been say, running. I'm in the process of programming the main interface of this "workout" which shows a stopwatch-like layout which includes both a timer showing the time which is updated every second, and a counter showing how far he's run so far.
My problem is updating the interface. I have a stopwatch class keeping track of the time, and I calculate the distance based on gps locations, but I'm at a loss to find the recommended way to run a continuous thread in the background that updates the ui on a fixed time rate.
As far as I've been able to tell, I should either be implementing the Handler.Callback interface in the activity class, or have a separate Callback object together with a Thread, but I'm sort of at a loss on how to get it all to work together. I both need to update the time shown that I get from my stopwatch class (simple start/stop time calculation, nothing thread-related), and the distance calculated based on the locations received in onLocationChanged().
Here's a stripped down version of my Activity code:
public class WorkoutActivity extends Activity implements OnClickListener, LocationListener
{
private LocationManager locManager;
private boolean workoutStarted = false;
// The TextViews to update
private TextView timeLabel;
private TextView distanceLabel;
private Stopwatch stopwatch;
#Override
public void onCreate(Bundle savedInstanceState)
{
/*
... Interface initialisation
*/
stopwatch = new Stopwatch();
}
private void startWorkout() {/* ...*/}
private void stopWorkout() {/* ...*/}
private void pauseWorkout() {/* ...*/}
private void resumeWorkout(){/* ...*/}
public void onClick(View v) {/* ...*/}
public void onLocationChanged(Location location){/* ...*/}
}
According to most answers I've read around here I should use a Handler object extending the handleMessage method, but nowadays (at least according to the warning in Lint) you have to make such objects static to avoid memory leaks. That however, makes it a bit strange to directly access and change the other private objects in the activity class from within the Handler. I suppose you could solve this by making the objects you need to affect parameters in a class extending Handler, but I dunno, it feels like a bit of a "hack" (nested class within WorkoutActivity):
static class TimeHandler extends Handler
{
static final int MSG_START_TIMER = 1;
static final int MSG_UPDATE_TIMER = 2;
static final int MSG_STOP_TIMER = 3;
private static final int REFRESH_RATE = 1000;
private Stopwatch stopwatch;
private TextView timeLabel;
public TimeHandler(Stopwatch stopwatch, TextView label)
{
this.stopwatch = stopwatch;
this.timeLabel = label;
}
#Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch(msg.what)
{
case MSG_START_TIMER:
stopwatch.start();
this.sendEmptyMessage(MSG_UPDATE_TIMER);
break;
case MSG_UPDATE_TIMER:
timeLabel.setText(stopwatch.getElapsedTimeString());
this.sendEmptyMessageDelayed(MSG_UPDATE_TIMER, REFRESH_RATE);
break;
case MSG_STOP_TIMER:
this.removeMessages(MSG_UPDATE_TIMER);
stopwatch.stop();
timeLabel.setText(stopwatch.getElapsedTimeString());
break;
}
}
So, could anyone please explain the "standard" way of updating the UI from a continuous background thread? Oh, and sorry for the long question, just wanted to be thorough.
Use runOnUiThread().
Define a Runnable:
private Runnable mRunnable = new Runnable() {
#Override
public void run() {
mTextViewOne.setText("10 miles!");
mTextViewTwo.setText("40 minutes!");
}
};
Inside your continuous thread:
runOnUiThread(mRunnable);
USe the following thread in AsyncTask... I hope this will work for you ....
Handler mHandler;
private final Runnable mRunnable = new Runnable() {
public void run() {
Call your AsyncTask Class;;;;
mHandler.postDelayed(mRunnable, 1000);
}
};
I'm doing a manipulation in database in an IntentService, and in the Activity im showing a CustomProgressBar, but I want to show also the percentage value. So, for that I get the percentage value in the IntentService and set this value in a static method in the Activity. But the error show up:
CalledFromWrongThreadException: Only the original thread that created
a view hierarchy can touch its views.
I dont want to do this operation in an AsyncTask becaus I don't want to block the UI, so I'm using a IntentService.
Here is how I am doing this.
MyIntentService.java
public class MyIntentService extends IntentService
{
#Override
public void onHandleIntent(Intent intent)
{
updateDatabase();
}
public void updateDatabase()
{
resetPercentage(cursor.getCount * 2)
do
{
// do operation for updating the database
// here I update the view everytime a new item is inserted in DB.
int updatedReturn = MyActivity.updatePercentageValue(percentage());
}
while(...)
}
public void resetPercentage(int elementsNum)
{
mUpdatePercentage = 0;
mMaxItems = elementsNum;
}
public int incrementPercentageCounter()
{
return ++mPercentageCounter;
}
public int percentage()
{
int value = (mPercentageCounter/mMaxItems)*100;
return (value > 100) ? 100 : value;
}
}
MyActivity.java
public class MyActivity extends Activity
{
private TextView mMyTextView;
#Override
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
mMyTextView = (TextView) findViewById(R.id.textview);
}
public static int updatePercentageValue(int percentageValue)
{
mMyTextView.setText("" + percentageValue + " %");
return 1;
}
}
Any Idea of how can I solve this problem or do this operation using my IntentService so the UI wont be blocked.
Obs: I want to keep using IntentService also because a lot of things is done, and I dont want to change all over again.
Thanks!
An AsyncTask won't block the UI. You should probably use it. The kind of thing you're trying to do is exactly what it's good for. The AsyncTask doesn't need to exist outside of your Activity, it's short-lived work (relatively), and your work needs to update this UI. An AsyncTask seems more appropriate than a Service here.
Doing things in an async task is specifically to not block the UI thread. It even has an on progress method to do exactly what you want to do wih updating the UI. Gven that you don't want to do that, but you need to look into the running method.
Looking at this code I am somewhat confused as to how it works since you are accessing a member variable in a static method.
Is it possible for a Runnable to return a value? I need to do some intensive work on an Editable and then return it back. Here is my mock code.
public class myEditText extends EditText {
...
private Editable workOnEditable() {
final Editable finalEdit = getText();
Thread mThread = new Thread(new Runnable() {
public void run() {
//do work
//Set Spannables to finalEdit
}
});
mThread.start();
return finalEdit;
}
...
}
So obviously my first problem is I'm trying to change finalEdit, but it has to be final in order to access it in and out of the thread, right? What's the correct way to do this?
In Java, a Runnable cannot "return" a value.
In Android specifically, the best way to handle your type of scenario is with AsyncTask. It's a generic class so you can specify the type you want to pass in and the type that is returned to the onPostExecute function.
In your case, you would create an AsyncTask<Editable, Void, TypeToReturn>. Something like:
private class YourAsyncTask extends AsyncTask<Editable, Void, Integer> {
protected Long doInBackground(Editable... params) {
Editable editable = params[0];
// do stuff with Editable
return theResult;
}
protected void onPostExecute(Integer result) {
// here you have the result
}
}
The following exists simply to point out the use of "pseudo-closures" in Java, as unsuitable as they may be in this case.
Java allows pseudo-closures through mutable-types stored in final variables.
See Java's pseudo-closures by Christopher Martin skip to the "Faking it" section. He introduces a mutable-type ValueHolder<T>:
private static class ValueHolder<T> {
T value;
}
...
final ValueHolder<Integer> i = new ValueHolder<Integer>();
...
i.value = 42;
Happy coding.
You realize that the thread keeps working past the end of the workOnEditable() function, right? If you want a synchronous response, get rid of the thread. If not, use a Handler to pass the data back to the main thread.
I have made 2 utility methods around Runnable that enable you to post a runnable and block until you get the result.
You can take a look here: https://github.com/Petrakeas/HVTHandler/
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();
}
});
}