Performance issues updating multiple UI elements every second - android

EDIT: I've posted my solution below, (basically, ListViews are very slow for some reason), and will try to update it further if I can clarify why exactly a ListView is so awful in this situation.
Objective:
Get 7 ListView objects showing 7 independently set clocks/timers which update/tick every second. These clocks are just Strings with a calculated elapsed time shown (SystemClock.ElapsedRealTime() - stored ElapsedRealTime() of when the object was instantiated.)
The problem:
Basically, 8 minutes into these 7 clocks ticking away - my program is essentially useless.. the information presented is inaccurate, the UI is pretty much unresponsive, etc. Here are some results of testing against the benchmark of a physical stopwatch:
At 04:00, clocks are slipping 1 second, and updating every 4 seconds.
At 06:00, clocks are slipping 3 seconds, and updating every 5 seconds.
At 08:00, clocks are slipping 6-7 seconds, and updating every 6 seconds.
At 16:00, clocks are slipping 7 seconds, and updating every 10 seconds.
What I have so far:
I have a custom class, ActivityTimer, with a stored long representing the SystemClock.ElapsedRealTime() of when each ActivityTimer was first instantiated. By clicking on a Button 7 times, I instantiate 7 of these, each of which are added to an ArrayList
I have a compound view, ActivityTimerControl, which is passed an ActivityTimer upon instantiation, and then presents data elements of my ActivityTimer in UI elements (such as the ticking clock.) My ArrayAdapter handles this instantiation and works fine. As per this fine tutorial I have a _Handler in this ActivityControl, which upon construction of the ActvityControl posts this:
private Runnable _timerUpdateTask = new Runnable()
{
public void run()
{
final long start = _ActivityTimer.getStartTime();
long millis = SystemClock.elapsedRealtime() - start;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
if (seconds < 10)
{
_tglActivityPauseButton.setTextOn(""+minutes+":0"+seconds);
}
else
{
_tglActivityPauseButton.setTextOn("" + minutes + ":" + seconds);
}
_tglActivityPauseButton.setChecked(true);
_timerUpdateHandler.postDelayed(this, 1000);
}
};
Other than what I've described my project really doesn't do anything yet, as I've been stuck on this fundamental issue so far. So I haven't posted any other code because I just don't think its relevant beyond the summaries I've given above - but if anyone feels some other part of the project is relevant just let me know and I'll post the detailed code for it. I'm presenting my final String into the SetTextOn() of a ToggleButton, but testing has shown that this isn't a significant factor - it doesn't matter whether I'm setting the Text of a normal TextView, or what, no matter what I've tried my results are always roughly the same, with noticable lag on each clock and eventually the UI becoming unresponsive completely.
My understanding is that a Handler is supposed to be the most efficient way of updating a UI element on a consistent and frequent basis, replacing java.util.Timer, but despite this my code starts out slow and just gets worst the longer I let it run.
Even upon increasing the postDelay to 5000ms, the same problems still occured and the app still force closed after 49 minutes. Since I would have thought this test would have extended the time til force-close by 5, if not fixing it altogether (at the detriment to the functionality I want), I'm suspecting that something isn't recycling right with the handler or some other component.
My Questions:
I suspect my problem is in having 7 objects (ActivityControls), each of which has its own Handler constantly cycling the update of the corresponding time that's displayed. Does anyone have experience to say if this would be the case?
Is there a way I can have a single Handler that calls upon each ActivityControl in my ListView to update its time?
Does posting a message to the Handler leave some memory trace that doesn't dispose automatically, or might benefit from being forced to dispose?
Does anyone else have other ideas about the most efficient way of running constant UI updates on multiple objects?

you are delaying by 1sec
_timerUpdateHandler.postDelayed(this, 1000);
You have not allowed anytime for your code to run.

Ugh.. wouldn't you know it.. this isn't a problem with my Handler, or anything else like that. It's either an inefficiency with a ListView, or some funkiness that takes place in an ArrayAdapter (the one I was using was as simple as it gets though.) I made a sandbox project (below) and instantiated my 7 ActivityTimers right into an ArrayList, and then iterated through that array using each object to instantiate an ActivityControl that's passed a copy of that ActivityTimer from the array. Everything I was doing previously, but replacing the ListView with a LinearLayout contained in a ScrollView. After 16 minutes I've got 7 ActivityControls ticking away roughly every single second, and keeping accurate time. Even after 30 minutes, showing no slight hint of problems (as it should be!) Here's what I did:
public class Sandbox_Experiments extends Activity {
ArrayList<com.maxx233.Sandbox.ActivityTimer> _ActivityTimers = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_ActivityTimers = new ArrayList<com.maxx233.Sandbox.ActivityTimer>();
View myLayout = findViewById(R.id.Info);
com.maxx233.Sandbox.ActivityTimer act1 = new com.maxx233.Sandbox.ActivityTimer();
com.maxx233.Sandbox.ActivityTimer act2 = new com.maxx233.Sandbox.ActivityTimer();
com.maxx233.Sandbox.ActivityTimer act3 = new com.maxx233.Sandbox.ActivityTimer();
com.maxx233.Sandbox.ActivityTimer act4 = new com.maxx233.Sandbox.ActivityTimer();
com.maxx233.Sandbox.ActivityTimer act5 = new com.maxx233.Sandbox.ActivityTimer();
com.maxx233.Sandbox.ActivityTimer act6 = new com.maxx233.Sandbox.ActivityTimer();
com.maxx233.Sandbox.ActivityTimer act7 = new com.maxx233.Sandbox.ActivityTimer();
_ActivityTimers.add(act1);
__ActivityTimers.add(act2);
__ActivityTimers.add(act3);
__ActivityTimers.add(act4);
__ActivityTimers.add(act5);
__ActivityTimers.add(act6);
__ActivityTimers.add(act7);
for (int i = 0; i < __ActivityTimers.size(); i++)
{
ActivityControl ac = new ActivityControl(this, (com.maxx233.Sandbox.ActivityTimer)__ActivityTimers.get(i));
ac.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
((LinearLayout)myLayout).addView(ac);
}
}
}
So I'll work on figuring out what spcifically it is about having a ListView in the equation that causes such a problem, and then I'll update this thread in case anyone else is having a similar problem ever. Unless someone else out there happens to already know why a ListView would make updating a handful of objects each second so much more resource intensive?

Related

Mana recovery issue

We're making a game in Android Studio and we got stuck. The resource (mana) used for specific spells should recover on time, e.g. 1 mana point per 5 minutes. We don't really get how to make it recover while the game is off. Is there a method to check current date/time and count the amount of mana replenished? Converting date and time to String and comparing it with the new date/time seems to be an "exciting" work to do, but we would bypass these mechanics if there is a way.
Thank you in advance.
The best way to do this in the background is to register a receiver in your manifest. This means the receiver will keep listening for broadcasts even if the app is off.
What you need is this particular action when registering your receiver Intent.ACTION_TIME_TICK
There is a more detailed answer about this matter here Time change listener
Another solution is to use the Calendar class in java. With it you can get the exact minutes passed from a point in the past to this moment. This way you don't have to worry about parsing dates and similar. I can't provide you specific examples because me myself have not used the Calendar class very much, but I'm sure you can find lots of stuff in the official documentation and on stackoverflow about it.
No need to work with Date objects, the simple usage of System.currentTimeMillis() should work. Here's a basic outline:
long mLastManaRefreshTime = System.currentTimeMillis();
void refreshMana()
{
long timeDelta = System.currentTimeMillis() - mLastManaRefreshTime;
mLastManaRefreshTime = System.currentTimeMillis();
float totalManaToRefresh = (float)AMOUNT_TO_REFRESH_IN_ONE_MINUTE * ((float)timeDelta / 60000f);
mMana += totalManaToRefresh;
if (mMana > MAX_MANA)
mMana = MAX_MANA;
}
This method is of course just an outline. You will need to call this once every update cycle. It will calculate how much time passed since the last time refreshMana was called, and replenish the required amount.
If you need this to work while the game is off, you can save the mLastManaRefreshTime to a SharedPreferences object and reload it when the game loads up again.
With System.currentTimeMillis() you can a current time-stamp in milliseconds.
You could save the latest time-stamp in your Preferences with every 5 min tick of the running game. For the other case, when your App comes back from a state where it does not do this (i.e. called the first time, woken up etc.).
Something like this:
int manacycles = ((int) (((System.currentTimeMillis() - oldtimestamp) / 1000) / 60) ) % 5;
would give you the number of Mana points you would have to add.
Alternately you could do the same thing with the Calendar class.
Also keep in mind players could cheat this way by simply changing their time. If your game is online you could get the time from the internet, with something like this:
try {
TimeTCPClient client = new TimeTCPClient();
try {
// Set timeout of 60 seconds
client.setDefaultTimeout(60000);
// Connecting to time server
// Other time servers can be found at : http://tf.nist.gov/tf-cgi/servers.cgi#
// Make sure that your program NEVER queries a server more frequently than once every 4 seconds
client.connect("nist.time.nosc.us");
System.out.println(client.getDate());
} finally {
client.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}

Android Sensor Data Collection is not working properly

I developed a Data collector which collects data from Accelerometer, Gyroscope, Magnetometer and it worked fine for a while. Then I added Linear Acceleration to it as well (After 4 months, this week). Now both the version are behaving very strangely. Sometime they log the data perfectly when I do some physical activities like walking etc. However, sometimes it doesn't update sensors values and just repeat old values i.e each sensor value is updated lets after 5 seconds, 2 sec etc randomly and I need a sampling rate of 50 samples per second. I experimented with 10-15 participants and all my data was invalid because of this. The strange things is that the same app has worked perfectly before. I can't find any problem in it. I am placing some of the snapshots here. May be if someone can point to any bug or something ?
The buffered Writter:
FileWriter fow;
BufferedWriter bow;
extfile = new File(extfilepath, message + ".csv");
fow = new FileWriter(extfile);
bow = new BufferedWriter(fow);
This bow.writer is then being used in timertask thread to log data every 20 milliseconds.
Can anyone please comment or help me with this ? This weird behavior of this app is beyond my understanding.
Check that you have a wake lock acquired if your application goes to background. I've used PowerManager.PARTIAL_WAKE_LOCK successfully in a data collection application.
When your display turns off, your application is at least paused (and system might even stop it). The partial wake lock "Ensures that the CPU is running; the screen and keyboard backlight will be allowed to go off." So reading between the lines it means that otherwise your CPU might go to sleep for small periods of time in order to save power.
Did you forget to paste in:
else if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION){} ?
Are you using the accelerometer data, then subtracting gravity?
OK. What's your code look like to call the timer?? Something like this?
Timer updateTimer = new Timer("linear accel");
updateTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
updateGUI();
}
}, 0, 100);
}
private void updateGUI() {
runOnUiThread(new Runnable() {
public void run() {} } ?

Android: Chronometer SetBase in minutes

Is there a way by which using the android chronometer class to set base of the chronometer in 15 minutes and from that period the times goes down until 0 seconds?
I have tried with setBase(60000) but this isn't work.
Check out this thread Android: chronometer as a persistent stopwatch. How to set starting time? What is Chronometer "Base"? as well as this thread Android - Get time of chronometer widget. Neither answers your question directly, but the nuggets there should lead you to an answer.
In general the chronometer works like this (if you would like to set the Base to a specific nr):
mChronometer.setBase(SystemClock.elapsedRealtime() - (nr_of_min * 60000 + nr_of_sec * 1000)))
what you are asking can be done through a countdown (http://developer.android.com/reference/android/os/CountDownTimer.html)
Or create your own countdown by using the chronometer like this (more work should be done cause i just wrote this and did not test it yet)
private OnChronometerTickListener countUp = new OnChronometerTickListener(){
#Override
public void onChronometerTick(Chronometer chronometer){
long elapsedTime = (SystemClock.elapsedRealtime() - mChronometerCountUp.getBase()) / 60000;
Log.v("counting up", elapsedTime);
// you will see the time counting up
count_down--;
if(count_down == 0){
mChronometerCountUp.stop();
}
// an int which will count down,
// this is not (very) accurate due to the fact that u r using the update part of the chronometer
// u just might implement the countdown i guess
// or 2 chronometers (one counting up and an other counting down using the elapsed time :p)
// just remember programming is creating ur solution to problems u face its like expression urself
};
};
http://developer.android.com/reference/android/widget/Chronometer.html
For set the base time you can use elapsedRealtime(), and you can output format with setFormat()

Precision of delay

I have a problem with this code used for Android (Java)
handler.postDelayed(new Runnable(){
public void run(){
// Your code goes here...
}
}, 500);
If the delay is about 500ms then the program seems to repeat the task at 0.5s, but if I change to less than 100ms or even less it does not follow any more. I test the brightness change and for a while it can repeat the change of brightness at that rate, but then slow down and come back to normal flash rate again. It seems unstable. Do you have any code that give exact delay regardless of the load of the phone's CPU.
Many thanks
Not from Java, no; stock Java isn't a real-time system.
Timing precision is subject to the whims of the JVM and the OS's scheduler. You may be able to get incrementally more precise, but there's no guarantee of the kind of precision you're looking for.
You might be able to do something more precise if you use a CountDownTimer which has a periodic tick. Essentially you set it to count down for a period which can be hours if need be, and there are two methods one method is called on each tick, and the other at the end of the timer at which point you could start another one. Anyway you could set the tick to be very fast, and then only kick off the code at the delay point by check the actual time difference in the click. I think thats about the best you could do. Essentially inside the tick you would issue a signal if the right amout of time had actually passed. That signal would either kick off the thread or release something the already running thread was waiting on. What is the value of the CountDownTimer, I guess its just that you can do a very frequent polling, and elapsed time check. Although its not guaranteed, the time between the ticks you can set it to a high frequency and check/poll very frequently. This could lead to a smooth performance not unlike a realtime system. Its more likely to be accurate because its just issuing a signal and not taking up the resources of threading just to issue the signal. You might also try an IntentService to perform the tasks and just call startService(intentToIntentService) each call. See if the threading works better inside a service like IntentService which does queue them up I believe.
Date startDate = new Date();
long startTime = startDate.getTime();
// Tick called every 10th of a second. OnFinish called at Signal.
CountDownTimer ctDownTimer = new CountDownTimer(30000, 100) {
long startIntervalTime=startTime;
public void onTick(long millisUntilFinished) {
Date now = new Date();
long nowTime = now.getTime();
if ((startIntervalTime - nowTime) > 100)
{
issueSignal();
intervalStartTime=nowTime;
}
now=null;
}
public void onFinish() {
Log.d("MyClass", "Done") // Maybe start out.
}
}.start();

Android Dev: Timer not honoring dynamic interval period

im working on a audio profile switcher for android and as part of the entire project, i have a service that is running in the background using the following timer code:
timer.scheduleAtFixedRate( new TimerTask() {
public void run() {.....}, 0, nextUpdateInterval);
what im noticing is that the timer is not honoring the dynamically generated next update interval period...the nextUpdateInterval is declared as private static long which is initialized to 30000 (30 seconds) for the first run....then once a profile is found, i do some math and update the nextUpdateInterval...i have converted the nextUpdateInterval value back out to hours/minutes for debugging purpose, and the calculation is working as expected...like it shows me in hours and minutes, when the next timer execution should take place...
nextUpdateInterval calculation: long entirePeriodDiff = toTimeMiliseconds - fromTimeMiliseconds;
then once a profile is found, i calculate the elapsedTime like so: long elapsedTime = rightNowDate.getTime() - fromDate.getTime();
and then i update the nextUpdateInterval: nextUpdateInterval = entirePeriodDiff - elapsedTime;
one example scenario: Profile of 'Work' is set from 9AM to 4:30PM, the service/app is executed at 2:02PM (EST), my toast message is executing constantly and is acting as a count down telling me how much time is left...in this case 2:28 and decreasing...ideally this should not display until the 2:28 is up...any ideas?
As per android doc:
With fixed-rate execution, the start time of each successive run of a task is scheduled without regard for when the previous run took place. This may result in a series of bunched-up runs (one launched immediately after another) if delays prevent the timer from starting tasks on time.
I think that could be the reason, may be you need to consider alternative 'fixed period'

Categories

Resources