Reputation: 140
I spent a while trying to find out why my emulator keeps freezing, turned out to be because of a while loop I had introduced. After button is clicked, a random number (LoadG1) is outputted then disappears after 4 seconds. User then inputs a number, if it's equal to loadg1 they get a point and program is looped by having another loadg1 available for 4 secs before disappearing etc...
I can't have the loop unless it's its own thread apparently. I made a thread for it and then tried. The whole app freezing after button press didn't happen, but when the loadg1 was up for 4 seconds, the app shut down. It's as if the first line of loop worked, then failed after the 4 seconds was over. I received the following error:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
After querying this I saw that I need to put it in a runOnUiThread, so I did, only to encounter the same issue. Here's my code:
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Random;
public class game1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game1);
final Button loseStarter1;
loseStarter1 = (Button) findViewById(R.id.Starter1);
loseStarter1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loseStarter1.setVisibility(View.GONE);
final int[] score = {0};
Random generateG1 = new Random();
final int loadG1 = generateG1.nextInt(1000000)+10000;
final TextView number = (TextView) findViewById(R.id.number);
number.setText(" "+loadG1);
new CountDownTimer(18000, 1000) {
@Override
public void onTick (long millisUntilFinished) {
}
public void onFinish() {
TextView result = (TextView) findViewById(R.id.outcome);
result.setText("Score: "+ score[0]);
TextView prompt = (TextView) findViewById(R.id.prompt);
prompt.setVisibility(View.GONE);
}
}.start();
runOnUiThread(new Runnable() {
@Override
public void run() {
Runnable myRunnable = new Runnable() {
@Override
public void run() {
while (true) {
SystemClock.sleep(4000);
number.setVisibility(View.GONE);
final TextView prompt = (TextView) findViewById(R.id.prompt);
prompt.setText(" Enter the number");
final EditText input = (EditText) findViewById(R.id.enterAnswer);
input.setVisibility(View.VISIBLE);
input.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
Editable answer = input.getText();
int finalAnswer = Integer.parseInt(String.valueOf(answer));
int finalLoadG1 = Integer.parseInt(String.valueOf(loadG1));
input.setVisibility(View.GONE);
prompt.setVisibility(View.GONE);
if (finalAnswer == finalLoadG1) {
score[0]++;
}
return true;
default:
}
}
return false;
}
});
}
}
};
Thread myThread = new Thread(myRunnable);
myThread.start();
}
});
}
});
}
}
I felt like I was so close to resolving this issue, would greatly appreciate all help given.
EDIT: I have tried an alternative solution without a loop, code here:
runOnUiThread(new Runnable() {
@Override
public void run() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(4000);
number.setVisibility(View.GONE);
final TextView prompt = (TextView) findViewById(R.id.prompt);
prompt.setText(" Enter the number");
final EditText input = (EditText) findViewById(R.id.enterAnswer);
input.setVisibility(View.VISIBLE);
input.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
Editable answer = input.getText();
int finalAnswer = Integer.parseInt(String.valueOf(answer));
int finalLoadG1 = Integer.parseInt(String.valueOf(loadG1));
input.setVisibility(View.GONE);
prompt.setVisibility(View.GONE);
if (finalAnswer == finalLoadG1) {
score[0]++;
}
return true;
default:
}
}
return false;
}
});
}
});
}
}, 0, 4000);
}
});
Now the random number is generated and displayed for 4 seconds, but when user input is made available the app freezes. Apparently 238 frames are skipped (I believe every four seconds this is shown), and the 'main thread' is doing too much work. NOTE: EVEN WITHOUT THE RUNONUITHREAD ENCAPSULATING MY CODE, THE SAME OUTCOME OCCURS. I TRIED BOTH WAYS WITH NO DIFFERENCE FOUND.
Would honestly appreciate anyone who can help me resolve this.
Upvotes: 0
Views: 414
Reputation: 2606
don't do while(true)
with UI operations, this is why you have stuck, Even more don't use while(true) at all - this is bad practice.
if you need to run some repeat operations use timer for this, you can start/stop timer, you can start another timer, even several in one time )
Don't make UI operations in threads, make them only on UI thread.
for hard operations use async tasks - and when all hard operations done then update UI. (in async task you can update UI by publishing progress), on stackoverflow tons of samples for async tasks.
OnKeyListener
- runs on non UI thread, but please read above and avoid using while(true)
update to answer:
okay if you want to run like this and dont want to have lags then do following:
create timer - no need to start it in runOnUIThread
. all codes with calculations move ti async task which you will start in timer, in async task before run - stop timer to avoid run again, make all calculations in async task, on end async task - update ui and (or you can update on publish progress) and on end of async
task you can start your timer again. if you need delay before next run use Handler
and postDelayed
Upvotes: 1