steviesama
steviesama

Reputation: 337

High frequency Android UI Thread updating from a separate thread?

I'm trying to update an EditText at a high rate of speed so the numbers flow more smoothly. But I've tried different combinations of techniques and objects, such as AsyncTask, Thread->Runnable with a handler, Timer->TimerTask with a handler, and no matter how long I set the timing for, it doesn't seem to get any better than approximately 1 second.

I'm thinking that it may be that fact that it is having to travel through the message pump, but I can't be sure. Does anyone have any idea how I could get an update frequency of at least 250 to 500 milliseconds? Higher frequency would be preferable, but that would do.

Update 2: I commented out everything but the count++; and I update one of the edit texts with this value and it is indeed updating very fast. So something that is happening in updateUi() is slowing it down. I just don't know what.

Update 1: I switched real-time usage of SharedPreferences which I was using to test with primitives just in case that was part of the problem. The performance appears to be the same. The only think that happens on a regular basis is an updateUi() function which runs on a timer. The code is below, and this is what I'm expecting to update quickly, but doesn't:

private void updateUi() {

    lastDate = cal;
    cal = Calendar.getInstance();

    synchronized(cal) {

        //if the current date is greater than the last stored date
        if(compareDates(cal, lastDate) > 0 && count == 0) {
            //disable timer
            calorieTimer.cancel();
            //dereference timer...probably not necessary
            calorieTimer = null;
            double bankBmr = currentBmr.getBMR() * 
                    activityLevelMultipliers[activityLevelSpinner.getSelectedItemPosition()];
            metrics.calorieBank += (int)bankBmr - metrics.caloriesEaten;
            prefsEditor.putInt("CALORIE_BANK", metrics.calorieBank);
            prefsEditor.putInt("CALORIES_EATEN", 0);
            prefsEditor.commit();
            metrics.caloriesEaten = 0;
            //update lastDate to prevent multiple calorie banking
            lastDate = cal;
            count++;
            Log.e("updateUi()", "count #" + count);
            //set up timer again
            setupCalorieTimer();
        }

        caloriesEatenEditText.setText("" + metrics.caloriesEaten);
        caloriesRemainingEditText.setText(String.format("%d", (int)activeBmr - metrics.caloriesEaten));
        bankEditText.setText("" + metrics.calorieBank);
        estimatedWeightEditText.setText(String.format("%.2f", metrics.currentWeight - metrics.calorieBank / 3500.0)) ;
        //update the time
        time.setToNow();
        //update calories available
        caloriesAvailableEditText.setText(
                String.format("%.2f", activeBmr * percentageOfDay(time) - metrics.caloriesEaten));

    } //End synchronized(cal)

} //End updateUi()

private void setupCalorieTimer() {              
    //create handler to post update back to ui thread
    final Handler handler = new Handler();          

    calorieTimer = new Timer();

    calorieTimer.schedule(new TimerTask() {
        @Override
        public void run() {
            //post to ui thread
            handler.post(new Runnable() {
                @Override
                public void run() {
                    updateUi();
                }                   
            });     
        }           
    }, 0, 50);      
}

Upvotes: 1

Views: 1858

Answers (4)

steviesama
steviesama

Reputation: 337

It turned out that a function I didn't initially list was using the android.text.format.Time object which only has 1 second resolution. So it wasn't even poor performance, it was just that the object only elapsed once per second so it couldn't possibly update any faster. Below are the first function that caused the false alarm, and the second one was the "fix". Hopefully this will help anyone who mistakenly thinks poor performance was the culprit like I did.

//"caused" the problem
private double percentageOfDay(Time time) {
    //factor the hour
    double percentage = time.hour / 24f;
    //factor the minute
    percentage += time.minute / 1440f;
    //factor the second
    percentage += time.second / 86400f;     

    return percentage;
}

//This fixed the problem
private double percentageOfDay(Calendar cal) {
    //factor the hour
    double percentage = cal.get(Calendar.HOUR) / 24f;
    //factor the minute
    percentage += cal.get(Calendar.MINUTE) / 1440f;
    //factor the second
    percentage += cal.get(Calendar.SECOND) / 86400f;
    //factor the millisecond
    percentage += cal.get(Calendar.MILLISECOND) / 86400000f;

    return percentage;
}

Upvotes: 1

HHK
HHK

Reputation: 5320

I can not reproduce this problem. With the following code updates are really fast for example (blurringly fast on a low-end tablet):

package com.mycompany.myapp42;

import android.app.*;
import android.os.*;
import android.widget.*;
import java.text.*;
import java.util.*;

public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final TextView tv = (TextView) findViewById(R.id.textView);
        new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    while (true)
                    {
                        runOnUiThread(new Runnable()
                            {
                                @Override
                                public void run()
                                {
                                    tv.setText(new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()));
                                }
                            });
                        try
                        {
                            Thread.sleep(10);
                        }
                        catch (InterruptedException e)
                        {}
                    }
                }
            }).start();
    }
}

EDIT

There are performance problems for me too if the soft keyboard (SwiftKey) is visible.

Upvotes: 1

Tal Kanel
Tal Kanel

Reputation: 10785

there is no suppose to be any problem updating in high frequency a EditText using Handler + delayed Runnable or AasyncTask with onProgressUpdate() callback. I can tell you that I did it lot's of times, with interval of 30-100 milliseconds, and it worked great.

that's what leading me to believe you are doing something else wrong. please post your code.

my feeling is that you are performing other code that blocks the UI thread for not reasonable amount of time (more then few milliseconds), and because of that - the part of the code that running your EditText update on the UI thread is limited. blocking the main thread usually happens when performing in it IO file operations, database writings, image decodings or other heavy calculations, so if you have one of those on the main thread - they would be the blame.

Upvotes: 1

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13515

try AsyncConnector from df4android, it passes messages pretty fast.

Upvotes: 1

Related Questions