Create timer(Handler) when view appears and stop it when view disappears - android

I have been developing iOS apps for quite a time and now i have switched to android. I have a requirement in which I have to start timer(In think in Android, I need to use handler) when view appears(onResume) and invalidate timer(stop handler) when view disappears(onPause). I am able to create runnable Handler but not able to stop it.
My code is:
protected void AutoRefresh() {
try{
handler.postDelayed(new Runnable() {
public void run() {
new LongOperation().execute("");
}
AutoRefresh();
}, 60000);
}
catch(Exception ex){
}
}
Now, how can I stop this this handler thread when view disappears. Please also comment, if its not the right way to do timer implementation in android.

when view appears(onResume) and invalidate timer(stop handler) when
view disappears(onPause). I am able to create runnable Handler but not
able to stop it.
Keep a reference to the Runnable you use:
private Runnable mRefresh = new Runnable() {
public void run() {
new LongOperation().execute("");
}
AutoRefresh();
}
//...
protected void AutoRefresh() {
handler.postDelayed(mRefresh, 60000);
}
and in onPause remove it like this:
handler.removeCallbacks(mRefresh);
Keep in mind that this will not remove the currently Runnable that is being executed(if any) so in the LongOperation's onPostExecute method you might want to check if the Activity is still available before refreshing the UI or doing any other interaction with the Activity.
Please also comment, if its not the right way to do timer
implementation in android.
You seem to need to do an action at a certain interval of time and using a Handler is the way to do it, I don't think a timer is what you need.

Related

runOnUiThread() no executing when using Thread.sleep()

I'm using code that looks like this :
_thread = new Thread(){
#Override
public void run() {
try {
while (true) {
operate();
Thread.sleep(DELAY);
}
} catch (InterruptedException e) {
// Doesn't matters...
}
}
};
operate function looks like this :
// does things....
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen
}
});
// does other things...
At the bottom line, what i wanted to achieve is an operation that happens once in a while, without interrupting the main thread and the UI, something like a game-loop.
In the first 2 times that operate() runs, it adds the ImageView and everything is alright, but after 2 or 3 times it stops adding the ImageViews, but the UI is still running as usual. When i debugged the problem, i found out that after 3 times the run() method of the Runnable isn't called anymore, even thought the operate function was called.
The wired thing (for me) was that when i removed the Thread.sleep, everything worked fine (much faster of course...). I tried to replace it with a very long for loop (just for checking) and it worked, but of course it is not an appropriate solution to the problem.
I read about the problem, most of the people that asked this question did a thread.sleep or an infinite loop on the main thread, but, as i see it, i didn't do such thing. Many people wrote that you should replace the Thread.sleep with Handler.postDelayed. I tried to do it but it didn't work, maybe I did it wrong. I even tried replacing the runOnUiThread with other options I found on the internet, but all of them gave me the same exact results. I tried to replace the method that I'm adding the view to the activity, but all of them, again, gave the same result.
The waiting is crucial for this application. I got to find a way to wait sometime and then execute a function on the UI thread, cause this pattern returns at least a couple of times in my application.
It sounds like you want a post delay so that you can do the code on the UI thread after some delay. Handler Post Delay.
private static final int DELAY = 500;
private Handler mHandler;
private Runnable mRunnable;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start()
{
mHandler = new Handler();
mRunnable = new MyRunnable(this);
mHandler.postDelayed(mRunnable, DELAY);
}
private void stop()
{
mHandler.removeCallbacks(mRunnable);
}
private void doSomething()
{
// Do your stuff here.
// Reschedule.
mHandler.postDelayed(mRunnable, DELAY);
}
Recommended way of creating a Runnable.
private static class MyRunnable implements Runnable
{
private WeakReference<MainActivity> mRef;
// In here you can pass any object that you need.
MyRunnable(MainActivity activity)
{
mRef = new WeakReference<MainActivity>(activity);
}
#Override
public void run()
{
// Safety check to avoid leaking.
MainActivity activity = mRef.get();
if(activity == null)
{
return;
}
// Do something here.
activity.doSomething();
}
}
There could be several reasons why the UI Runnable isn't being executed. Probably the activity variable has something messed up with it or it's referencing the context incorrectly, or as you said the Thread.sleep() could be causing an issue. At this point more parts of the code needs to viewed to better solve the problem.
A better way of implementing your logic is to use a scheduled Timer instead of using an infinite loop with a Thread.sleep() in it. It will execute the code within a background thread. And then use a Handler to update the UI instead of activity.runOnUiThread(). Here's an example:
// Global variable within the activity
private Handler handler;
// Activity's onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
handler = new Handler(getMainLooper());
Timer timer = new Timer("ScheduledTask");
// Timer must be started on the Main UI thread as such.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
operate();
}
}, 0L, DELAY);
}
private void operate() {
// does things in background....
handler.post(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen from within the Main UI thread
}
});
// does other things in the background...
}

Automatically start execution upon activity launch

I'm working on an app that synchronizes some graphic UI events with an audio track. Right now you need to press a button to set everything in motion, after onCreate exits. I'm trying to add functionality to make the audio/graphical interaction start 10 seconds after everything is laid out.
My first thought is, at the end of onCreate, to make the UI thread sleep for 10000 miliseconds using the solution here and then to call button.onClick(). That seems like really bad practice to me, though, and nothing came of trying it anyway. Is there a good way to implement this autostart feature?
Never ever put sleep/delay on UI-thread. Instead, use Handler and its postDelayed method to get it done inside onCreate, onStart or onResume of your Activity. For example:
#Override
protected void onResume() {
super.onResume();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//do whatever you want here
}
}, 10000L); //the runnable is executed on UI-thread after 10 seconds of delay
}
Handler handler=new Handler();
Runnable notification = new Runnable()
{
#Override
public void run()
{
//post your code............
}
};
handler.postDelayed(notification,10000);
Yes, putting the UI thread to sleep isnt a good idea.
Try this
private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
worker.schedule(task, 10, TimeUnit.SECONDS);

Why is posting & cancelled a runnable on a View and Handler result in different bahviour?

I've been playing about with Runnables and have discovered that if you postDelayed a Runnable on a View then removing the callback won't work, however if you do the same but post the Runnable on a Handler then removing the callback does work.
Why does this work (Runnable run() code never gets executed):
Runnable runnable = new Runnable() {
#Override
public void run() {
// execute some code
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 10000);
handler.removeCallbacks(runnable);
where as this doesn't (Runnable run() code always gets executed)?:
Runnable runnable = new Runnable() {
#Override
public void run() {
// execute some code
}
};
View view = findViewById(R.id.some_view);
view.postDelayed(runnable, 10000);
view.removeCallbacks(runnable);
If the View is not attached to a window, I can see this happening, courtesy of what looks like a bug in Android. Tactically, therefore, it may be a question of timing, making sure that you do not post or remove the Runnable until after the View is attached to the window.
If you happen to have a sample project lying around that replicates this problem, I'd like to take a look at it. Otherwise, I will try making my own, so I can have something I can use to report my presumed bug.
UPDATE
As mentioned in the comments, removeCallbacks() on more ordinary widgets works, so it appears this is a WebView-specific problem, per the OP's sample code.
For various reasons, the View's handler (view.getHandler()) may not be ready when you want to initiate the animation.
Therefor you should probably wait before assigning the runnable to the view.
Assuming you are trying to do that from within an Activity, here is a code that waits for the handler to be available before posting the runnable:
private void assignRunnable(final View view, final Runnable runnable, final int delay)
{
if (view.getHandler() == null) {
// View is not ready, postpone assignment
this.getView().postDelayed(new Runnable() {
#Override
public void run() {
assignRunnable(view, runnable, delay);
}
}, 100);
//Abort
return;
}
//View is ready, assign the runnable
view.postDelayed(runnable, delay);
}
Looking at ViewRootImpl.java, the semantics of View.removeCallbacks() seem unclear to say the least.
RunQueue.removeCallbacks just removes the Runnables from an ArrayList. See here.
If RunQueue.executeActions is called before removeCallbacks, then the ArrayList is cleared in all cases making removeCallbacks a no-op. See here.
RunQueue.executeActions is called for every traversal.... See here.
So unless I miss something, View.removeCallbacks will not work if a traversal has happened since you called View.post.
I'll stick to #james-wald comment above and not use View.post

Android Async, Handler or Timer?

Every 5 seconds, I want to call my webservice and get text (not images), then display it in my ImageAdapter. What would be the best way to accomplish this?
final Handler handler = new Handler();
final Runnable r = new Runnable()
{
public void run()
{
callWebservice();
}
};
handler.postDelayed(r, 5000);
It depends if you want to use a different thread or not. Do you want the user to be able to interact with the application on the UI Thread while the images are downloading? If so, then I would definitely use an AsyncTask with a small ProgressBar (style="#android:style/Widget.ProgressBar.Small")
If you don't care about threading then what #inazaruk said.
Edit: the truth is most modern apps that retrieve data from a web service will use an AsyncTask with a discreet little loader in the corner just to let the user know it's updating.
Edit 2: here's an example of using a TimerTask to run something every 5 seconds. The key is the runOnUiThread(). There may be better ways to tie all the elements together but this accurately portrays all the pieces.
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
CallWebService();
}
}, 0, 1000);
}
private void CallWebService()
{
this.runOnUiThread(fetchData);
}
private Runnable fetchData = new Runnable() {
public void run() {
asyncTask.execute();
}
};
You should call asynctask inside the application main thread. Asynctask can't be called in a background thread.

Android OnDraw()

I am new to Android and I am having trouble understanding the concept so basically this is what i want to do to understand it better..
I created a DrawShape class that extends view. In this class in the OnDraw() I am creating a circle and filling it with a color.
From the Activity I am calling the application. //Until this point I am doing fine.
Now, I need to re-paint the Circle multiple times (Blue, Red, Yellow etc..)
So I was reading and the best way is to use Threads.. I also read you need to use postInvalidate() to redraw (I still dont understand from where I should be calling this) is this called from the Activity?, or within the OnDraw()?.
Hopefully you understand what i want to accomplish, is just that i havent found a good tutorial that shows this, how to repaint something x amount of times .. when I do Thread.sleep() it all stops then it shows my app.. but now i understand why, because i am playing with the main Thread.
Please help me understand this..
Thank you
i did something like this
animcolor()
{
Timer timer = new Timer();
int delay = ...;
int period = ...;
timer.schedule(new TimerTask(){
run() {
setColor( randomint() ); )
customview.postInvalidate();
}
}, delay, period);
threads? not necessary to create them; Timers make a good job on concurrency.
... and the code would somewhat look like....
res/layout/file.xml
<org.customviewlayout a:id="#+id/customlayout"/>
src/org.MyActivity.java
class MyActivity
{
onCreate()
{
customlayout = findViewById(R.id.customlayout);
customlayout.animcolor();
}
}
src/org.customlayout.java
import org.customview;
class customlayout
{
customview;
customlayout(context, attrs)
{
customview = new customview();
addview(customview); // so it's onDraw() method will be called
}
onlayout(...)
{
customview.layout(...);
}
animcolor()
{
Timer timer = new Timer();
int delay = ...;
int period = ...;
timer.schedule(new TimerTask(){
run() {
setColor( randomint() ); )
customview.postInvalidate();
}
}, delay, period);
}
setcolor(int)
{
....
}
}
i think, you can do this using a Timer and a TimerTask inside your activity. The TimerTask runs with the delay you specify and when run all you what to do is yourDrawShapeInstance.postInvalidate();
the mechanism is this, because you arent on the ui thread you call postInvalidate() to add an invalidate on the ui queue, when the ui engine pick the delayed invalidate that you invoke before, then calls automatically the onDraw method of your DrawShape view and the view will be redrawed.
(i donĀ“t test this, i write here)
TimerTask task = new TimerTask(){
public void run(){
myDrawShapeInstance.postInvalidate();
}
}
When drawing, always use a thread away from the main thread and always invalidate after thread has finished (for the most part -- to display current results from drawing). You will probably call your drawing functions from some user-related event, so make sure you're making another thread for that drawing process. Follow those rules and you will be fine.
void drawCircleToCanvas(int color)
{
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
myDrawShapeInstance.postInvalidate();
}
};
Thread updateUI = new Thread() {
public void run() {
//************draw something here***************
handler.sendEmptyMessage(0);
}
};
updateUI.start();
}

Categories

Resources