I have a problem with my Thread, the Thread doesn't start, I don't have any idea why it doesn't run.
This is my code,
public class MainActivity extends AppCompatActivity {
TextView tv;
int seg=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=(TextView)findViewById(R.id.reloj);
}
boolean on=false;
public void inicia(View view){
if(!on){
tiempo.start();
on=true;
}
}
Thread tiempo=new Thread(){
public void run(){
try{
while(true){
Thread.sleep(1000);
seg++;
tv.setText(seg+"");
}
}catch (InterruptedException e){
}
}
};
}
prints this error
Only the original thread that created a view hierarchy can touch its views.
Well first off I'm assuming you set an onClick in you xml to set inicia to be the click handler for the button. Otherwise, that's part of your problem.
You thread is in an infinite loop. The while loop would only exit if an interrupt occurred for some reason, and there's nothing that would do that. If you want to post accio every second, the post needs to be inside the while loop.
while(true){
Thread.sleep(1000);
}
The thread is sleeping all the time.
Ok lets try,
Thread will be created from the main looprer thread. So, Your textview will be updated only by mainlooper thread,. When you create the thread, it will start become another thread. it is not related with MainLooper thread, You have to use Handler class to update the textView or use Asynctask or runOnUiThread method to update your text view.
runOnUiThread(new Runnable() {
#Override
public void run() {
textview.settext("");
}
});
Related
I get some error. I really couldn't solve it today :( I get error after set ID data to lblID in FillData() method. It sets ID data properly but lblTitle and lblPrice always returns error like "Only the original thread that created a view hierarchy can touch its views" and program stops running.
Note : This is not my original code. I just minimized it to be more understandable and of course it gives same error like below code. Anyway in FillData() method i get data from wcf service and it returns data properly. i tried runonuithread but it didn't make any sense. Also if i write the code outside of the thread it doesn't fill the controls. Because it's originally gets the data from wcf service.
public class MainActivity extends AppCompatActivity {
LinearLayout lytData;
TextView lblTitle, lblID, lblPrice;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lytData = (TextView)findViewById(R.id.lytNewData);
lblID = (TextView)findViewById(R.id.lblID);
lblTitle = (TextView)findViewById(R.id.lblTitle);
lblPrice = (TextView)findViewById(R.id.lblPrice);
new Thread() {
public void run() {
FillData();
}
}.start();
lytData.setOnTouchListener(new OnCustomTouchListener (context) {
#Override
public void ToLeft() {
new Thread() {
public void run() {
FillData();
}
}.start();
}
#Override
public void ToRight() {
new Thread() {
public void run() {
FillData();
}
}.start();
}
});
}
void FillData() {
lblID.setText("aaa");
lblTitle.setText("aaa");
lblPrice.setText("aaa");
}
The problem is you're trying to update the UI in another thread, but the UI can only be updated in the UI thread. If you're simply updated the UI as your code is showing then you should remove the calls from FillData from the secondary thread, use a secondary thread if you're doing heavy loading inside FillData() otherwise you're better off updating the UI directly in the UI thread:
So instead of doing this:
new Thread() {
public void run() {
FillData();
pd.cancel();
}
}.start();
Just simply call FillData(); outside the new thread.
You can also call runOnUiThread to bring the update to the ui thread:
getActivity().runOnUiThread(new Runnable() {
public void run() {
FillData();
}
});
If your code inside FillData is mixed with heavy load code, then you can bring the runOnUiThread method to inside the FillData and move only the UI update code to runOnUiThread.
If you still want to keep your code the way it is you can "post" changes from your secondary thread like this:
viewElement.post(new Runnable() {
public void run() {
//update UI
}
});
}
viewElement is any UI element that extends from View.
I was testing this code to check if app crashes for changing ui component from background thread. but it didn't.
Here in the code added below. I started a new thread in onCreate() method of MainActivity and It should have crashed as per the android docs which says
In the class, the Runnable.run() method contains the code that's
executed. Usually, anything is allowable in a Runnable. Remember,
though, that the Runnable won't be running on the UI thread, so it
can't directly modify UI objects such as View objects.
So I was expecting it to crash. Which it didn't. See code -
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
#Override
public void run() {
txt.setText("bro");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
thread.start();
}
}
While if i try changing ui while starting thread from onClicklistener() as below it does crash. which is expected.
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
#Override
public void run() {
txt.setText("bro");
}
};
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
thread.start();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.setOnClickListener(listener);
}
}
Now, that the second code snippet crashes, which is expected and the first one doesn't.
Please explain why is this happening, as I'm creating a new worker thread each time but just at different places. Official docs reference will be appreciated.
I found the reason behind this behavior as pointed out by #krish in the comments on my question. The reason is that, the thread was able to make changes in the TextView object only till it was not visible on UI screen i.e not rendered. It is only after the view rendering, that any thread except the Main thread may not make changes to any UI components. I Tried using view observer to see if the view was rendered before the changes or not. which showed that changes were made before the view rendering.
Here is the code that i tried.
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
#Override
public void run() {
txt.setText("bro");
Log.d("ThreadTest", "The Text was changed.");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Log.d("ThreadTest", "The TextView was rendered");
}
});
}
#Override
protected void onResume() {
super.onResume();
thread.start();
}
}
Using the code above. You'll see in the output:
The Text was changed.
The TextView was rendered
Which means text was changed before view rendering. if you try to start thread to makes changes in onGlobalLayout method. App crashes as it should.
The UI is not thread-safe see processes-and-threads, so you were just lucky you did not hit one of the many landmines waiting for you.
If you don't like relying on luck then:
You should be using:
runOnUiThread(runnable);
instead of:
thread = new Thread(runnable);
AsyncTask enables proper and easy use of the UI thread.
Edit: It was suggested that it would be helpful to add my code so, my AsyncTask code is now pasted below...
I'm just learning Android and I have a UI with a few buttons. I want to animate the UI, changing the color of the buttons, in a sequence.
I shouldn't do that from the main thread of course, and it doesn't work anyway. The code manipulating the UI runs but the UI doesn't update until the end of the sequence.
So I created a thread and tried to run through the sequence from a background thread however, I would get an error trying to manipulate the UI components from the background thread. Only the main thread can touch the UI components.
Then I discovered AsyncTask. What I figured was, I could run through the sequence in doInBackground(). Every time I needed to update the UI I'd call publishProgress() which would cause onProgressUpdate() to be called from the main thread so I could access UI components without error.
Every time I call publishProgress() I would follow it with a SystemClock.sleep(500) to let time pass until the next animated UI update.
What I found though was that doInBackground() would run through the 4 UI state changes in about 2 seconds (500 ms each) but the UI would not update with each call to publishProgress(). Instead doInBackground() completes and then onProgressUpdate() is called 4 times in a row.
From the description, publishProgress & onProgressUpdate are designed to update a progress bar as doInBackground cranks through some longish running task so, obviously, onProgressUpdate must execute multiple times before doInBackground completes, right?
Am I missing something?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startGame(View view) {
MyTask task = new MyTask();
task.doInBackground();
}
class MyTask extends AsyncTask<Void,Void,Void> {
private int current_int;
#Override
protected Void doInBackground(Void... voids) {
this.current_int = 1;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
this.current_int = 2;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
this.current_int = 1;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
this.current_int = 2;
Log.e("doInBackground","light up button "+this.current_int);
publishProgress();
SystemClock.sleep(500);
return null;
}
#Override
protected void onProgressUpdate(Void... voids) {
super.onProgressUpdate(voids);
Log.e("onProgressUpdate","Updating button "+this.current_int);
Button btn1 = (Button) findViewById(R.id.button1);
Button btn2 = (Button) findViewById(R.id.button2);
if (this.current_int==1){
btn1.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
btn2.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_dark));
} else {
btn2.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
btn1.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_dark));
}
}
}
}
Just for reference : An Asynctask presents a systematic way to transition from main thread (calling thread) to the new thread (called thread). The onPreExecute() and onPostExecute() methods execute on the calling thread and the doInBackground() is the actual method executing on the new thread. Doing UI updates on main thread will hence lead to exception if done from doInBackground() method.
Your core background logic should hence be placed in the doInBackground() method.
If you want to update UI from background thread (Asynctask or otherwise), you can do it using this :
YourActivity.this.runOnUiThread(new Runnable(){
#Override
public void run()
{
//UI update operations here
}
});
You can use Handler for this,
public class TestClass extends AsyncTask<Void,Void,Void> {
boolean isRunning = true; //set false after executing UI logic.
Handler mHandler = new Handler();
#Override
protected Void doInBackground(Void... params) {
YourFunctionToUpdateUI();
return null;
}
public void YourFunctionToUpdateUI()
{
new Thread(new Runnable() {
#Override
public void run() {
while (isRunning) {
try {
mHandler.post(new Runnable() {
#Override
public void run() {
// your code to update the UI.
}
});
} catch (Exception e) {
//handle exception
}
}
}
}).start();
}
}
I'm an idiot. The problem is, I'm creating an AsyncTask and then calling doInBackground() directly, from my main thread instead of calling execute() which creates the background thread
I have made an android app, which has two TextViews. When I change the text using setText() then the app stops.
I have read other answers on StackOverflow and have implemented them in my program, but the problem still persists.
Here is my onCreate() code:
TextView ec, vc;
Thread t;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ec = (TextView) findViewById(R.id.ce);
vc = (TextView) findViewById(R.id.cv);
t = new Thread(new Runnable() {#Override public void run() {loop();}});
t.start();
}
Here is my loop() code:
public void loop()
{
try{Thread.sleep(7000);}catch(Exception e){}
ec.setText("");
vc.setText("");
try{Thread.sleep(53000);}catch(Exception e){}
t.stop();
}
I want to empty the text of both the TextViews after 7 seconds.
But when I run my app, after 7 seconds, it stops.
Other answers on StackOverflow say that I should put setContentView(R.layout.activity_main); before I initiate the ec and vc variables. It is already there, but still the app stops.
Please help me.
You can use the View.postDelayed method to run a Runnable on the UIThread after a certain time.
UI Elements can only be changed from the UI (Main) thread, therefore it breaks down. Try using:
activity.runOnUiThread(new Runnable() {
public void run() {
//Do something on UiThread
}
});
But you'll make the UI thread sleep, which means it'll freeze up any way. So, bad practice.
try that
public void loop()
{
try{Thread.sleep(7000);}catch(Exception e){}
activity.runOnUiThread(new Runnable() {
public void run() {
ec.setText("");
vc.setText("");
}
});
try{Thread.sleep(53000);}catch(Exception e){}
t.stop();
}
I have an activity class as below.
public class LoginActivity extends Activity implements OnClickListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 = (ImageView) findViewById(R.id.button1);
button1.setOnClickListener(this);
}
#Override
public void onClick(View v) {
loader = (ProgressBar) findViewById(R.id.loader);
Thread processThread = new Thread(loaderThread);
loader.setVisibility(View.VISIBLE);
processThread.start();
try {
Thread.currentThread().join();
Log.i("Activity","gone past join()");
loader.setVisibility(View.INVISIBLE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Runnable loaderThread = new Runnable() {
public void run() {
ServiceCaller serviceCaller = new ServiceCaller();
boolean status = serviceCaller.checkProcess(url);
}
};
}
Further Question [EDITED]
Here is the scenario. The main activity class creates a thread on a click. The then created thread fetches some data from the server. It is a time consuming task. So a progress bar is displayed on the UI. Currently I am using AsyncTask (not shown here) to accomplish server data retrieval. But the real challenge is wait for the background task to complete and get the value from it. What I am looking for is:
wait until server calls are made and get the results. Meanwhile show the progress bar.
Any thoughts? Apologies in case I confuse you.
Thanks in advance!
You must have a look at AsyncTask
http://developer.android.com/reference/android/os/AsyncTask.html
http://www.vogella.com/articles/AndroidPerformance/article.html
and you can show the ProgressBar in onPreExecute()
do the task in doInBackground()
and hide the ProgressBar in onPostExecute()
Join method blocks the current thread. In your case Onclick method is called in UI thread, so all UI operations are blocked. It is never a good idea to block Ui thread.
So you probably should use either a Handler or Asynctask to keep updating Progressbar