postDelayed blocking ui thread - android

What I want: The user keeps the button clicked for 1900ms. If he picks his finger before 1900ms the phone stops vibrating. While if he keeps his finger on the button for more than 1900 sec, calculate() method runs.
I am using: postDelayed as I read it does not interfere with ui thread.
I was trying to check if 1900 sec has passed user has not picked his finger, even than calculate method runs.
Error Happening: If the user picks his finger before 1900 seconds or just he touches and immediately picks, the phone keeps vibrating. Although it should not happen as I am checking it with MotionEvent.ACTION_UP. Please help !!
int flag = 0;
int aborted_flag = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
Handler mHandler = new Handler();
if(event.getAction()==MotionEvent.ACTION_DOWN){
scanning();
t1 = System.currentTimeMillis();
vibrator.vibrate(1900);
mHandler.postDelayed(new Runnable() {
public void run() {
check();
}
}, 1901);
}
if(event.getAction()==MotionEvent.ACTION_UP){
if(flag == 0){
t2 = System.currentTimeMillis();
vibrator.cancel();
calculate();
aborted_flag = 1;
}
}
return true;
}
private void check() {
t2 = System.currentTimeMillis();
Log.e("Hello","Inside Check");
Log.e("Hello",""+aborted_flag);
vibrator.cancel();
if(aborted_flag==0){
calculate();
flag = 1;
}
}
private void scanning() {
textView.setText("Scanning");
}
private void calculate() {
Log.e("t2-t1 ", t2-t1+"");
if(t2-t1>=1900){
Random r = new Random();
int k = r.nextInt((5 - 0) + 1) + 0;
textView.setText(str[k]);
////////////animation library code/////////////
YoYo.with(Techniques.StandUp)
.duration(700)
.playOn(findViewById(R.id.text_view));
////////////////////////////////////////
changeBackgroundColor(k);
//textView.setTextColor(Color.parseColor("#00ff00"));
flag = 0;
}
else{
textView.setText("Aborted\n Try Again");
relativeLayout.setBackgroundResource(R.color.red);
}
}
public void changeBackgroundColor(final int k) {
runOnUiThread(new Runnable(){
public void run() {
switch(k){
case 0: relativeLayout.setBackgroundResource(R.color.blue);
break;
case 1: relativeLayout.setBackgroundResource(R.color.pink);
break;
case 2:;
case 3: relativeLayout.setBackgroundResource(R.color.green);
break;
default:relativeLayout.setBackgroundResource(R.color.yellow);
}
}
});
}

If you call postDelayed from the UI thread then your code will be executed on the UI thread.
To use a different thread create one:
Thread t = new Thread(new Runnable(){});
t.start();

You can create a HandlerThread and use it's looper to create a handler and then post stuff to it either post or postdelayed
public class myActivity extends Activity
{
private HandlerThread loaderThread;
private Handler loader;
#override
public void onResume()
{
super.onResume();
if (loaderThread == null)
{
loaderThread = new HandlerThread("MyActivitySeparateThread");
loaderThread.start();
loader = new Handler(loaderThread.getLooper());
}
}
#override
public void onDestroy()
{
//Stop and release handler
try {
loader.removeCallbacksAndMessages(null);
loader.dispose();
} finally {
loader = null;
}
//Stop and release Thread
try {
loaderThread.quit();
loaderThread.dispose();
} finally {
loaderThread = null;
}
super.onDestroy();
}
#Override
public boolean onTouch(View v, MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
scanning();
t1 = System.currentTimeMillis();
vibrator.vibrate(1900);
loader.postDelayed(new Runnable()
{
public void run()
{
check();
}
}, 1901);
break;
}
case MotionEvent.ACTION_UP:
{
if (flag == 0)
{
t2 = System.currentTimeMillis();
vibrator.cancel();
calculate();
aborted_flag = 1;
}
break;
}
}
return true;
}
}

Because in mHandler.postDelayed you call check() which calls calculate() which calls relativeLayout.setBackgroundResource(). UI can be changed only from UIThread.
Make function:
public void changeBackgroundColor(final int color) {
runOnUiThread(new Runnable(){
public void run() {
relativeLayout.setBackgroundResource(color);
}
});
}
And call it from your calculate() function.

Related

How to change the variable of a method in Runnable?

What I want to do is to change my run() method, such that it can receive different variables. I want to change player1life to something else, so that I can use customHandler.postDelayed(updateTimerThread, 0); on a different one, such as player2life.
What the code does, is, when a button is being pressed, a variable changes its value.
NOTE: I am an all most completely newbie in android development.
final android.os.Handler customHandler = new android.os.Handler();
final Runnable updateTimerThread = new Runnable()
{
public void run()
{ if(pressed) {
player1life--;
textView.setText(String.valueOf(player1life));
customHandler.postDelayed(this, 100);
}
}
};
leftButton1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN :
pressed = true;
customHandler.postDelayed(updateTimerThread, 0);
leftButton1.setImageResource(R.drawable.arrownewred);
winFunction();
break;
case MotionEvent.ACTION_UP :
pressed = false;
leftButton1.setImageResource(R.drawable.arrownew); break;
}
return true;
}
});
I smell an XY problem...
Why not create a different Runnable for handling player2life? In the on click handler, just do an if statement to check which players' life should you decrease!
if ... {
customHandler.postDelayed(runnableThatHandlesPlayer1Life, 0);
} else {
customHandler.postDelayed(runnableThatHandlesPlayer2Life, 0);
}
And the runnables can be declared this way:
final Runnable runnableThatHandlesPlayer1Life = new Runnable()
{
public void run()
{ if(pressed) {
player1life--;
textView.setText(String.valueOf(player1life));
customHandler.postDelayed(this, 100);
}
}
};
final Runnable runnableThatHandlesPlayer2Life = new Runnable()
{
public void run()
{ if(pressed) {
player2life--;
textView.setText(String.valueOf(player2life));
customHandler.postDelayed(this, 100);
}
}
};
But your code is kind of suspicious. From my understanding of your code, player1's life will decrease at a rate of 10/second when a button is pressed. It's strange.
It seems like that you're trying to create a timer thingy. Try this Timer class that I wrote. It should make things a bit easier:
import android.os.Handler;
public class Timer {
private Handler handler;
private boolean paused;
private int interval;
private Runnable task = new Runnable () {
#Override
public void run() {
if (!paused) {
runnable.run ();
Timer.this.handler.postDelayed (this, interval);
}
}
};
private Runnable runnable;
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public void startTimer () {
paused = false;
handler.postDelayed (task, interval);
}
public void stopTimer () {
paused = true;
}
public Timer (Runnable runnable, int interval, boolean started) {
handler = new Handler ();
this.runnable = runnable;
this.interval = interval;
if (started)
startTimer ();
}
}
Is problem just getting past the need for final variables in the anonymous inner class? If so, you can use the "trick" of using a final array of length 1, and then updating the element of the array.
Here is a simple example:
public interface Incrementer {
void increment();
}
public class Foo {
private int bar = 0;
public int getBar() {
return bar;
}
public void increment() {
this.bar++;
System.out.println("value is now: " + bar);
}
}
public class Closure {
public static void main(String[] args) {
System.out.println("hello");
Foo foo1 = new Foo();
Foo foo2 = new Foo();
final Foo[] theFoo = {foo1};
Incrementer incrementer = new Incrementer() {
public void increment() {
theFoo[0].increment();
}
};
incrementer.increment();
incrementer.increment();
incrementer.increment();
theFoo[0] = foo2;
incrementer.increment();
incrementer.increment();
System.out.println("foo1 = " + foo1.getBar());
System.out.println("foo2 = " + foo2.getBar());
}
}

Should I pause my thread or not Android

So I have thread where it checks every 10ms's if drag is almost outside draggingzone. Basicly my thread code is doing nothing 99% of time so should I make it to pause and resume only when needed? Or does this literally do nothing when right and left are false?
My code looks like this
timer = new Thread() { //new thread
public void run() {
b = true;
try {
do {
sleep(10);
runOnUiThread(new Runnable() {
#Override
public void run() {
if (right) {
dragzone.moveleft(-5);
} else if (left) {
dragzone.moveleft(5);
}
}
});
}
while (b);
} catch (InterruptedException e) {
}
}
;
};
timer.start();
It looks like using a Thread here is not necessary, and you should switch to using a Handler and postDelayed()
First, declare your Handler, boolean, and a Runnable as instance variables:
Handler handler;
boolean b;
Runnable checkDragZone = new Runnable(){
#Override
public void run() {
if (right) {
dragzone.moveleft(-5);
} else if (left) {
dragzone.moveleft(5);
}
if (b){
handler.postDelayed(this, 10);
}
}
};
To start monitoring, set b to true, and start the Runnable:
handler = new Handler();
b = true;
handler.postDelayed(checkDragZone, 10);
To stop it (temporarily or permanently), just set b to false:
b = false;
It's not really a good practice to keep it running. You can start it when you detect the Drag action and then release it when it's finished.
Runnable runnable;
Thread globalThread;
public void startThread() {
if (threadController) {
runnable = new Runnable() {
#Override
public void run() {
while (threadController) {
for (int i = 0; i < adapter.getCount(); i++) {
final int value = i;
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
viewPager.setCurrentItem(value, true);
}
});
}
}
}
};
globalThread = new Thread(runnable);
globalThread.start();
} else {
return;
}
}
#Override
public void onPause() {
super.onPause();
threadController = false;
handler.removeCallbacks(runnable);
runnable = null;
if (globalThread != null) {
globalThread.interrupt();
}
}
#Override
public void onDestroy() {
super.onDestroy();
threadController = false;
}
Your resolve must be like this globalThread.interrupt();

Show imageview after click wait a moment then hide again [Android]

I'm trying to show some imageviews after the user has clicked on a button then wait for some time for the user to see which was the correct button to click then hide it again. Something like this:
User clicks on button giving his answer
-> Score is updated and correct answer is shown to user by setting the visibility of the imageview to visible
-> wait a moment for the user to be able to see which was the correct answer
-> hide the imageview again and load a new question.
So far I have this:
private void gameLoop(){
new Thread(new Runnable() {
#Override
public void run() {
while (isRunning) {
if (time > 0) {
sleepmillis(1000);
time--;
runOnUiThread(new Runnable() {
#Override
public void run() {
textViewTime.setText(time + " sec.");
}
});
} else
finishGame();
}
}
}).start();
}
private void sleepmillis(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void onClick(View view) {
int givenAnswer = -1;
switch (view.getId()) {
case R.id.buttonFirst:
case R.id.imageViewFirst:
givenAnswer = 0;
break;
case R.id.buttonSecond:
case R.id.imageViewSecond:
givenAnswer = 1;
break;
case R.id.buttonThird:
case R.id.imageViewThird:
givenAnswer = 2;
break;
case R.id.buttonFourth:
case R.id.imageViewFourth:
givenAnswer = 3;
break;
}
processAnswer(givenAnswer);
}
private void processAnswer(int givenAnswer){
if (givenAnswer == answers[4]){
score += 5000;
correctAnswersInARow++;
if (correctAnswersInARow == 3)
score += 15000;
if (correctAnswersInARow == 6){
score += 30000;
correctAnswersInARow = 0;
}
}
runOnUiThread(new Runnable() {
#Override
public void run() {
textViewScore.setText("Score: " + score);
}
});
showCorrectAnswer();
answers = gameData.getAnswers();
updateAnswers();
}
private void showCorrectAnswer(){
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (answers[4]){
case 0:
ivCorrectFirst.setVisibility(View.VISIBLE);
break;
case 1:
ivCorrectSecond.setVisibility(View.VISIBLE);
break;
case 2:
ivCorrectThird.setVisibility(View.VISIBLE);
break;
case 3:
ivCorrectFourth.setVisibility(View.VISIBLE);
}
}
});
sleepmillis(400);
hideCorrectAnswers();
}
private void hideCorrectAnswers(){
runOnUiThread(new Runnable() {
#Override
public void run() {
ivCorrectFirst.setVisibility(View.INVISIBLE);
ivCorrectSecond.setVisibility(View.INVISIBLE);
ivCorrectThird.setVisibility(View.INVISIBLE);
ivCorrectFourth.setVisibility(View.INVISIBLE);
}
});
}
So the important part is in the showCorrectAnswer method, where I try to do what I explained here. Although the app waits 400 ms before loading a new question it doesn't show the correct answer.
imageView.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
imageView.setVisibility(View.INVISIBLE);
}
}, ms);
ms is time duration in milliseconds.

Splash Screen not working with Thread

I write a Splash Screeen to run at the boot time of application
public class SplashScreen extends Activity {
ImageView imgView;
int[] imgID = new int[]{R.drawable.frame0, R.drawable.frame1, R.drawable.frame2, R.drawable.frame3,
R.drawable.frame4, R.drawable.frame5, R.drawable.frame6};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
imgView = (ImageView) findViewById(R.id.imgSplash);
new Thread(new WelcomeScreen()).start();
}
private class WelcomeScreen implements Runnable {
#Override
public void run() {
try {
for (int i = 0; i < imgID.length; i++)
{
imgView.setImageResource(imgID[i]);
sleep(500);
}
} catch (InterruptedException e) {
}finally {
Intent intent = new Intent(SplashScreen.this,LoginActivity.class);
startActivity(intent);
finish();
}
}
}
}
It getting error "Sorry the application has stopped unexpectedly" . I don't know why . Somebody can help me ????
you can not set the resource for yuor ImageView inside a thread different from the UI Thread.
you can use runOnUiThread. It takes as paramter a runnable, and post it in the UI Thread queue. There, the UI thead takes it and update your ImageView. All in all your runnable will become:
private class WelcomeScreen implements Runnable {
#Override
public void run() {
try {
for (int i = 0; i < imgID.length; i++)
{
final int resuorceId = imgID[i];
runOnUiThread(new Runnable() {
#Override
public void run() {
imgView.setImageResource(resuorceId);
}
});
sleep(500);
}
} catch (InterruptedException e) {
}finally {
Intent intent = new Intent(SplashScreen.this,LoginActivity.class);
startActivity(intent);
finish();
}
}
You can not access your views from Thread.
You will need to put your code imgView.setImageResource(imgID[i]); in runOnUiThread
use like:
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
imgView.setImageResource(imgID[i]);
}
});
Thanks
You can not change something in UI from non-UI thread so replace this you code:
imgView.setImageResource(imgID[i]);
to:
runOnUiThread(new Runnable() {
#Override
public void run() {
imgView.setImageResource(imgID[i]);
}
});
//try code this way...
public class SplashScreen extends Activity {
private Intent launchIntent;
private Thread splashThread; //used for perform splash screen operation
private int splashTime = 10000, sleepTime = 50; //used for threading operation
private boolean active = true; //used for touch event
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen); //Set splashscreen.xml here
try {
splashThread = new Thread() { // Creating Thread for splash the screen
#Override
public void run() { // run method implemented to perform threading operation
try {
int waitTime = 0; //counter for threading
do {
sleep(sleepTime); //delay for specific time
if (active)
waitTime += 100;
//write your image code here that display your no. of images
} while (active && (waitTime < splashTime)); //Check touch condition and counter
} catch (Exception e) {
// to handle runtime error of run method
Validation.displayToastMessage(SplashScreen.this, e.toString()); //Call static method of class ToastMessage
}
finish(); //finish current activity
startJustCoupleActivityScreen(); //Call below defined function
}
};
splashThread.start(); //start thread here
} catch (Exception e) {
message("SplashScreen : "+ e.toString()); //Call static method of class ToastMessage
}
}
public void startJustCoupleActivityScreen() {
launchIntent=new Intent(SplashScreen.this,JustCoupleActivity.class); //call Next Screen
startActivity(launchIntent); //start new activity
}
#Override
public boolean onTouchEvent(MotionEvent event) { //onTouch Event
//on touch it immediate skip splash screen
if(event.getAction()==MotionEvent.ACTION_DOWN) active=false; //Check Touch happened or not
return true;
}
public void message(String msg)
{
Validation.displayToastMessage(SplashScreen.this, msg); //display Error Message
}
}

Android Thread Exception?

i got thread exception in android , what i intend to do is, while clicking a button i started a thread going to dynamically invoke the handler ,handler update the text view with integer value , while reaching integer 10, i going to stop the thread and have to show an alert ,but it will cause an error, what i possibly doing is shown below
public class sample extends Activity implements Runnable{
public Camcorder()
{
try{
counterThread = new Thread(this);
}catch(Exception ee)
{
}
}
public void run()
{
try{
while(counterFlag)
{
System.out.println("The time starts at : "+counter);
Thread.sleep(1000);
calculate(counter);
counter++;
}
}catch(Exception ee){
System.out.println("Err in ee : "+ee);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
c=this.getApplicationContext();
requestWindowFeature(Window.FEATURE_NO_TITLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.main);
authalert3 = new AlertDialog.Builder(this);
authalert3.setTitle("Save Video");
authalert3.setMessage("Do you want to save this Video?");
authalert3.setPositiveButton("Yes", null);
Button test = (Button) findViewById(R.id.widget33);
test.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
counter = 0;
counterFlag = true;
counterThread.start();
}
});
public void calculate(int counter2) {
// TODO Auto-generated method stub
if(counter2<60){
if(counter2<10)
{
smin="0"+counter2;
}
else{
smin=""+counter2;
}
}
else{
hours++;
counter=0;
smin="00";
if(hours<10){
shours="0"+hours;
}
else{
shours=""+hours;
}
}
handler.sendEmptyMessage(0);
}
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
String tes=shours+":"+smin;
time.setText(tes);
test();
};
};
public void test(){
duration=1;
if(duration==hours){
counterFlag = false;
videoPath=camcorderView.stopRecording();
authalert3.create().show();
counterThread.stop();
}
}
the error is thrown at counterThread.stop();
Anyone suggest me , how to solve this error.
You don't stop threads by calling counterThread.stop. This method is deprecated. In your case, by setting counterFlag = false; your thread should be stopping itself.
You will also be getting an exception if you click twice on your button: you cannot call start on a Thread that has already been started. You must create a new instance of that Thread and start that new instance (stop the old instance before if necessary).
You can see that SO answer for some sample code on how to create/stop threads: Android thread in service issue. I suggest that you also read some tutorial on Java Threads (this is not specific to Android).
Additionally I think that you don't need a thread at all, you are doing nothing complicated and thus you could simply use the handler to do all the work:
private static final int MSG_REFRESH_UI = 0;
private static final int MSG_UPDATE_COUNTER = 1;
private int counter = 0;
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
if (msg.what==MSG_REFRESH_UI) {
String tes=shours+":"+smin;
time.setText(tes);
test();
} else if (msg.what==MSG_UPDATE_COUNTER) {
counter++;
if (counter<10) {
calculate(counter);
handler.sendEmptyMessageDelayed(MSG_UPDATE_COUNTER, 1000);
handler.sendEmptyMessage(MSG_REFRESH_UI);
}
}
};
};
public void onResume() {
handler.sendEmptyMessage(MSG_UPDATE_COUNTER);
}
public void calculate(int counter2) {
if (counter2<10) {
smin = "0"+counter2;
} else if (counter2<60) {
smin = ""+counter2;
} else{
hours++;
counter=0;
smin="00";
if(hours<10){
shours="0"+hours;
} else {
shours=""+hours;
}
}
}
This will stop the thread at 10
while(counterFlag)
{
System.out.println("The time starts at : "+counter);
Thread.sleep(1000);
calculate(counter);
counter++;
if(counter == 10) counterFlag = false;
}

Categories

Resources