Update Activity UI from a Timer , after restarting Activity - android

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();
}

Related

android thread only runs once

I just wanted to test Log.i() and look at the console in android studio. In the code below onResume should start the thread and run() should write an endless stream of "dings" with the tag "run" in the monitor. But the run method apparently only gets called once. Why?
public class MainActivity extends Activity implements Runnable {
Thread gameThread = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("onCreate","getting started");
}
public void run() {
Log.i("run","ding");
}
#Override
public void onResume() {
super.onResume();
gameThread = new Thread(this);
gameThread.start();
}
}
You're missing the notion of what threading really does. It allows you to run a unit of work asynchronously. So, all the same normal rules apply. The reason it only runs once, is because the thread exits after run() returns. So just like any other method, you should put something like
while(true)
{
Log.i("run","ding");
}
inside of run(). Ideally you would actually check some condition so that you can exit the thread as needed.
Finally, it is probably a bad idea to have your MainActivity implement Runnable. Typically it is good style to have a thread implemented by its own class, for example DingThread implements Runnable.
You're missing while loop that why its run only once. Use below code. This is the better approach to use thread concept.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("onCreate","getting started");
}
#Override
public void onResume() {
super.onResume();
startThread();// create thread obj and start
}
private GameThread mGameThread = null;
private void startThread() {
stopThread();// if thread already running stop it then create new thread and start (for avoiding multi-threading).
mGameThread = new GameThread();
mGameThread.start();//start the thread.
}
//To stop the thread simply call this method.
private void stopThread() {
if(mGameThread != null) {
mGameThread.setStop();
mGameThread = null;
}
}
private class GameThread extends Thread {
private boolean mIsStop;// mIsStop is default false
#Override
public void run() {
while (!mIsStop) { // if mIsStop is false then only come inside loop.
Log.i("run","ding"); //log will print
}
}
public void setStop() {
mIsStop = true;// set mIStop variable to true.
}
}
}

Timer object doubles when I change activity and come back

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;
}
}
}

Updating Shared prefrence after activity loads in Android

In this code value of the shared preference will be assigned after this activity is launched.I want this code to continuously listen to the change in the shared preference value and upon change an activity to trigger.But the code on execution throws an error saying Null pointer.
Thanks Varanasi
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.d_pay);
con=dpay.this;
sh=getApplicationContext().getSharedPreferences("CRUZER_PREF", MODE_PRIVATE);
getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
#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(){
p=sh.getString("p", null);
Log.e("p", p);
// your logic here
}
#Override
public void onPause() {
autoUpdate.cancel();
super.onPause();
}
I think that having a timer that checks the preferences is a bit wasteful. Assuming that you have access to all the code that changes your SharedPreferences, what you can do is create a wrapper class around SharedPreferences, let's call it PrefHelper. In PrefHelper you'll have a method called setMySpecificPref(context, your parameters here). Anywhere you need to change that preference, you call that method. That method will then change the preference and launch your desired activity.

How do I know if my app has been minimized?

Activity.onPause() and onStop() are called in (at least) two situations:
The another Activity was launched on top of the current one.
The app was minimized.
Is there an easy way to tell the difference?
You could do it this way. Make all of your activities extend from a base activity. The base activity needs to keep a visibility counter that is incremented/decremented during onResume/onPause:
public abstract class MyBaseActivity extends ActionBarActivity {
private static int visibility = 0;
private Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
}
#Override
protected void onResume() {
super.onResume();
visibility++;
handler.removeCallBacks(pauseAppRunnable);
}
#Override
protected void onPause() {
super.onPause();
visibility--;
handler.removeCallBacks(pauseAppRunnable);
// give a short delay here to account for the overhead of starting
// a new activity. Might have to tune this a bit (not tested).
handler.postDelayed(pauseAppRunnable, 100L);
}
#Override
protected void onDestroy() {
super.onDestroy();
// uncomment this if you want the app to NOT respond to invisibility
// if the user backed out of all open activities.
//handler.removeCallBacks(pauseAppRunnable);
}
private Runnable pauseAppRunnable = new Runnable() {
#Override
public void run() {
if (visibility == 0) {
// do something about it
}
}
};
}

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