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/
Related
Yes, it shouldn't but the thread i made in my mainActivity in onCreate can modify UI, like this :
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
someTextview.setText("Hello");
}
});
thread.start();
I'm using android studio 2.2.2 .
Short answer: you can do it as long as you do it before the mainthread enter onResume()
Detail:
Usually you get a error like this when modifying UI from non UI thread
android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
and this exception is throwed here in the ViewRootImpl class
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
the mThread is the main thread. After the ViewRootImpl instance is created, UI modify opertations will need to check thread.
So when did the ViewRootImpl instance is created?
android.view.WindowManagerImpl.java
#Override
public void addView(#NonNull View view, #NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
android.view.WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
...
root = new ViewRootImpl(view.getContext(), display);
...
}
So when did the addView method is called?
android.app.ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
.
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
...
case RESUME_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
here you can see the ViewRootImpl instance is created around RESUME_ACTIVITY event, so before the main thread handles the resume event, you are allowed to modify UI from other threads. you just need to be quick.
It is not recommended to do that though.
Why non UI thread can modify UI?
Technically, a non UI thread can do that. But it is a really bad idea.
Why?
Because there are all sorts of problems with thread-safety if you do that. The UI framework is designed on the assumption that it can access the UI data structures without synchronization. (This simplifies things immensely, and removes the possibility of deadlocks between UI and non-UI threads ... which are next to impossible for a mortal programmer to anticipate.)
This can result in the worst kind of bugs; i.e. the ones that you can't test for, and you can't reproduce reliably ... or at all ... on your hardware.
The Android framework tries to protect you from doing this kind of thing to yourself ...
If you want to modify UI on Non-UI thread, you can do it via
Activity.runOnUIThread()
Runnable
View.Post(Runnable)
AsyncTask
In your code, you are using Activity.runOnUIThread()
Do a look at https://developer.android.com/guide/components/processes-and-threads.html
you can not update the UI from any thread other than the UI thread or the "main" thread.
only using :
Activity.runOnUIThread(runnable)
View.Post(Runnable), view.postDelayed()
AsyncTask
the important question is why your code is modifying the UI?
probably, you are starting the Thread from the onCreate(). if you tried to run this thread inside a Button onClick() for example, your app will crash throwing
CalledFromWrongThreadException
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
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.
im trying to make a thread that will run a While-loop and inside the while-loop actions that will change the UI should take place :
public void GameHandler()
{
layout = (RelativeLayout) findViewById(R.id.MyLayout);
params=new RelativeLayout.LayoutParams(30,40);
btn1 = (Button) findViewById(R.id.MovingObj);
xBounds=new int[2];
yBounds=new int[2];
xBounds[0]=0;
xBounds[1]=layout.getWidth();
yBounds[0]=0; //will change once i set up the screen settings !
yBounds[1]=layout.getHeight();
selectFlyingObject();
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (CurrentXLocation <= xBounds[1] + 10) {
CurrentXLocation = CurrentXLocation + 1;
params.leftMargin = CurrentXLocation;
params.topMargin = 50;
btn1.setLayoutParams(params);
SystemClock.sleep(1000 - SpeedRate);
}
}
};
Thread myThread = new Thread(myRunnable);
myThread.start();
}
However my first problem is that the following error occurs :
Only the original thread that created a view hierarchy can touch its views.
and the second problem is that i have heard that using threads in background will consume toooooo much of the device CPU, fact that may lead to my app-crash. Does something like that indeed happens ?
thank you for your answers
First off, I recommend reading this article, as the current construction you're using is kind of .. dirty ..
Anyway, in order to respond to the actual error you're receiving, it's obvious that you can't update the UI thread from a background thread.
Why ? Because if you could, imagine 10 different threads updating the UI at the same time .. That'd give buggy-looking applications, wouldn't it?
Also it would mean that every UI element would need to be locked, synchronised, in order to provide more or less consistent behaviour. This would defeat the purpose of having threads perform 'hard work', they'd become dependant on the UI ..
(If anyone can give a very clear reason / reference to a guideline and / or reason comment it and I'll adjust the answer)
So, in Android you can get a hold of the main Looper, a utility used to perform tasks in a sequential order, performed on a specified thread.
What we hereby do, is actually queueing our operations to be performed on the main thread.
public class GameHandler implements Runnable {
Handler mHandler = new Handler(Looper.getMainLooper());
Layout mLayout;
View mView;
int currentX, mSpeedRate;
public GameHandler(Layout layout, View viewToAnimate, int speedRate) {
mLayout = layout;
mView = viewToAnimate;
mSpeedRate = speedRate;
}
public void handleGame() {
final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(30, 40);
int[] xBounds = new int[2];
int[] yBounds = new int[2];
xBounds[0] = 0;
xBounds[1] = mView.getWidth();
yBounds[0] = 0; //will change once i set up the screen settings !
yBounds[1] = mView.getHeight();
selectFlyingObject();
while ( currentX <= xBounds[1] + 10 ) {
currentX = currentX + 1;
params.leftMargin = currentX;
params.topMargin = 50;
mHandler.post(new Runnable() {
#Override
public void run() {
mView.setLayoutParams(params);
}
});
SystemClock.sleep(1000 - mSpeedRate);
}
}
void selectFlyingObject() {
// No idea what you're doing here, but keep in mind you're still working on the
// background thread if you call this method from somewhere not wrapped in the mHandler
}
#Override
public void run() {
handleGame();
}
}
Disclaimer, this code is untested and from the top of my head. If it does not do what you think it should do, feel free to mention it.
The structure is adapted, what you have to do now is start the thread as you were attempting to, or use an Executor.
Thread myThread = new Thread(new GameHandler());
myThread.start();
In case I'm not clear enough on the subject, these links are for reference :
What is the purpose of Looper and how to use it?
http://developer.android.com/reference/android/os/Handler.html
"I have heard..." is not a particularly credible source of information. Using background threads is absolutely fine in Android. Spawning undying threads every second does lead to an app crash, just like making an infinite loop will - and with the same comments from fellow devs.
Your "problem" is self-explanatory - as each and every tutorial on Java concurrency in a UI toolkit states - DO NOT UPDATE UI FROM BACKGROUND THREADS. Use one of the numerous mechanisms to send a signal to the UI thread to do it. In your case - a Timer will fit nicely (it will wake up every x seconds and execute a runnable).
I'm lost here. I need to read the files in a directory and make buttons from them when an app starts. I have to use a while loop, and I have to update the UI. I've tried for quite a while to run a runnable and have only the code inside the loop run in the UI thread. I'm relatively new to android, but this seemed simple enough.
This code is what I have right now. It throws no errors or warnings, but it doesn't do anything. I know the button making code works because the "add button" button makes buttons correctly. I have no idea why it isn't working.
(This runs in OnCreate)
Runnable aRunnable = new Runnable() {
public void run() {
Looper.prepare();
File f = new File(Environment.getExternalStorageDirectory() + "/myapp/");
File[] filearray = f.listFiles();
int amount = filearray.length;
final String[] files = new String[amount];
int count = 0;
while (count != amount) {
files[count] = filearray[count].toString();
count += 1;
}
int times = files.length;
int counter = 0;
while (counter != times) {
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
// Button making code
}
});
}
Looper.loop();
}
};
Thread thread = new Thread(aRunnable);
thread.start();
One problem with your existing code is that everything in the run() method gets run on a background thread, not the main (sometimes called the UI) thread.
This is where you're creating your handler object:
Handler handler = new Handler();
This is not correct. You need to create (instantiate) the Handler on the main thread, if using the default constructor. From the Handler docs:
public Handler () Added in API level 1
Default constructor associates this handler with the Looper for the
current thread. If this thread does not have a looper, this handler
won't be able to receive messages so an exception is thrown.
So, a simple fix is simply to move that line of code earlier, into a place that you know is run on the main thread. For example, in Activity#onCreate():
public void onCreate(Bundle b) {
super.onCreate(b);
handler = new Handler();
}
where handler is changed to be a member variable:
private Handler handler;
Also, just remove your Looper calls.
See this article for more on Handler.
Other Options
Another option would be to avoid using Handler at all, and get famliar with the AsyncTask class. I personally think that's easier for new developers to use. The vogella.com link I showed also has good information on AsyncTask.
Yet one more option would be to avoid Handler and use the runOnUiThread() method in your Activity to add your buttons.
it seems to me that you're trying to have a list of buttons based on some array. It seems that is a job for a ListView and a custom array adapter. I believe you should google some examples on it (there're billions around the internet, it's a fairly basic android pattern).
and while you're in the learning process, don't forget to check how to use for() in Java, all those while loops are just so ugly.