I am using a function into an adapter and I added a Handler to do the refresh every 2s like below:
final Handler refreshHandler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
refreshHandler.postDelayed(this, 2000);
myfunction();
}
};
refreshHandler.postDelayed(runnable, 2000);
When I'm not into the fragment where I deploy the adapter the handler is always reloading.
So my question is, how to stop the handler every time I quit the fragment?
I think you should use the removeCallbacks(Runnable r) method.
That's how you put it in your code:
final Handler refreshHandler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
refreshHandler.postDelayed(this, 2000);
myfunction();
}
};
refreshHandler.postDelayed(runnable, 2000);
#Override
public void onDestroy () {
refreshHandler.removeCallback(runnable);
super.onDestroy ();
}
Something like that wherever you want. Hope you understand
Related
I need to delete a value from SharedPreferences after 5 minutes or when the user finished to do something . So when I add that value I start this:
Activity A
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mySharedPreferences.removeValue(mContext, Utils.MY_VALUE);
}
}, Utils.TIME_BEFORE_DELETE);
and in the case users finished all I do this:
Activity B
mySharedPrefernces.removeValue(mContext, Utils.MY_VALUE);
But how can I stop the Handle into second activity?? Or is there another way to do it??
you can you boolean variable if you want to cancel this.
create public static boolean to check if the task is cancelled or not.
public static boolean isCanceled = false;
Use this in run() method
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (!isCanceled)
mySharedPreferences.removeValue(mContext, Utils.MY_VALUE);
}
}, Utils.TIME_BEFORE_DELETE);
if you want to cancel then set:
isCanceled = true;
Runnable run = new Runnable() {
#Override
public void run() {
mySharedPreferences.removeValue(mContext, Utils.MY_VALUE);
}
};
Handler handler = new Handler();
handler.postDelayed(run, Utils.TIME_BEFORE_DELETE);
//to dismiss pending runnable
handler.removeCallbacks(run);
A better way to do: Example code
publc static final Handler handler = new Handler();
public static final Runnable runnable = new Runnable() {
public void run() {
try {
Log.d("Runnable","Handler is working");
if(i == 5){ // just remove call backs
handler.removeCallbacks(this);
Log.d("Runnable","ok");
} else { // post again
i++;
handler.postDelayed(this, 5000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
//now somewhere in a method
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
handler.removeCallbacks(runnable);
handler.postDelayed(runnable, 5000);
}
});
You can use handler.removeCallbacksAndMessages(null);. More information link
In this case you can use service with sticky flags. So you start service with intent "start_handler" and start handler also. When you need cancel handler you send the intent to stop handler and service. Or when time is passed and handler calls your code you should also stop service.
Using service with sticky flag provides possibility restoring handler. Also you need add some logic saving time when handler was run for correct restoring handler.
For that you can't use direct Runnable inside handler, you need to take one instance of it then you can do this like below,
Runnable myRunnable = new Runnable(){};
Then assign this in handler
handler.postDelayed(myRunnable);
And on no need use below line
handler.removeCallbacks(myRunnable);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
//add your code hare
finish();
}
}, 10000);
by using this way you can stop your runnable in a fix time
I have used a service which posts code to a handler at an interval of 3 seconds to keep generating a simple touch event (a button press).
This is the service code:
public class BackgroundTouchService extends IntentService
{
public BackgroundTouchService() {
super("BackgroundTouchService");
}
#Override
protected void onHandleIntent(Intent intent)
{
final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_BUTTON_PRESS, 400, 400, 0);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run()
{
View v = new View(getApplication());
v.onTouchEvent(event);
handler.postDelayed(this, 3000);
}
});
}
}
In my UI, I have covered the screen with buttons (with appropriate listeners) so that I can easily spot which button has been pressed. However, the main activity loads up and then nothing happens. Why is this?
EDIT:
As Vojtěch Sáze correctly pointed out, the handler in the above code is not associated with the main thread, and hence cannot be used to modify the UI by generating touch events. Hence, I wrote the code for the handler in the main activity itself:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startTouch();
}
private void startTouch()
{
final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_BUTTON_PRESS,400.0f, 400.0f, 0);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run()
{
onTouchEvent(event);
handler.postDelayed(this, 3000);
}
});
}
}
However, this still does not do anything. Any ideas?
SOLUTION:
Okay, so in the event type parameter of the MotionEvent.obtain method I had originally specified ACTION_BUTTON_PRESS, however, when I used to separate events to specify ACTION_DOWN and ACTION_UP separately, this seemed to work.
Any explanations as to why this happened are welcome.
In case someone needs this, here is the code:
final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN,400.0f, 400.0f, 0);
final MotionEvent event2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP, 400.0f, 400.0f, 0);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run()
{
dispatchTouchEvent(event);
dispatchTouchEvent(event2);
handler.postDelayed(this, 3000);
}
});
I think you need to use dispatchTouchEvent method to send the touch event to the view.
I'm not sure if this will help. But definitely there is problem with
final Handler handler = new Handler();
From documentation:
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.
But you don't have main thread in onHandleIntent(). You'll need to create the Handler in the main thread.
Just use code below. Using Android Handler, you need also looper for posting action.
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run()
{
onTouchEvent(event);
handler.postDelayed(this, 3000);
}
});
Hi im trying to do an app that searchs bluetooth devices that are near every 5-10 seconds, so I tried to do an endless loop but the app is getting stuck when I press the button that starts the loop:
int stopInt=2;
serachB.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
do {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
listAdapter.clear();
getPairedDevices();
startDiscovery();
}
}, 4000);
} while (stopInt>1);
}
private final Handler mMyhandler = new Handler();
private final Runnable checkBluetoothRunnable = new Runnable() {
public void run() {
//Do your work here
mMyHandler.postDelayed(this, 5000);
}
};
serachB.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v){
mMyhandler.post(checkBluetoothRunnable);
}
}
}
Change it this way:
serachB.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
while(true){
listAdapter.clear();
getPairedDevices();
startDiscovery();
Thread.sleep(4000);
}
}
});
}}
You are continually creating new instances of Handler in your while loop which is going to eat up memory. I wouldn't be surprised if you were getting an OutOfMemoryError which is causing the termination. Try creating a single instance outside of your loop.
The Runnable passed to Handler runs on the UIThread since it was created on that thread. This should be avoided for lengthy operations and instead delegated to another thread.
When you create a new Handler, it is bound to the thread / message
queue of the thread that is creating it
Also, rather than using the Handler class you could potentially swap it out to use ScheduledThreadPoolExecutor which will give you a pool of threads which can then periodically execute your device discovery code.
Firstly create a ScheduledExecutorService:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
Create an instance of your Runnable:
MyRunnable deviceDiscovery = new MyRunnable();
Then in your onClick method you kick off with scheduleAtFixedRate:
ScheduledFuture<?> future = scheduledExecutorService.scheduleAtFixedRate(deviceDiscovery, 0, 1, TimeUnit.MINUTES);
Then when you want to stop it running you need to call cancel on the ScheduledFuture:
future.cancel(true); // Set to true to say it can interrupt if already running
I want to cyclically update an Android Layout. For this purpose I wrote a short class derived from TimerTask.
Unfortunately my code causes an exception and I do not really know, what the problem might be. :(
So maybe anybody could help.
Thanks
Chris
Here's my code:
In the main activity I've got:
private MyLayoutClass m_MyLayout;
...
public void onCreate(Bundle savedInstanceState)
{
...
m_MyLayout = new AdLayout(this);
Timer caretaker = new Timer();
caretaker.schedule(new MyReloadTimerTask(m_MyLayout), 1000, 5000);
...
}
This is my derived TimerTask class:
public class MyReloadTimerTask extends TimerTask
{
private MyLayoutClass m_MyLayout;
public MyReloadTimerTask(MyLayoutClass aLayout)
{
m_MyLayout = aLayout;
}
#Override
public void run()
{
m_MyLayout.doReload();
}
}
The doReload() cannot be executed, I get an exception with this message: Can't create handler inside thread that has not called Looper.prepare()
Timertask runs on a different thread. So you cannot not update/access ui from a background thread.
Probably m_MyLayout.doReload() is updating ui. Use a Handler or runOnUiThread
runOnUiThread(new Runnable() {
#Override
public void run() {
m_MyLayout.doReload()
}
});
Using Handler
Handler m_handler;
Runnable m_handlerTask ;
m_handler = new Handler();
m_handlerTask = new Runnable()
{
#Override
public void run() {
// do something
m_handler.postDelayed(m_handlerTask, 1000);
// repeat some task every 1 second
}
};
m_handlerTask.run();
To cancel the run
m_handler.removeCallbacks(m_handlerTask);
I am working on a layout which shows a tab like structure on bottom of the layout. Which I need to show on double tap and then hide it after 5 sec. So I am using this countdown timer:
public void timer()
{
cdt=new CountDownTimer(5000,1000) {
#Override
public void onTick(long millisUntilFinished) {
System.out.println("Timer Working"+millisUntilFinished+"");
}
#Override
public void onFinish() {
System.out.println("Finished");
main =(LinearLayout)findViewById(R.id.parent);
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)main.getLayoutParams();
mlp.height=420;
set_up_views();
find_module();
tl.setVisibility(View.INVISIBLE);
}
}.start();
}
But I dont know how to stop and restart this timer. How can I do?
I suggest you not to Use CountDownTimer for this case.
Use Handler.postDelayed(Runnable runnable, long delay)
public class yourActivity extends Activity
{
public Handler handler = new Handler();
...
public void hideAfter5Sec()
{
handler.postDelayed(new Runnable()
{
View view = findViewById(view_to_hide);
view.setVisibility(View.INVISIBLE);
}, 5000);
}
}
postDelayed will execute that code after 5Sec.
EDITED:
postDelayed will be call only once after 5 Sec through Lopper.loop(). If there are multiple call to hideAfter5Sec() then only you will get multiple call to postDelayed.
If you have multiple call hideAfter5Sec() i dont think there is any wrong because hideAfter5Sec() is just hidding it. so if it one or many your view will be hidden.
If in case you want to hide only in the last call of hideAfter5Sec() use this variant.
public class yourActivity extends Activity
{
public Handler handler = new Handler();
public long lastHideAfter5Sec = 0L;
...
public void hideAfter5Sec()
{
lastHideAfter5Sec = System.currentTimeMillis();
handler.postDelayed(new Runnable()
{
if(System.currentTimeMillis() - lastHideAfter5Sec < 5000)
return;
View view = findViewById(view_to_hide);
view.setVisibility(View.INVISIBLE);
}, 5000);
}