I have a Service that sends an Intent to my Activity every 0.1 seconds. I use it to update a custom implementation of a Chronometer. Here everything goes right. The problem comes when I want to update 14 TextView I have in a TableView inside a Fragment in my Activity. Here the app is very slow.
The method in my Activity where it receives the Intent from the Service:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long milis = intent.getLongExtra("milis",0);
if( mFragment != null)
mFragment.Update(milis);
}
};
The code inside the Fragment where I update the TextViews:
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se aumenta la cantidad pasada como parĂ¡metro
for(int i=0;i<7;++i) {
long mCurrentMilis1 = mVectorMilis1.get(i);
long mCurrentMilis2 = mVectorMilis2.get(i);
TextView1 t1 = mListaTitularLayoutLocal.get(i);
TextView1 t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
#Sherif brings up a good point about hidden alpha values that bog down your application a lot.
Depending on your platform you may also want to check
<application android:hardwareAccelerated="true"... />
Another thing you can look into that may help performance is not firing off all those Intents. Once you start firing intents you are getting the system involved and depending on how they are getting resolved it may take some extra time.
For this issue I like to use Handlers. They are more light weight than intent.
You may also want to look at AsyncTask. This is basically like a thread, but also gives hooks that run on the UI Thread so you can perform both perform a background operation and update the UI without have to post runnables.
EDIT: Lastly, you can always run your layouts through the layoutopt tool. I was personally told by Romain Guy himself that if your drawing too slow, than you need to draw less. Just check out a screenshot (from a less than ideal view tree, but well within the max) from the profiling tool. You can see how much of the resources view drawing takes up. It's very important to keep this as lean as possible if you want your app to be responsive.
EDIT: It is no longer called layoutopt, it's called lint. Check your ~/android-sdk/tools/
I have once faced a situation where a fragment was really slow.
I am just predicting that your fragment has some kind of alpha and it is drawn on a 'heavy' activity.
The conclusion is that each time you are setting the text of a textview your whole view hierarchy is being invalidated.
It seems that fragments have this flaw. Anyway, use some layout instead of the fragment and check if it remains 'slow'.
ADDITION: A wrap_content textview will cause much more delay after a setText than a fill_parent textview.
You're likely running into slowdowns due to layout management with TableLayout and TextView. Every time you update text in one of those, a large amount of view measuring has to take place in order to put the characters in the right place on the screen. You should really just profile the app yourself using Traceview to find out. More information at: http://developer.android.com/tools/debugging/debugging-tracing.html
I've had the exact same issue you're seeing with the same type of layout (Fragment > TableLayout > Multiple TextViews). One way to test if your TableLayout/TextView setup is to blame is simply replace all that with a single TextView. That will probably run pretty well. Then put your 14 views into a FrameLayout or RelativeLayout. Even if they all overlap, you should still get decent performance, because it's the complexity of the TableLayout view measurements that's really causing slowdown.
As someone said you can use HardwareAccelerated but this is not a great solution, you will waste ram and cpu if you can't solve it in a different way. A solution probably more safety is to reduce the number of TextView. Try to reduce 14 to 7 and it will go twice faster. Usually is hard to do it but if you put the objects in a strategy position a pair of TextView one above other can be together if you make a TextView with two lines. And don't forget that findViewById is so expensive, if you will use a view object often find it one time and hold its reference.
Benchmarks are always useful for determining where slowness actually comes from, but I feel pretty confident suggesting that sending an Intent is probably much slower than updating 14 TextViews. Sending 10 Intents per second is a sign that you're Doing It Wrong (TM). This is just isn't what they're for.
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
Updating 14 TextViews per second isn't inherently complex; you should be able to easily achieve this with a more appropriate application design. ASyncTask or Handler come to mind as possible tools, but it's hard to know what's best without knowing more about exactly what you're trying to do.
You can try to declare vars outside the loop :
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se
// aumenta la cantidad pasada como parĂ¡metro
long mCurrentMilis1;
long mCurrentMilis2;
TextView1 t1;
TextView1 t2;
for(int i=0;i<7;++i) {
mCurrentMilis1 = mVectorMilis1.get(i);
mCurrentMilis2 = mVectorMilis2.get(i);
t1 = mListaTitularLayoutLocal.get(i);
t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
And to setText() with mixed type, you can try setText("" + milis + mCurrentMilis2);
Related
Hi I'm implementing click listeners in the following way but after some time the methods and variables inside the listener's closure get the wrong values or something. Let me explain the implementation of the listener a little better a for loop creates the listener for a set of image views then later in the program the for loop is called a second time and it resets the listener methods and variables to different values. Everything works great for about 30 minutes but then for some reason, the listener's methods and variables start having the wrong values. Has anybody ever heard of this behavior or can tell me where I've gone wrong with the code? Keep in mind that the listener I'm about to paste here is just a small piece of a 1014 line class. I'm hoping somebody can spot How I'm implementing the listener wrongly and can give me some advice on how to "reset" the listener so that it's variables and values stay over time. Hopefully you can read the code without putting it in an editor but feel free to copy it for readability's sake Here is the code for the image view listener with comments.
//image views are held in an array
//set an image view in its imageview container
imgArr0[slotId1].invalidate()
imgArr0[slotId1].setImageDrawable(null)
//drw is not defined in this example
imgArr0[slotId1].setImageDrawable(drw)
/*if video or image id is set to image then set a listener for the image
*/
/*slotId1 is not defined in this example but it is simply a counter to iterate over the ImageView array
*/
if (videoOrImageId0[slotId1] == "image") {
//null any listeners that might be attached to the image view
imgArr0[slotId1].setOnClickListener(null)
//set or reset the listener
imgArr0[slotId1].setOnClickListener() {
`enter code here`//if the current config is portrait then set a new image image
if (currentConfig0 == "portrait0") {
act0.lrgImage0.invalidate()
act0.lrgImage0.setImageDrawable(null)
/*drw is not defined in this example but works fine in the production script
*/
act0.lrgImage0.setImageDrawable(drw)
}
--calmchess
ccc tv application with problem.
(https://i.stack.imgur.com/PjdbN.jpg)![enter image description here](https://i.stack.imgur.com/FaMnc.
I was able to partially solve this question by destroying all the image views and their associated click listeners then rebuilding those... However I don't consider this issue completely solved so if anybody can provide a better solution I'd love to hear it because rebuilding the images every few minutes has to be using a lot of unnecessary hardware resources.
--calmchess
Running my application causes ~40% CPU usage on my Phone:
final String position = String.format("%02d:%02d:%02d", time.getHours(), time.getMinutes(),
time.getSeconds());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
c.mTxtPosition.setText(position);
...
By commenting out the setText method the CPU Usage drops to the expected level of ~4%. The method is invoked every second and does refresh ImageViews, CustomViews ... without causing the same load excess.
Besides the CPU Usage dalvik constantly reports garbage collecting of about 10-1000 objects just by calling setText().
Creating a tracefile like this:
Debug.startMethodTracing("setText");
c.mTxtPosition.setText(position);
Debug.stopMethodTracing();
traceview lists the following methods as Top 5 by their respective exclusive CPU%:
ViewParent.invalidateChildInParent(16%)
View.requestLayout(11%)
ViewGroup.invalidateChild(9%)
TextView.setText(7%)
toplevel(6%)
Has anybody an explanation for this?
I noticed this myself a while ago, I think the problem is that every time you call setText, the size of the textbox can change, thus requiring the entire screen to go through relayout (expensive).
I haven't tried this myself yet, but if your textbox is simple and can be made to be a relatively fixed size, maybe try to subclass TextView and create a view that does not resize itself on setText, but rather just draws whatever it can into the existing area? That would save a lot of time.
Perhaps theres already a flag to setText that can make it do this, but I'm not aware of it, though I haven't searched closely.
In my case, I update a TextView from touch event, which cause a lot of updating The solution was to change the TextView layout_width & layout_height to fixed sized.
some possible improvements :
try using a handler which updates the textview every 0.5 seconds instead of a thread that does it.
make the runnable a final constant object instead of craeting a new one every second.
consider checking that the time has changed (newTimeInMs-LastPublishedTimeInMs>=1000) before telling the textview to update itself.
instead of String.format , try using StringBuilder . however , you won't enjoy the locale solution that the String.format gives (for example , for arabic digits) .
In my case it was this property of TextView:
android:ellipsize="marquee"
Removing it speeded up setting text.
If you look at the source code of setText method you can see that it does a lot of heavy lifting - there is measuring, drawing and object allocations, all of which run on the main thread.
You can use the new PrecomputedText API in order to do all of this on the background thread and make setText faster.
You can use the following working example using kotlin & coroutines
private fun TextView.setTextAsync(text: String) {
val textView = this
lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(text, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
For more details you can read an article about it on my blog
https://androidexplained.github.io/android/ui/2020/10/21/improving-textview-settext-performance.html
I'm making some kind of "karaoke" android apps
So, basically, I have some TextViews and I want to change the text color programmatically while playing audio.
TextViews have the "begin_time" and "end_time" attributes which relates to the position in the audio file
Playing audio, and gathering informations about that is easy with MediaController
So, now I'm trying to change the color dynamically ... I tried with a separate thread and many other ways, but I always stuck with the same issue
" android.view.ViewRoot$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views. "
I tried to learn more about that, and what I learned is that everything related with the UI has to be done in the UI Thread.
Most of people use handlers to perform GUI update in background thread. But I didn't made this damn thing worked out ... So any suggestion about handlers and how they work is welcome.
Anyways, why not using AsyncTask ?
Quote from the "Processes and Threads dev' guide"
" Perhaps the best solution, though, is to extend the AsyncTask class,
which simplifies the execution of worker thread tasks that need to
interact with the UI. "
Actually, I'm using this AsyncTask, but it doesn't "simplifies" threading
Or Maybe they're is an other solution for GUI update from time event ?
Well, in fact, i don't want to make "just a karaoke" Textview have to be clickable, so when the user click on a word, the audio goes to that position in audio, and textview color have to change dynamically
So, I thought about using Animation for smooth color changing. Or maybe TransitionDrawable, but it use layers and canvas.
I also find out this, with ForegroundColorSpan:
Spannable WordtoSpan = new SpannableString("I know just how to whisper");
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 1, 13, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tv = new TextView(context).setText(WordtoSpan);
But not sure it will be dynamic enough due to calculation of the end of the ForegroundColorSpan.
So considering using only TextView, what is the best way to achieve dynamic textview color changing (with those solutions, or any other)?
Second edit
Well, i'm almost done with this project ...
I am now able to color word dynamically over time, but the problem is that it's not synchronized with audio.
i.e :
let's take a sentence like "hello, my name is john and i'm late for my job"
If audio is playing [i'm] the colored text is [job]
So, it's just a synchronization problem, but i'm facing it for several weeks.
The text is organized into a linked list
elements of the linked list are Object (because i'm having both Word and Ponctuation class) but both have a begin and end position, corresponding to their relative position in audio in milliseconds.
so ... I have something like that :
Hello : begin = 0, end = 1000
my : begin = 1200, end = 1400
and so on ...
In the main activity now, I have a Runnable that deal with text coloration
And I also have 3 Object, previous_element, current_element, and next_element
At the begining of the runnable, the color of the previous_element is set back to White, so only the current word is colored.
Then, the current_element's color is set to Yellow, or whatever ...
Then, I check if next_element.getBegin() >= current position in audio
... If it is, that mean the current word is now over, so I change the current_element to next_element, and start over the process using Handler.post(MyRunnableThatDoesColoration)
So I'm not understanding where it's going wrong
If anyone one have a clue ?
" android.view.ViewRoot$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views. "
This can be achieved by using runOnUIThread:
runOnUiThread(new Runnable() {
public void run() {
//Your UI Changing code HERE!
}
});
If you see the karaoke as basic gui (textViews), I think it won't be dynamic enough. You should use Android Canvas or a gameEngine (Andengine..).
I have a source code editor for Android, and I have a line numbers counter that's to the left of the main EditText with source code inside it.
I have the following function I use for updating the line numbers text view:
String lineDelimiter = "\n";
public void updateLineNumbers(){
int lines = textBox.getLineCount();
lineNums.setText(1 + lineDelimiter);
for(int i = 2; i < lines; i++){
lineNums.append(i + lineDelimiter);
}
}
All this is fine, but the problem is when you have a document with say 200 odd lines you start to notice a little delay when adding lines. Is this cause Android TextView's setText/append methods are a little slow? Or is it the concatination that's causing the delay?
I've also made a similar function that appends a line number when the user adds a line number, and vice versa, as opposed to clearing the TextView and adding each line numbers again like the function above does. But this function still lags the app when the user adds/removes line(s).
How can I stop this? I can't think of what to do and it's stressing me out because it's lagging my app and rendering it unusable for large files! :(
Thanks for looking!
SOLUTION
I've found a way to have fast line numbers, which is to use a custom TextView with onDraw(Canvas canvas) overriden and to draw them that way which results in lag-free line numbers :).
Is this cause Android TextView's setText/append methods are a little slow? Or is it the concatination that's causing the delay?
Use Traceview and find out.
Off the cuff, I would imagine that calling append() a whole bunch of times on a TextView will be vastly slower than calling append() a bunch of times on a StringBuilder, then calling setText() once on the TextView.
How can I stop this?
Don't handle line numbers that way. For example, put a TextView to the left of the EditText, and put your line numbers in the TextView, one per line.
I'm making a quiz-like game, where user answers each question before they are allowed to go to the next one.
Characteristics of my app:
Each session will have
around 10-30 screens.
In general, the screens are heterogenious in layout
structures, but they can be
classified into 5-6 types.
The expected time that user interacts with each screen is 10-30
seconds
Once user goes to the next screen,
the previous one is not needed
anymore (he never goes back)
I want to have a nice sliding
transition animation when going from
one screen to the next
Implementations I'm considering:
Start a new Activity for each
screen in the 'forwarding' style,
i.e. start the next screen then
finish the current one.
Load all the views before hand and
use ViewAnimator
It looks like none of my current solution is good. Can you help me on a solution that is good in terms of memory consumption, battery consumption, and responsiveness?
Thank you very much.
OK below is what I did. It turned out I manually set the animation
onCreate() {
mAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_in_right);
mViewPool = new View[] { /* A few views for re-using, each of different type */ };
}
proceed() {
nextView = getView(type);
mFrame.removeAllChilds();
mFrame.addView(nextView);
nextView.startAnimation(mAnimation);
}
getView(int type) {
View view = mViewPool[type];
// reset some subviews if neccessary
return view;
}
Where mFrame is whatever ViewGroup you think is appropriate, not neccessarily ViewAnimator. Mine happens to be ScrollView.
If you see any potential problem with this approach, please let me know. Many thanks.