Android: can't access view from within Thread - android

I am trying to write a simple app example that will run through a for loop incrementing its counter by 1 each time and then use the current value of the counter i to update the view and print out:
"i = #"
I get an error saying you can't update a view that was not created in that thread. i tried to address this by inflating the view from within the thread and also by creating a new TextView and calling "setContentView(myTextView)". but neither of these fixed the problem.
I tried a different version that used an AsyncTask but I got stuck on how to divid up the code into each of AsyncTask's methods. could someone help me see how to do this as it has shown me I am missing in my understanding on this point.
Thanks
Edward
ps. the commented out lines for the inflater and the setContentView are from my attempts to fix it.
my code from my original attempt that is trying to update my TextView "myTextView" in the main layout for the app:
public void loopForever(View view) {
Thread thread = new Thread(){
public void run(){
// LayoutInflater inflater = null;
// inflater.inflate(R.layout.activity_main, null);
TextView myTextView;
myTextView = (TextView) findViewById(R.id.myTextView);
// setContentView(myTextView)
for(int i = 0; i < 1000; i++){
myTextView.setText("i = " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}

You can only access View elements from within the UI Thread (Activities, fragments etc. callbacks). You could either switch to an Asynctask and do the UI changes via the postexecute or publish progress callbacks (http://developer.android.com/reference/android/os/AsyncTask.html), or use runOnUiThread, example:
public void loopForever(View view) {
Thread thread = new Thread(){
public void run(){
for(int i = 0; i < 1000; i++){
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
(TextView) activity.findViewById(R.id.myTextView).setText("i = " + i);
}
});
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}

As the other users have stated, you cannot update user interface elements from any thread other than the main thread. You should use the view class post() or postDelayed() method, your code should look something like this:
for(int i = 0; i < 1000; i++){
myTextView.postDelayed(new Runnable(){
public void run(){
myTextView.setText("i = " + i);
}
}, 1000);
}
here is a link:
http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable)
You might also want to take a look at android async task class
http://developer.android.com/reference/android/os/AsyncTask.html

You must update User Interface (UI) elements, like your TextView, from the "UI Thread". You cannot update them from other threads such as the one you have made.
This Android lesson might be useful to read:
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. To move data from a background thread to the UI thread, use a Handler that's running on the UI thread.
If you use AsyncTask, you can override the onProgressUpdate(Progress...) method to update your TextView. onProgressUpdate(Progress...) deliberately runs on the UI thread to allow this.

Related

Running a Method for a Certain Number of Time

So I have this method called PredictionEngine(int) that I want to run a certain number of time with a certain time-delay between each run. The method goes like this:
private void PredictionEngine(int delay) throws Exception {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
enableStrictMode();
String val = null;
try {
if (tHighPass == 0 && tLowPass == 0 && tKalman == 1) {
//Magic
} else {
//Magic
}
} catch (Exception e) {
e.printStackTrace();
}
enableStrictMode();
new DropboxTask(side_output, "Result", val).execute();
}
}, delay);
}
As obvious, I am running a network operation in the main thread as this is a research app and no client is ever going to use it.
I want this whole function to run for say a 100 times with a certain delay, say 2 seconds. The initial thought was to do this:
for(loop 100 times){
PredictionEngine(int)
Thread.sleep(2000); //sorry for StackOverflow programming.
}
However I don't want to block the main thread as I am reading some sensor data there. Any ideas for the same would be very helpful!
Thanks.
The best way to solve this is by using rxJava library, because it allow to create, modify and consume streams of events. You can implement everything in a few lines of code and modify it so operatioin will be performed in background as well.
Observable.interval(1, TimeUnit.SECONDS)
.take(100)
// switch execution into main thread
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(t -> {
doSomethingOnMainThread();
});
On the other hand, there is another solution- you can use Handler, which is usually bein used for thread communication. It has method .postDelayed() allowing you to postpone execution of task. Handler can be conveniently used along with HandlerThread. But, rxJava is more convenient and simple way to solve your problem.
While creating your Handler, you can provide a looper as one of the constructors parameters that is based on different thread then the main thread:
HandlerThread thread = new HandlerThread("Thread name", android.os.Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
Looper looper = thread.getLooper();
Handler handler = new MyHandler(looper);
Messages received by MyHandler will be processed on a separated thread, leaving the UI thread clear from interferences.
To loop on the task periodically, use something like:
for (int i=0; i<100; i++){
handler.postDelayed(new Runnable(){
...
...
...
}, i*delay);
}
This way, in case you decide that the periodic tasks need to be canceled, you will always be able to invoke:
handler.removeCallbacksAndMessages(null);
I tried to solve the issue as follows without blocking the main Thread
I created the worker thread for looping and still running the predictionEngine() on main thread
MyThread t = new MyThread(2000, 3000); // delay and sleep
t.startExecution();
Worker thread class looks as follows
class MyThread extends Thread{
private int delay;
long sleep;
MyThread(int delay, long sleep){
this.delay = delay;
this.sleep = sleep;
}
#Override
public void run() {
for(int i = 0; i < 100; i++){
try {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
predictEngine(delay);
}
});
Log.i("Mtali","About to pause loop before next predict");
sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void startExecution(){
start();
}
}
Hop this helps!

fix thread synchronisation in android

i have the code bellow to execute a simulation for an android application of a car but it seems that threads are not well synchronized how can i fix this
public void Simulation()
{
ambientTemp = 20;
engTemp = 20;
mileage = 123456;
fuel = 100;
thread = new Thread()
{
menu1_Fragment f1 = new menu1_Fragment();
menu2_Fragment f2 = new menu2_Fragment();
menu3_Fragment f3 = new menu3_Fragment();
public void run()
{
for (int i=0; i<l; i++)
{
try {
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
speed = SPEED[i];
revs = ENGSPEED[i];
System.out.println(speed);
System.out.println(revs);
fuel -= 1;
engTemp += 0.5;
mileage += 1;
gear = AMP[i];
time += 1;
if (tachoFrag != null && tachoFrag.isVisible())
{
View item1 = findViewById(R.id.progressBar4);
f1.setRevs(item1,revs);
f1.setSpeed(speed);
f1.setFuelGauge(fuel);
final View item2 = findViewById(R.id.milage);
final View item3 = findViewById(R.id.ambienttemp);
final View item4 = findViewById(R.id.gear);
try {
Thread.sleep(1);
runOnUiThread(new Runnable() {
#Override
public void run() {
f1.setMileage(item2,mileage);
f1.setAmbientTemp(item3,ambientTemp);
f1.setGear(item4,gear);
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container, f1);
transaction.commit();
}
});
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
i've added some println to make suure that the loop is working and it seems fine but the UI is not refreshing as it should to be... how can i fix that?
The code you have presented is really bad written for Android. All the synchronization is handled via Handlers and the results are posted to one or another thread. For example if you do new Handler() you are creatting a "Thread handler" for the current thread, which by default is the main thread.
If you call handler.post(myRunnable) you will be running something in the UI thread, but you can do similar things with other threads or looping threads.
Given that, if your problem is that the UI is not being refreshed in the moment you want, the reason could be that you are not "posting" the results to the UI thread in the correct moment. So before starting the thread, create a UI handler, and from your thread post the results. Remember that you are not allowed to perform any UI operation outside the UI thread.
Quick tip
From my experience, using Thread.sleep(n) is not a good idea for synchronization between threads, use traffic lights or messages between them.
Are you calling .start() on your thread? Also why do you try a one ms Thread.sleep ?
As far as I understand, you cannot update the UI thread from other thread. To do that you have to send message to UI main thread
this may help
Updating Android UI using threads

setText wont work in the thread

Hi i have this code to modify a text view but it keeps telling me : Only the original thread that created a view hierarchy can touch its views.
here is my code :
public void Simulation()
{
ambientTemp = 20;
engTemp = 20;
mileage = 123456;
fuel = 100;
thread = new Thread()
{
menu1_Fragment f1 = new menu1_Fragment();
menu2_Fragment f2 = new menu2_Fragment();
menu3_Fragment f3 = new menu3_Fragment();
public void run()
{
for (int i=0; i<l; i++)
{
try {
Thread.sleep(99);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
speed = SPEED[i];
revs = ENGSPEED[i];
System.out.println(speed);
System.out.println(revs);
fuel -= 1;
System.out.println(fuel);
engTemp += 0.5;
System.out.println(engTemp);
mileage += 1;
System.out.println(mileage);
...
View item2 = findViewById(R.id.milage);
// f1.setMileage(item2,mileage);
View item3 = findViewById(R.id.ambienttemp);
f1.setAmbientTemp(item3,ambientTemp);
View item4 = findViewById(R.id.gear);
f1.setGear(item4,gear);
transaction.replace(R.id.container, f1);
transaction.commit();
}
f1.setMileage(item2,mileage); this one is causing the probleme ... how can i fix it please
put all your codes related to a view inside a ui thread
runOnUiThread(new Runnable() {
#Override
public void run() {
View item2 = findViewById(R.id.milage);
// f1.setMileage(item2,mileage);
View item3 = findViewById(R.id.ambienttemp);
f1.setAmbientTemp(item3,ambientTemp);
View item4 = findViewById(R.id.gear);
f1.setGear(item4,gear);
transaction.replace(R.id.container, f1);
transaction.commit();
}
});
Your application must create other threads and put long running work on non-UI threads. There are options on how to accomplish the creation of alternate threads. You can create and start your own java.lang.Thread. You can create and start an AsyncTask - Android’s own thread simplification mechanism. The non-UI thread then handles long running processing – like downloading a file – while the UI thread sticks to displaying the UI and reacting to user events. Life seems good again.
However, there is a problem in paradise. Unfortunately, the user interface (UI) cannot be updated by non-UI threads. For example, after successfully downloading a file, a separate (non-UI) thread can’t show an AlertDialog, update a TextView widget, otherwise make a UI change to indicate the file has been successfully downloaded. If you attempt to update the UI from a non-UI thread, the application will compile, but you get a CalledFromWrongThreadException thrown from the point your non-UI thread attempts to make the UI change. As the exception message will inform you, “Only the original thread that created a view hierarchy can touch its views.”
for reference, click this link http://www.intertech.com/Blog/android-non-ui-to-ui-thread-communications-part-1-of-5/

Is it a bug or a feature? In some cases it is possible to access the UI thread from a task not running on the UI thread

The developer.android.com says:
Only objects running on the UI thread have access to other objects on
that thread.
That said, all the following examples (cases A..C) should not work since they try to modify an object in the UI thread. But in fact cases A and B do access the object (TextView) in the UI thread.
Here we start a new thread from the MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new ClientThread()).start();
}
Case A (the object in the UI thread is modified)
class ClientThread implements Runnable {
public void run() {
final TextView myTextView = (TextView) findViewById(R.id.myTextView);
myTextView.setText("Hello there!");
}
}
Case B (the object in the UI thread is modified multiple times)
class ClientThread implements Runnable {
public void run() {
final TextView myTextView = (TextView) findViewById(R.id.myTextView);
for (int i=0; i < 600; i++) {
myTextView.setText("Hello there!");
}
}
}
Case C (after a slight delay the object in the UI thread is not modified)
class ClientThread implements Runnable {
public void run() {
final TextView myTextView = (TextView) findViewById(R.id.myTextView);
try {Thread.sleep(900);} catch (InterruptedException e) {};
myTextView.setText("Hello there!");
}
}
Only the case C throws the exception:
CalledFromWrongThreadException: Only the original thread that created
a view hierarchy can touch its views.
Am I missing something? At the moment it seems that in some cases the UI thread can be modified from a thread not running on the UI thread (if it happens fast enough).
It is some kind of fun. You cannot update the UI in background thread. But in this case, UI is not drawn yet so it doesn't treat your code like update the UI but more like set the value. Anyway if you update it after the UI was shown, it will be treated like UI updating.

Android update UI from another thread [duplicate]

This question already has an answer here:
Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread
(1 answer)
Closed 9 years ago.
Good day,
I want to update an image button in my UI from another thread. below is my code that i run in my mains threads onCreate() method.
new Thread(new Runnable() {
public void run() {
ImageButton btn = (ImageButton) findViewById(R.id.connected_icon);
if (netConnection.IsConnected()) {
// Change icon to green
btn.setImageResource(R.drawable.green_small);
} else {
// Change icon to red
btn.setImageResource(R.drawable.red_small);
}
try {
// Sleep for a second before re_checking.
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
No when i run this i gen an error int he LogCat saying i cannot update the UI from annother thread.
I remember reading soem where once that this is the case so that you don't get multiple threads updating the same UI object at once. But how can i achieve this. i am sure there is a work around?
Thanks
You cannot directly acces UI components from the thread.
The correct way to do this is by creating a handler
final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
And send messages to UIThread with
Message msg = new Message();
//TODO: add stuff to message
mHandler.sendMessage(msg);
inside your Thread.
This or use an AsyncTask instead and do the updates from inside of pre, post or progressUpdate methods
UI Elements should be updated only from the UI thread. Use an async task to do background word, and modify the UI in onPostExecute, which runs on the UI thread

Categories

Resources