0

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

kasf
  • 113
  • 1
  • 2
  • 11
  • possible duplicate of [How to use an Android Handler to update a TextView in the UI Thread?](http://stackoverflow.com/questions/15685752/how-to-use-an-android-handler-to-update-a-textview-in-the-ui-thread) – Michael Jul 23 '14 at 11:32
  • _"i have heard that using threads in background will consume toooooo much of the device CPU"_. That's kind of like saying "I've heard that it can get too cold in the winter". While that may be true in some circumstances, it depends on a lot of factors. It's certainly better to place heavy computations in other (non-UI) threads if you want to avoid ANRs and the like. – Michael Jul 23 '14 at 11:36
  • 1
    @Ordous: The answer to the question I suggested duplicating to describes a solution to the problem the OP for this question is asking about (a solution I've used myself in the past). I'd say it has a lot to do with this question. – Michael Jul 23 '14 at 11:39
  • The thread you posted would be very helpfull if i wanted to update the android ui only once ,but my thread updates the ui every 1 second or less ! How can i achive changing the coordinates of my button using the info in the thread you posted every second ??? – kasf Jul 23 '14 at 11:54
  • @kasf By posting it after you've let your thread 'sleep', within the loop.. – Daneo Jul 23 '14 at 11:56
  • @Michael Right, I've misunderstood what a `Handler` is, thought it was already on the right thread. – Ordous Jul 23 '14 at 12:02
  • @Daneo i cant understand what you are saying and how to do it ! Could you please provide an example ? i have searched it alot and i only find complex examples that i cant use to understand how to do it when a while-loop takes place in the external thread ! The button will move continuesly meaning that the handler should continusly accept parameneters and thus updating the UI ! i cant understand how to do this though ... I would really appreciate an example ! thank you – kasf Jul 23 '14 at 14:08

2 Answers2

2

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 :

Community
  • 1
  • 1
Daneo
  • 508
  • 3
  • 17
  • thank you very much :) with slight modifications it really works and i got to understand how things work ! thank you again – kasf Jul 23 '14 at 21:59
  • @kasf Glad to hear. Bear in mind, that the reason your UI is not hanging now, is that you're doing a `SystemClock.sleep()` on the thread, right after you give the command to update the UI. So you're basically waiting after updating the UI. Should you remove the `SystemClock.sleep()` then you'd be updating the UI at the same speed the thread works. Which (about) comes down to doing the work on the UI thread, and making it hang again. Glad I could be of assistance! – Daneo Jul 24 '14 at 07:47
0

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

Ordous
  • 3,844
  • 15
  • 25