Android While-Loop using Thread - android

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

Related

How to use android handler in a loop

I am building my first android application and I am trying to make a memory game. Anyhow, I need to make an array of buttons change color for 1 second and then return to its original color in order, for example: button1 changes to yellow, stays like that for 1 second then returns to gray, then button2 changes to yellow for 1 second then returns, and so on. I tried using the handler but it always works only after the last iteration, this is my code:
for (i = 0; i < 9; i++) {
buttonList.get(i).setBackgroundColor(Color.YELLOW);
runnable =new Runnable(){
#Override
public void run() {
buttonList.get(i).setBackgroundColor(Color.GRAY);
}
};
handler.postDelayed(runnable,1000);}
what am I doing wrong?
EDIT
Found How to do it. First I need to make a runnable class that takes paramaters ex MyRunnable implements Runnable (using Runnable interface), then writing a method that uses this paramater, I can't do it with the regular one because it depends on i and i changes with the iteration.
You need to create a new Runnable inside each loop because all 9 delayed posts are running the same runnable that you create on the 9th and final loop since the loop no doubt takes less than a second to complete. So try something like this:
for (i = 0; i < 9; i++) {
buttonList.get(i).setBackgroundColor(Color.YELLOW);
Runnable runnable = new Runnable(){
#Override
public void run() {
buttonList.get(i).setBackgroundColor(Color.GRAY);
}};
handler.postDelayed(runnable,1000);
}
You're synchronously (at the same time) setting all buttons' colors to yellow, and also creating 9 asynchronous tasks (one for each button) to change color to gray after one second. It means all buttons will change colors back to gray after around 1 second, (more or less) at the same time.
Think of the handler as a queue that you add tasks to. The call postDelayed() is scheduling your tasks to be executed in the future, but all of them are scheduled at the same time, so all of them will be executed at the same time in the future.
I haven't run it, but I think this approach is more of what you are looking for:
// Those are fields
private int buttonIndex = 0;
private boolean yellow = false;
private final Handler handler = new Handler(new Handler.Callback() {
#Override
public void handleMessage(Message msg) {
if (!yellow) {
buttonList.get(buttonIndex).setBackgroundColor(Color.YELLOW);
handler.sendEmptyMessageDelayed(0, 1000);
} else {
buttonList.get(buttonIndex).setBackgroundColor(Color.GRAY);
if (++buttonIndex < 9) handler.sendEmptyMessage(0);
}
yellow = !yellow;
}});
// Call this to start the sequence.
handler.sendEmptyMessage(0);
Note that I'm using sendEmptyMessage*() instead of post*(), but either approach could be used. Additionally, handler's messages (tasks) can have input parameters, so it'd be nice to use them.

Make views visible one by one; android

I'm having trouble with making views visible. As in: they do all appear, but at the same time, whereas I would like to show them with some delay in between. Currently I have the following code, which should make it more clear:
public void performExperiment (View v) {
Log.i(TAG, "Experiment has started on view: " + v);
final ArrayList<FocusPoint> permutation = new ArrayList<>(Arrays.asList(focusPoints));
Collections.shuffle(permutation);
for (FocusPoint fp: permutation) {
try {
Thread.sleep(1000);
fp.setVisibility(ImageView.VISIBLE);
//fp.invalidate();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Sleep failed");
}
}
}
The FocusPoint-class is an extension of ImageView in Android and the method is called upon a button-click.
What needs to happen is that all the views show up in a random order on the screen, with a second between them. This code however waits for 16 seconds (the amount of views is 16) and then shows all the views at once. I tried invalidating my the view to redraw it, and I also tried to take 'fp.setVisibility(ImageView.VISIBLE)' out of the try-catch block, but both didn't work. (Of which the latter obviously doesn't work, I didn't really expect that to work, but I'm getting really desperate :P) I have been searching for hours now, and none of the StackOverflow-pages and other fora/documentation had an answer for this problem. How can I make sure that the focuspoint draws, before the loop continues to the next?
Using Thread.sleep(ms) to delay the UI thread is a very dangerous idea, and as you can see, it doesn't lead anywhere. By blocking the thread for 16 seconds, you are effectively freezing the application for that period - including any redraws and other event handling that might happen during that time. Don't ever do that.
Instead, use a Handler and its postDelayed(Runnable, ms) method to schedule visibility changes in the future. Handlers work by adding messages to the event loop, so they don't disrupt normal behavior.
Check this modified version of your code:
private static final long FP_SHOW_DELAY_MS = 1000;
private Handler handler = new Handler();
public void performExperiment (View v) {
Log.i(TAG, "Experiment has started on view: " + v);
final ArrayList<FocusPoint> permutation = new ArrayList<>(Arrays.asList(focusPoints));
Collections.shuffle(permutation);
for (int i = 0; i < permutation.size(); i++) {
final FocusPoint fp = permutation.get(i);
handler.postDelayed(new Runnable() {
#Override
public void run() {
fp.setVisibility(View.VISIBLE);
}
}, (i + 1) * FP_SHOW_DELAY_MS);
}
}

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/

Generate android buttons with while loop in separate thread

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.

Pausing with handler and postDelayed in android

I'm very new to android programming so please forgive my noobie-ness. I'm trying to create a very simple activity that will have one TextView in the middle of the Layout and just have it switch to a different text every couple of seconds. For example, the TextView will say "text1", pause for a couple of seconds, then say "text2, and pause again. Eventually, I want to add more texts and have them all cycle one after another. I know this seems like a super simple thing but I'm mainly trying to learn about threads and handlers at this moment. Anyways, I've read up on how we should keep lengthy things off the UI thread to prevent an error so I thought I'd use a handler to simply switch between 2 texts on screen. Unfortunately, I can't get this to work. Here's some code:
public class MainActivity extends Activity {
String[] myarray = {"text1" , "text2"};
int arraylength = myarray.length;
int count;
Handler handler = new Handler();
TextView mytexts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mytexts = (TextView)findViewById(R.id.my_texts);
mytexts.setText(myarray[0]);
Thread t = new Thread( new Runnable(){
public void run() {
for (int count = 0; count < arraylength; count++){
handler.postDelayed(new Runnable(){
public void run() {
mytexts.setText(myarray[1]);
}
}, 7000);
}
}
});
t.start();
}
}
From what I can see in the logcat, the handler seems to run postDelayed one right after another (in my code's case, it does NOT wait 7 seconds with the postDelay to do another postDelayed). Also, I would like to make the 1 in "mytexts.setText(myarray[1]);" be the same as "count" in the for loop so it can be the same as one of the strings in the array but that gives me an error. I've been stuck on this for hours and other examples I've found online seem way too complicated for someone like me who mainly wants to get the basics down before I can tackle other things. Any help at all with any of this would be much appreciated. Thank you.
postDelayed is non blocking, meaning it would add it to a queue of I'll do this later. So what you are probably seeing is all text updates happening together at the 7th second. I say this because you are postDelaying from the onCreate method when in reality you probably want to do it from onResume or even onPostResume.
Also there is no reason to create a thread to add runnables to the post queue. Your code should look more like this:
(Note the time to delay multiplier)
#Override
protected void onResume() {
super.onResume();
for (int count = 0; count < arraylength; count++){
handler.postDelayed(new Runnable(){
#Override
public void run() {
mytexts.setText(myarray[count]);
}
}, 7000 * (count + 1));
}
}
This is because your loop is setting all your handlers to run after 7 seconds not 7 seconds after each other but but after 7 seconds from now. You can either add in the postDelayed method or use the postAtTime method in handler .
Also, you don't need to do this in a thread, you can get rid of that altogether.

Categories

Resources