Having trouble communicating between threads - android

My first attempt at both building a game and using multithreading is going mostly well, but I'm stuck at the moment.
It's a simple Whack a mole clone, so I've got a 3x4 grid of mole ImageViews declared in a layout.xml, then I'm using setContentView(R.layout.layout) to put it up, then a separate thread to make one of them appear for a second, then disappear. Here's my Activity's onCreate():
public class WAM_Activity extends Activity {
private static final int MAKE_VISIBLE = 1;
private static final int MAKE_INVISIBLE = 0;
private ImageView[] mole = new ImageView[11];
private ImageView currentMole;
private int[] moleId = {R.id.mole1, R.id.mole3, R.id.mole4, R.id.mole5, R.id.mole6, R.id.mole7, R.id.mole8, R.id.mole9, R.id.mole10, R.id.mole11, R.id.mole12};
private boolean running = true;
private int randomInt = 0;
private Random rand = new Random();
private Handler handler;
//private WAM_Thread wamthread = new WAM_Thread();
private Context cont = this;
private static Handler h;
Message msg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wam_view_layout);
//add ImageViews declared in R.layout.wam_view_layout to ImageView objects
for (int i = 0; i < 11; i++) {
mole[i] = (ImageView) findViewById(moleId[i]);
mole[i].setVisibility(View.INVISIBLE);
mole[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(cont, "You clicked one!", Toast.LENGTH_SHORT).show();
}
});
}
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case MAKE_VISIBLE:
currentMole = (ImageView) msg.obj;
currentMole.setVisibility(View.VISIBLE);
break;
case MAKE_INVISIBLE:
currentMole = (ImageView) msg.obj;
currentMole.setVisibility(View.INVISIBLE);
}
}
};
Runnable runnable = new Runnable() {
public void run() {
while (running) {
randomInt = rand.nextInt(11);
msg = handler.obtainMessage();
msg.obj = mole[randomInt];
msg.what = MAKE_VISIBLE;
handler.sendMessage(msg);
handler.postDelayed(new Runnable() {
#Override
public void run() {
msg = handler.obtainMessage();
msg.obj = mole[randomInt];
msg.what = MAKE_INVISIBLE;
}
}, 1000);
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
All the moles are declared invisible by default in the layout xml file, so this code should be making one of them visible, wait a second, then make him invisible again and then make another visible and repeat. Instead, it's making them all visible all the time. They still respond to taps, but that's it.
Anyone see what I'm doing wrong? I've been struggling with this since last night but I'm really close to getting it right.

Two things:
at the end of your runnable, add a Thread.sleep(1000) so that the background thread pauses after making one thing visible
inside your postDelayed runnable, you're not calling handler.sendMessage

Related

Handler doesn't work correctly

first of all excuse me if my title doesn't describe my question very well but i couldn't find a better one .
there is a simple stopWatch app that has three button start,stop,reset and a textview to display time . app has just one activity like this:
public class StopwatchActivity extends AppCompatActivity {
private int mNumberOfSeconds = 0;
private boolean mRunning = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
//if if uncomment this runner method and delete the runner inside onClickStart everything will work find
//runner()
}
public void onClickStart(View view){
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
}
public void onClickReset(View view){
mRunning = false;
mNumberOfSeconds = 0;
}
public void runner(){
final TextView timeView = (TextView) findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeView.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
});
}
}
my problem is when i comment the runner() in onClickStart method and put it in the onCreate method everything is ok . but when i change the code like above the code is still running but after i press stop button and then press start again the second will increment by 4 or 5 very fast.
can anyone explain me what is the difference between this two modes?
declare your handler globally
public void runner(){
timeView = (TextView) findViewById(R.id.time_view);
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeView.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
}
handler.post(runnable);
}
in button function
public void onClickStart(View view){
if(handler != null) {
//restart the handler to avoid duplicate runnable
handler.removeCallbacks(runnable);//or this handler.removeCallbacksAndMessages(null);
}
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
handler.removeCallbacks(runnable); // this will stop the handler from working
}

How to update TextView dynamically (periodically)?

I am developing a simple android activity with a scrollable TextView. I am displaying numbers from 1-100 in my TextView with a time delay. However my desired output is not what I'm getting.
Current Output: 1 replaced by 2 replaced by 3....till 100.
Desired Output:
1
2
3
4
.
.
100
Here is my Activity code:
public class MainActivity extends ActionBarActivity {
private static int i = 0;
TextView textView;
Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_page);
textView = (TextView) findViewById(R.id.text_area);
new PrimeCalculation().execute();
handler = new Handler();
handler.post(updateView);
}
private Runnable updateView = new Runnable() {
#Override
public void run() {
if(i <= 100) {
textView.setText(Integer.toString(i));
i++;
handler.postDelayed(this, 1000);
}
}
};
}
How about this:
textView.setText(textView.getText() + "\n" + i);
Create a new String Array. Set the text view to the array.toString(); Every time that your timer runs out insert the most recent number into the array and repeat. The most recent number should be an int that increases when the timer runs out. Hope this helps!
Try this
private Handler mHandler = new Handler();
private int nCounter = 0;
View.OnClickListener mButtonStartListener = new OnClickListener() {
public void onClick(View v) {
try {
mHandler.removeCallbacks(hMyTimeTask);
// Parameters
// r The Runnable that will be executed.
// delayMillis The delay (in milliseconds) until the Runnable will be executed.
mHandler.postDelayed(hMyTimeTask, 1000); // delay 1 second
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
private Runnable hMyTimeTask = new Runnable() {
nCounter++;
hTextView.append("\n"+nCounter);
}
public void run() {
};
Hope this will help you
You can use following code......
public class MainActivity extends ActionBarActivity {
private static int i = 0;
TextView textView;
Handler handler;
String textViewText="";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text_area);
handler = new Handler();
handler.post(updateView);
}
private Runnable updateView = new Runnable() {
#Override
public void run() {
if(i <= 100) {
//
textViewText=textViewText+Integer.toString(i)+" ";
textView.setText(textViewText);
// textViewText=textViewText+textView.getText().toString();
i++;
handler.postDelayed(this, 1000);
}
}
};}
I hope it will help you....

Issue related to handler desing pattern

I have one class in which a listener is implemented and depending on the methods call in listener, I am to update the data in corresponding activities. For this, i am interested in using the different-different handlers for each activity.
Since, my handler class in each activity is static and hence, handler object is non-static in activity.
So, problem is how to use those activity's handlers to send Message to them.? Help?
This is one of my class in which I am to update the data from another class: How to do?
public class HandlerActivity extends Activity {
TextView tv;
int counter;
Handler handler = new HandlerExtension(this);
//handler static class
private static class HandlerExtension extends Handler {
private final WeakReference<HandlerActivity> targetActivity;
//constructor
HandlerExtension(HandlerActivity activity){
this.targetActivity = new WeakReference<HandlerActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerActivity activity = targetActivity.get();
if(activity != null){
final String data = msg.getData().getString("counter");//gettting msg data
activity.tv.setText(data);
}
}
}//HandlerExtension
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handler_activity);
Button startB = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.textView1);
final Runnable runnable = new Runnable() {//object to be executed in the thread
#Override
public void run() {
counter+=10;
String result = String.valueOf(counter);
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("counter", result);
msg.setData(bundle);
handler.sendMessage(msg);//Now, updating the view from background thread via handler
SystemClock.sleep(1000);
}
};
//starting the background thread on button click
startB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(runnable).start();
}
});
}//onCreate
}//HandlerActivity

How to stop a runnable by using a buttonStop in Android

I am developing a simple application in which buttons background change dynamically after one second. I have a button in my app name stopButtonBackgroundChanging, as I click the button it stops the changing of buttons background but after 2-seconds the app Stops Unexpectedly". Please help me in this respect and also see my code below you might get better idea that what's really I want to do.
UpdateRunnable updateRunnable;
private Runnable mEndlessRunnable;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
Button stopButtonBackgroundChanging = (Button)findViewById(R.id.buttonStop);
stopButtonBackgroundChanging.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
//See implementation of stop() method at the end of the code
updateRunnable.stop();
}
});
mEndlessRunnable = (Runnable) new UpdateRunnable(new Handler(), new Button[]
{
(Button) findViewById(R.id.button1),
(Button) findViewById(R.id.button2),
(Button) findViewById(R.id.button3),
(Button) findViewById(R.id.button4),
});
mEndlessRunnable.run();
}// End of onCreate()
//Start of UpdateRunnable
private static class UpdateRunnable implements Runnable
{
private boolean myContinue = true;
private Random mRand = new Random();
private Handler mHandler;
private Button[] mButtons;
private Button mCurButton;
private int mState;
public UpdateRunnable(Handler handler, Button[] buttons)
{
mHandler = handler;
mButtons = buttons;
}
public void run()
{
if(myContinue)
{
// select a button if one is not selected
if (mCurButton == null)
{
mCurButton = mButtons[mRand.nextInt(mButtons.length)];
}
// check internal state, `0` means first bg change, `1` means last
switch (mState)
{
case 0:
mCurButton.setBackgroundResource(R.drawable.buttonyellow);
mState = 1;
break;
case 1:
mCurButton.setBackgroundResource(R.drawable.buttonblue);
// reset state and nullify so this continues endlessly
mState = 0;
mCurButton = null;
break;
}
mHandler.postDelayed(this, 1000);
}
}// End of run()
public void stop()
{
this.myContinue =false;
}
}//End of class UpdateRunnable
I believe you will get this to work by changing the your onClick:
public void onClick(View v)
{
//See implementation of stop() method at the end of the code
((UpdateRunnable) mEndlessRunnable).stop();
}

android, failing to use timer

I would like to change the background image of a frame layout per second. For this task I use timer and timertask classes but it does not seem to work as the initial background never changes and the pyhsical device that I test the following code terminates abnormally.
FrameLayout fl;
List<Integer> myList;
int i = 0;
TimerTask myTimerTask = new TimerTask()
{
public void run()
{
fl.setBackgroundResource(myList.get(i));
i++;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myList = new ArrayList<Integer>();
myList.add(R.drawable.square1);
myList.add(R.drawable.square2);
myList.add(R.drawable.square3);
myList.add(R.drawable.square4);
myList.add(R.drawable.square5);
myList.add(R.drawable.square6);
myList.add(R.drawable.square7);
myList.add(R.drawable.square8);
myList.add(R.drawable.square9);
myList.add(R.drawable.square10);
myList.add(R.drawable.square11);
myList.add(R.drawable.square12);
fl = (FrameLayout)findViewById(R.id.frameLayout1);
long delay = 1000;
long period = 1000;
Timer t = new Timer();
t.schedule(myTimerTask,delay,period);
}
Where do I fail ? ^^
Thanks in advance for your time.
You should call invalidate() after setting the new background resource.
You can't access a view from a non UI thread like a timer. You need to have a handler to update the view and get the timer to send messages to it. And you need to stop i from going out of bounds, like:
TimerTask myTimerTask = new TimerTask() {
public void run() {
Message m = Message.obtain();
m.what = i;
myUpdateHandler.sendMessage(m);
i++;
if (i >= myList.size())
i = 0;
}
};
.
Handler myUpdateHandler = new Handler() {
/** Gets called on every message that is received */
#Override
public void handleMessage(Message msg) {
fl.setBackgroundResource(myList.get(msg.what));
}
};
.
Well, you got several problems on your code.
From your code it doesn't clear where the "fi" is initialized, is it before the timer callback is called? after?
What is the purpose of the int "i"? Shouldn't it be a class member?
You must stop the timer on the onDestroy of the Activity, otherwise you might get some undesired behavior when accessing the frame layout.
Anyway, try running the the following from the onCreate:
final FrameLayout fl = (FrameLayout)findViewById(R.id.frameLayout1); // You must have final here
final List<Integer> myList = <get it from where you need to>
int i = 0; // What is the purpose of this int? it passed by value to the callback - are you sure it is needed?
TimerTask myTimerTask = new TimerTask()
{
public void run()
{
fl.setBackgroundResource(myList.get(i));
i++; // Shouldn't it be a class member?
}
};

Categories

Resources