Timer object doubles when I change activity and come back - android

So I'm trying to make a clicker game that is about money. Every time you click the middle button it gives you 1€, but it also gives 1€/second.
I'm having a problem when I go to the shop activity and then come back to MainActivity, it counts as 2 timers are running, and if I alternate activities again, it counts as if 3 Timers were running at the same time. Thats giving me issues because I declared "money_per_sec" int, and dont want 2€/s or more just because I click on "Shop".
Any help?
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
money = loadGame.getInt("money", 0);
money_per_sec = loadGame.getInt("money_per_sec", 0);
money_per_click = loadGame.getInt("money_per_click", 0);
money = money + money_per_sec;
SharedPreferences saveGame = getSharedPreferences(SAVE, 0);
SharedPreferences.Editor editor = saveGame.edit();
editor.putInt("money", money);
editor.putInt("money_per_sec", money_per_sec);
editor.apply();
runOnUiThread(new Runnable() {
#Override
public void run() {
tv_money_per_sec.setText(String.valueOf(money_per_sec));
tv_money_per_click.setText(String.valueOf(money_per_click));
toolbar_title.setText(String.valueOf(money));
}
});
}
}, 1000, 1000);

You have to check which Activity's life-cycle method are you using for starting your timer. I think you are using onResume which case this problem, and you start another timer by getting back to your Activity.
you can take a look at this diagram and choose the best position, or even separate your timer from activity life-cycle:
also you can implement Application class and place your timer there. this class is instantiated once during your application life time:
public class MyApplication extends Application{
private OnTimerTick listener;
public void setListener(OnTimerTick listener) {
this.listener = listener;
}
#Override
public void onCreate() {
super.onCreate();
// start your timer
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
money = loadGame.getInt("money", 0);
money_per_sec = loadGame.getInt("money_per_sec", 0);
money_per_click = loadGame.getInt("money_per_click", 0);
money = money + money_per_sec;
SharedPreferences saveGame = getSharedPreferences(SAVE, 0);
SharedPreferences.Editor editor = saveGame.edit();
editor.putInt("money", money);
editor.putInt("money_per_sec", money_per_sec);
editor.apply();
if(listener!=null)
listener.onTick(money,money_per_sec);
}
}, 1000, 1000);
}
interface OnTimerTick {
void onTick(int money, int moneyPerSec);
}
}
you have to add this to your manifest like this:
<application
android:name=".MyApplication"
...
also you can access this singleton class every where in your application:
MyApplication application = (MyApplication) getApplication();
inside your MainActivity:
public class MainActivity extends AppCompatActivity implements MyApplication.OnTimerTick {
private MyApplication application;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
application = (MyApplication) getApplication();
// other stuff
}
#Override
protected void onResume() {
super.onResume();
application.setListener(this);
}
#Override
protected void onPause() {
super.onPause();
application.setListener(null);
}
#Override
public void onTick(final int money, final int moneyPerSec) {
runOnUiThread(new Runnable() {
#Override
public void run() {
tv_money_per_sec.setText(String.valueOf(moneyPerSec));
tv_money_per_click.setText(String.valueOf(moneyPerSec));
toolbar_title.setText(String.valueOf(money));
}
});
}
This is how you can even access the Timer within any other activity you like.

First, you should probably consider using a Handler instead of a Timer. This will allow you to more easily manage your Timer within the lifecycle of the Activity.
private final int interval = 1000; // 1 Second
private Handler handler = new Handler();
private Runnable runnable = new Runnable(){
public void run() {
Toast.makeText(MyActivity.this, "C'Mom no hands!", Toast.LENGTH_SHORT).show();
}
};
...
handler.postAtTime(runnable, System.currentTimeMillis()+interval);
handler.postDelayed(runnable, interval);
Next, based on your comment:
I'm actually using onCreate in all other activities, but the fun part
is, that when I change activity, the timer still runs. And when I go
back to the activity that has the Timer, it "creates" another timer.
It looks like you are using the standard launchMode for your Activity. This launchMode will create a new Activity when the Back or Up buttons are pressed because the second Activity is creating an Intent to start that Activity.
Try setting the launchMode for the Activity with the Timer to android:launchMode="singleTop". This will instead route the Intent from the second Activity to the existing parent Activity, and trigger a call to onNewIntent. This will prevent the Activity from being restarted, and creating another Timer.
You should also probably terminate the Timer in the onDestroy method of your Activity, otherwise it will continue to run and could cause memory leaks.

Make your timer an instance of the activity rather than an anonymous class. Check if it is already running in onResume(). If not, start it. Stop it in onDestroy(). Something like this:
class MyActivity extends Activity {
private Timer mTimer;
#Override
public void onResume() {
super.onResume();
if (mTimer == null) {
mTimer = new Timer() {
};
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
}

Related

Auto logout user, when App in background for 5 mins, when the user resumes the app?

If the screen is locked (while the app is still there) or if the app has moved to background for more than 5 mins I want to logout my app. I have a BaseActivity which extends the AppCompatActivity. All other activities extends BaseActvity.
I have used the below code in BaseActivity but after 5 mins, the app opens up by itself in LoginActivity. Can you please help me with this?
Java code below:
#Override
protected void onPause() {
super.onPause();
Log.v(TAG, "on pause called");
timer = new Timer();
Log.i(TAG, "Invoking logout timer");
LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
timer.schedule(logoutTimeTask, 300000); //auto logout in 5 minutes
}
#Override
protected void onResume() {
super.onResume();
Log.v(TAG, "on resume called");
if (timer != null) {
timer.cancel();
Log.i(TAG, "cancel timer");
timer = null;
}
}
private class LogOutTimerTask extends TimerTask {
#Override
public void run() {
//redirect user to login screen
Constants.SESSION_ID = "";
Intent i = new Intent(getApplicationContext(), LoginActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
}
Edit
I have added all possible suggestion but the timer is working even when I launch the app. A code snippet would be useful.
#Override
protected void onPause() {
super.onPause();
long LastTime= System.currentTimeMillis(); //save this time
}
#Override
protected void onResume() {
super.onResume();
long CurrentTime= System.currentTimeMillis(); //this is your current time
//get LastTime and compare
long difference = CurrentTime - LastTime;
// now convert difference into minutes and start login activity
}
This may helps you.
You can use:
import java.util.Calendar
Calendar c = Calendar.getInstance();
int seconds = c.get(Calendar.SECOND);
int minutes = c.get(Calendar.MINUTES);
This will give you the user's time at the moment.
You can save them in onStop() and take a look at them in onRestart()/onResume(if you destroy it). There you can apply your logic. Make some calculations so you can have information about how much time has passed and go from there.
You can save it in SharedPreferences or any other way you like.
The problem here is with your LogoutTimerTask class that contains the code to automatically move to login screen. Don't start an activity inside that LogoutTimerTask class. Instead, set a globally defined variable value to logout inside the run() method after 5 minutes hacve passed and when user reenters your app, I would suggest that you use that variable in onCreate to check if you have manually logged out the user or not and call appropriate activity class from there
I have solved the issue using the following snippet.
Since I am using fragments after login hence, using this code only in the Activity which contains the fragments helped me.
#Override
protected void onPause() {
super.onPause();
}
private class LogOutTimerTask extends TimerTask {
#Override
public void run() {
//redirect user to login screen
finish();
Constants.SESSION_ID = null;
}
}
#Override
protected void onStop() {
super.onStop();
timer = new Timer();
Log.i(TAG, "Invoking logout timer");
LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
timer.schedule(logoutTimeTask, DISCONNECT_TIMEOUT); //auto logout in 5 minutes
}
#Override
public void onResume() {
super.onResume();
if (timer != null) {
timer.cancel();
Log.i(TAG, "cancel timer");
timer = null;
}
}

Update Activity UI from a Timer , after restarting Activity

In the onCreate() method of my activity I have a Timer + TimerTask that will schedule a ParseQuery. On The ParseQuery callback, which is on mainThread, I delegate an interface callback to make a simple UI update. This works when I let the Activity unchanged. But if I exit from the activity and enter again it (A new timer will NOT be created here, because it gets created only when starting the activity from a certain point) wouldn't work. I think is something with Activity instances but I cannot handle it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
....
uiTimer = new Timer();
uiTask = new TimerTask() {
#Override
public void run() {
doParseTask();
}
};
uiTimer.schedule(uiTask, shortCodeLeft);
}
doParseTask(){
Utils.doParseQuery(this, new MyListener{
#Override
public void onSuccess() {
updateUI();
}
});
}
updateUI(){
Log.i(TAG, "Show changed layout"); //This is always shown, this way I ensure it gets here!!
mTextView.setText(foo); //this doesn't work
mLayout.setVisibility(View.GONE); //this doesn't work
}
The ParseQuery is executed in done() callback method, I call the function that updates the UI:
public class Utils{
.......
doParseQuery(Context ctx, MyListener listener){
.......
query.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if(e == null){
....
listener.onSuccess();
}
}
});
}
}
What I have tried, with no success:
1. make the `uiTimer` and `uiTask` static global variables; // I thought that maybe it gets leaked into activity instances
2. update the UI using
runOnUiThread(new Runnable() {
#Override
public void run() {}
});
OR
mLayout.post(new Runnable() {
#Override
public void run() {
mLayout.setVisibility(View.GONE);
}
});
3. android:launchMode= "singleInstance" //in Manifest
If you want that your UITimer to gets executed every time your activity goes to foreground, you should implement the onStart or onResume method and move your uiTimer implementation to one of both method. Even your activity being already started these two methods are called after exiting the activity and reopening it again.
A better explanation of Android Activity lifecycle is well explained by google documentation https://developer.android.com/guide/components/activities/activity-lifecycle.html.
Your code would look like this:
#Override
protected void onStart() {
super.onStart();
....
uiTimer = new Timer();
uiTask = new TimerTask() {
#Override
public void run() {
doParseTask();
}
};
uiTimer.schedule(uiTask, shortCodeLeft);
}
doParseTask(){
Utils.doParseQuery(this, new MyListener{
#Override
public void onSuccess() {
updateUI();
}
});
}
When you exit from your activity, the instances mTextView and mLayout will be destroyed.
Then, when you create a new activity, the activity creates new instances of the text view and layout.
Your timer goes off and tries to update the original elements, which are now invalid as the activity has been closed (but the log still works as this is separate to your activity).
You should initialise the timer & task in onCreate(), and then in order to stop updating the old UI elements:
#Override
protected void onStop() {
if (uiTimer != null) {
uiTimer.cancel();
}
super.onStop();
}

Unable to reschedule timer in Android

i am working on a simple app. in my app i am using timerTask like this:
TimerTask saveData=new TimerTask() {
#Override
public void run() {
saveDatatoFile();
}
};
i call TimerTask in onCreate() of my activity like:
int file_saving_pollingtime=60000;
timer.schedule(saveData,10000,file_saving_pollingtime);
its working fine. i wants to change the file_saving_pollingtime on realtime basis for it i am using BroadcastReceiver to read message of specific pattern which contains the file_saving_pollingtime. i successfully read the file_saving_pollingtime from message and store it in sheared preference. but i am unable to refresh the file_saving_pollingtime of timer as per message file_saving_pollingtime:
if (frequency.matches(regexStr)){
editor.putString(FILE_FREQUENCY, frequency);
editor.commit();
int fre=Integer.parseInt(sharedPreferences.getString(FILE_FREQUENCY, "0"));
int freq=fre*1000;
Log.d("dfdfdfdf", String.valueOf(freq));
Novipod mv=new Novipod(); //mv is the instance of main activity class
mv.timer.cancel();
mv.timer.schedule(mv.saveData,1000,freq);
}
please help me guys
Try with following code:
public void rescheduleTimer() { // call it to reschedule the timer
if(timer != null){
timer.cancel();
}
timer = new Timer();
timerTask = new MyTimerTask();
timer.schedule(timerTask,1000,freq);
}
private class MyTimerTask extends TimerTask {
#Override
public void run() {
saveDatatoFile();
}
}

Activity gets killed when orientation is changed

I have two activities in my project- Splash and AActivity. Splash is the main activity and is working fine. But if i change the orientation while Splash activity is running, the UI of splash activity goes off but it opens the AActivity after 10 sec.
code for splash activity is -
public class Splash extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
Thread timer = new Thread(){
public void run() {
try{
sleep(10000);
}catch(InterruptedException e){
e.printStackTrace();
}finally{
Intent AActivityIntent = new Intent("com.example.ex.AACTIVITY");
startActivity(AActivityIntent);
}
}};
timer.start();
}
#Override
protected void onPause() {
super.onPause();
finish();
}
}
I want to retain the UI of splash activity for 10 seconds even if orientation is changed. After 10 sec splash activity should be finished. How to do it ???
I suggest you doing the following:
public class Splash extends Activity {
private Thread timer;
private volatile long timeLeft;
private long timeStarted;
private long timeStopped;
private static final long TIME_TO_SHOW = 100000
private static final String KEY_TIME_LEFT = "timeLeftToRun";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
if (savedInstanceState != null) {
timeLeft = savedInstanceState.getLong(KEY_TIME_LEFT, 0);
} else {
timeleft = TIME_TO_SHOW;
}
timer = new Thread() {
public void run() {
try {
sleep(timeLeft);
Intent AActivityIntent = new Intent("com.example.ex.LISTSCREEN");
startActivity(AActivityIntent);
} catch(InterruptedException e) {}
}
}};
timeStarted = SystemClock.elapsedRealtime();
timer.start();
}
#Override
protected void onPause() {
super.onPause();
timer.interrupt();
timeStopped = SystemClock.elapsedRealtime();
finish();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
timeLeft -= timeStopped - timeStarted;
if (timeLeft > 0) outState.putLong(timeLeft);
}
}
The main idea here is that you kill the thread if the activity is killed, but you take a note for how long it has run and how much time it has left. When the activity is restored, you do the same actions, except you have to wait for a smaller amount of time.
The code above is, of course, untested, but it should illustrate the idea.
Insert into your manifest these below block.
It means orientation change situation controlled by your "activity".
http://developer.android.com/guide/topics/manifest/activity-element.html#config
android:configChanges="orientation"
And more.
Override "onConfigurationChanged" method.
Try this. You can do everything you want.
Instead of using a Thread for delayed launch of activity use Alarmmanager, even if you are quitting the app you could always cancel the pending Alarm

Periodically refresh/reload activity

I have one activity. OnCreate the activity gets the source (html) of a web page to a string and presents the result (after parsing it a bit) in a textview.
I would like the activity to reload/refresh periodically to always present the latest information.
What is the best solution for this?
First of all... separate the updating logic from your onCreate method. So, for instance, you can create an updateHTML().
Then, you can use a Timer in order to update the page periodically:
public class YourActivity extends Activity {
private Timer autoUpdate;
public void onCreate(Bundle b){
super.onCreate(b);
// whatever you have here
}
#Override
public void onResume() {
super.onResume();
autoUpdate = new Timer();
autoUpdate.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
updateHTML();
}
});
}
}, 0, 40000); // updates each 40 secs
}
private void updateHTML(){
// your logic here
}
#Override
public void onPause() {
autoUpdate.cancel();
super.onPause();
}
}
Notice that I'm canceling the updating task on onPause, and that in this case the updateHTML method is executed each 40 secs (40000 milliseconds). Also, make sure you import these two classes: java.util.Timer and java.util.TimerTask.

Categories

Resources