Ich habe ein RecyclerView-Beispiel erstellt, das auf dem Handbuch zum Erstellen von Listen und Karten basiert . Mein Adapter hat eine Musterimplementierung nur zum Aufblasen des Layouts.
Das Problem ist die schlechte Bildlaufleistung. Dies in einer RecycleView mit nur 8 Elementen.
In einigen Tests habe ich überprüft, dass dieses Problem in Android L nicht auftritt. In der KitKat-Version ist jedoch eine Leistungsminderung erkennbar.
Antworten:
Ich hatte kürzlich das gleiche Problem, daher habe ich Folgendes mit der neuesten RecyclerView-Unterstützungsbibliothek getan:
Ersetzen Sie ein komplexes Layout (verschachtelte Ansichten, RelativeLayout) durch das neue optimierte ConstraintLayout. Aktivieren Sie es in Android Studio: Gehen Sie zu SDK Manager -> Registerkarte SDK Tools -> Support Repository -> überprüfen Sie ConstraintLayout für Android und Solver für ConstraintLayout. Zu den Abhängigkeiten hinzufügen:
compile 'com.android.support.constraint:constraint-layout:1.0.2'
Wenn möglich, stellen Sie alle Elemente der RecyclerView auf dieselbe Höhe . Und füge hinzu:
recyclerView.setHasFixedSize(true);
Verwenden Sie die Standardmethoden für den RecyclerView- Zeichnungscache und passen Sie sie an Ihren Fall an. Dazu benötigen Sie keine Drittanbieter-Bibliothek:
recyclerView.setItemViewCacheSize(20); recyclerView.setDrawingCacheEnabled(true); recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Wenn Sie viele Bilder verwenden , stellen Sie sicher, dass deren Größe und Komprimierung optimal sind . Das Skalieren von Bildern kann sich auch auf die Leistung auswirken. Das Problem hat zwei Seiten: das verwendete Quellbild und die decodierte Bitmap. Das folgende Beispiel gibt Ihnen einen Hinweis zum Dekodieren eines aus dem Internet heruntergeladenen Bildes:
InputStream is = (InputStream) url.getContent(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap image = BitmapFactory.decodeStream(is, null, options);
The most important part is specifying
inPreferredConfig
- it defines how many bytes will be used for each pixel of the image. Keep in mind that this is a preferred option. If the source image has more colors, it will still be decoded with a different config.Make sure onBindViewHolder() is as cheap as possible. You can set OnClickListener once in
onCreateViewHolder()
and call through an interface a listener outside of the Adapter, passing the clicked item. This way you don't create extra objects all the time. Also check flags and states, before making any changes to the view here.viewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Item item = getItem(getAdapterPosition()); outsideClickListener.onItemClicked(item); } });
When data gets changed, try to update only the affected items. For example instead of invalidating the whole data set with
notifyDataSetChanged()
, when adding / loading more items, just use:From Android Developer Web Site :
But if you need to use it, maintain your items with unique ids:
adapter.setHasStableIds(true);
Even if you do everything right, chances are that the RecyclerView is still not performing as smoothly as you would like.
quelle
recyclerView.setItemViewCacheSize(20);
can improuve the performance. ButrecyclerView.setDrawingCacheEnabled(true);
andrecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
though! I am not sure these will change anything. These areView
specific calls that allows you to programmatically retrieve the drawing cache as a bitmap and use it for your advantage later.RecyclerView
does not seem to do anything about it.I solved this problem by adding the following flag:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#setHasStableIds(boolean)
quelle
I discovered at least one pattern that can kill your performance. Remember that
onBindViewHolder()
is called frequently. So anything you do in that code has the potential to slam your performance to halt. If your RecyclerView does any customization, it's very easy to accidentally put some slow code in this method.I was changing the background images of each RecyclerView depending on the position. But loading images takes a bit of work, causing my RecyclerView to be sluggish and jerky.
Creating a cache for the images worked wonders;
onBindViewHolder()
now just modifies a reference to a cached image instead of loading it from scratch. Now the RecyclerView zips along.I know that not everyone will have this exact problem, so I'm not bothering to load code. But please consider any work that is done in your
onBindViewHolder()
as a potential bottle-neck for poor RecyclerView performance.quelle
In addition to @Galya's detailed answer, I want to state that even though it may be an optimization issue, it is also true that having the debugger enabled can slow things down a lot.
If you do everything to optimize your
RecyclerView
and it still doesn't work smoothly, try switching your build variant torelease
, and check how it works in a non-development environment (with the debugger disabled).It happened to me that my app was performing slowly in
debug
build variant, but as soon as I switched to therelease
variant it worked smoothly. This doesn't mean that you should develop with therelease
build variant, but it is good to know that whenever you are ready to ship your app, it will work just fine.quelle
I had a talk about
RecyclerView
's performance. Here are slides in English and recorded video in Russian.It contains a set of techniques (some of them are already covered by @Darya's answer).
Here is a brief summary:
If
Adapter
items have fixed size then set:recyclerView.setHasFixedSize(true);
If data entities can be represented by long (
hashCode()
for instance) then set:adapter.hasStableIds(true);
and implement:
// YourAdapter.java
@Override
public long getItemId(int position) {
return items.get(position).hashcode(); //id()
}
In this case
Item.id()
would not work, because it would stay the same even ifItem
's content has changed.P.S. This is not necessary if you are using DiffUtil!
Use correctly scaled bitmap. Don't reinvent the wheel and use libraries.
More info how to choose here.
Always use the latest version of
RecyclerView
. For instance, there were huge performance improvements in25.1.0
- prefetch.More info here.
Use DiffUtill.
DiffUtil is a must.
Official documentation.
Simplify your item's layout!
Tiny library to enrich TextViews - TextViewRichDrawable
See slides for more detailed explanation.
quelle
I'm not really sure if the usage of
setHasStableId
flag is going to fix your issue. Based on the information you provide your performance issue could be related to a memory issue. Your application performance in terms of user interface and memory is quite related.Last week I discovered my app was leaking memory. I discovered this because after 20 minutes using my app I noticed the UI was performing really slow. Closing/opening an activity or scrolling a RecyclerView with a bunch of elements was really slow. After monitoring some of my users in production using http://flowup.io/ I found this:
The frame time was really really high and the frames per second really really low. You can see that some frames needed about 2 seconds to render :S.
Trying to figure it out what was causing this bad frame time/fps I discovered I had a memory issue as you can see here:
Even when the average memory consumption was close to the 15MB at the same time the app was dropping frames.
That's how I discovered the UI issue. I had a memory leak in my app causing a lot of garbage collector events and that's was causing the bad UI performance because the Android VM had to stop my app to collect memory every single frame.
Looking at the code I had a leak inside a custom view because I was not unregistering a listener from the Android Choreographer instance. After releasing the fix, everything became normal :)
If your app is dropping frames due to a memory issue you should review two common errors:
Review if your app is allocating objects inside a method invoked multiple times per second. Even if this allocation can be performed in a different place where your application is becoming slow. An example could be creating new instances of an object inside a onDraw custom view method on onBindViewHolder in your recycler view view holder. Review if your app is registering an instance into the Android SDK but not releasing it. Registering a listener into a bus event could also be possible leak.
Disclaimer: The tool I've been using to monitor my app is under development. I have access to this tool because I'm one of the developers :) If you want access to this tool we will release a beta version soon! You can join in our web site: http://flowup.io/.
If you want to use different tools you can use: traveview, dmtracedump, systrace or the Andorid performance monitor integrated into Android Studio. But remember that this tools will monitor your connected device and not the rest of your user devices or Android OS installations.
quelle
Its also important to check the parent layout in which you put in your Recyclerview. I had a similar scrolling issues when I testing recyclerView in a nestedscrollview. A view that scrolls in another view that scroll can suffer in performance during scrolling
quelle
In my case, I found out that the notable cause of the lag is frequent drawable loading inside
#onBindViewHolder()
method. I solved it just by loading the images as Bitmap once inside the ViewHolder and access it from the mentioned method. That is all I did.quelle
In my RecyclerView, I use Bitmap Images For background of my item_layout.
Everything @Galya said is true (and I thank him for his great answer). But they didn't work for me.
This is what solved my problem:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
For more information please read this Answer.
quelle
In mycase I have complex recyclerview childs. So It affected the activity loading time (~5 sec for activity rendering)
I load the adapter with postDelayed() -> this will give the good result for activity rendering. after activity rendering my recyclerview load with smooth.
Try this answer,
recyclerView.postDelayed(new Runnable() { @Override public void run() { recyclerView.setAdapter(mAdapter); } },100);
quelle
I see in the comments that you are already implementing the
ViewHolder
pattern, but I will post an example adapter here that uses theRecyclerView.ViewHolder
pattern so you can verify that you are integrating it in a similar way, again your constructor can vary depending on your needs, here is an example:public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> { Context mContext; List<String> mNames; public RecyclerAdapter(Context context, List<String> names) { mContext = context; mNames = names; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()) .inflate(android.R.layout.simple_list_item_1, viewGroup, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { //Populate. if (mNames != null) { String name = mNames.get(position); viewHolder.name.setText(name); } } @Override public int getItemCount() { if (mNames != null) return mNames.size(); else return 0; } /** * Static Class that holds the RecyclerView views. */ static class ViewHolder extends RecyclerView.ViewHolder { TextView name; public ViewHolder(View itemView) { super(itemView); name = (TextView) itemView.findViewById(android.R.id.text1); } } }
If you have any trouble working with
RecyclerView.ViewHolder
make sure you have the appropriate dependencies which you can verify always at Gradle PleaseHope it resolves your problem.
quelle
This helped me getting more smooth scrolling:
override the onFailedToRecycleView(ViewHolder holder) in the adapter
and stop any ongoing animations (if any) holder."animateview".clearAnimation();
remember to return true;
quelle
I solved it by this line of code
recyclerView.setNestedScrollingEnabled(false);
quelle
Adding to @Galya's answer, in bind viewHolder,I was using Html.fromHtml() method. apparently this has performance impact.
quelle
i Solve this issue by using the only one line in with Picasso library
.fit()
Picasso.get().load(currentItem.getArtist_image()) .fit()//this wil auto get the size of image and reduce it .placeholder(R.drawable.ic_doctor) .into(holder.img_uploaderProfile, new Callback() { @Override public void onSuccess() { } @Override public void onError(Exception e) { Toast.makeText(context, "Something Happend Wrong Uploader Image", Toast.LENGTH_LONG).show(); } });
quelle