programing

RecyclerView 스크롤 성능

nasanasas 2020. 11. 22. 19:50
반응형

RecyclerView 스크롤 성능


목록 및 카드 만들기 가이드 를 기반으로 RecyclerView 예제를 만들었습니다 . 내 어댑터에는 레이아웃을 확장하기위한 패턴 구현 만 있습니다.

문제는 낮은 스크롤 성능입니다. 이것은 8 개 항목 만있는 RecycleView입니다.

일부 테스트에서 Android L에서이 문제가 발생하지 않음을 확인했습니다. 그러나 KitKat 버전에서는 성능 저하가 분명합니다.


최근에 동일한 문제에 직면 했으므로 이것이 최신 RecyclerView 지원 라이브러리로 수행 한 작업입니다.

  1. 복잡한 레이아웃 (중첩 된 뷰, RelativeLayout)을 새로 최적화 된 ConstraintLayout으로 바꿉니다 . Android Studio에서 활성화 : SDK Manager-> SDK Tools 탭-> Support Repository-> ConstraintLayout for Android 및 Solver for ConstraintLayout을 확인합니다. 종속성에 추가하십시오.

    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    
  2. 가능하면 RecyclerView의 모든 요소를 동일한 높이로 만드십시오 . 그리고 추가 :

    recyclerView.setHasFixedSize(true);
    
  3. 기본 RecyclerView 드로잉 캐시 방법을 사용하고 경우에 따라 조정하십시오. 이를 위해 타사 라이브러리가 필요하지 않습니다.

    recyclerView.setItemViewCacheSize(20);
    recyclerView.setDrawingCacheEnabled(true);
    recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
    
  4. 많은 이미지 를 사용하는 경우 크기와 압축이 최적 인지 확인하십시오 . 이미지 크기 조정도 성능에 영향을 미칠 수 있습니다. 문제에는 사용 된 소스 이미지와 디코딩 된 비트 맵이라는 두 가지 측면이 있습니다. 다음 예제는 웹에서 다운로드 한 이미지를 디코딩하는 방법에 대한 힌트를 제공합니다.

    InputStream is = (InputStream) url.getContent();
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    Bitmap image = BitmapFactory.decodeStream(is, null, options);
    

가장 중요한 부분은 지정 inPreferredConfig하는 것입니다. 이미지의 각 픽셀에 사용될 바이트 수를 정의합니다. 이것이 선호되는 옵션 임을 명심하십시오 . 소스 이미지에 더 많은 색상이있는 경우에도 다른 구성으로 디코딩됩니다.

  1. onBindViewHolder ()가능한 한 저렴한 지 확인하십시오 . OnClickListener를 한 번 설정 onCreateViewHolder()하고 인터페이스를 통해 어댑터 외부의 리스너를 호출하여 클릭 된 항목을 전달할 수 있습니다. 이렇게하면 항상 추가 개체를 만들지 않습니다. 또한 여기에서보기를 변경하기 전에 플래그와 상태를 확인하십시오.

    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Item item = getItem(getAdapterPosition());
              outsideClickListener.onItemClicked(item);
          }
    });
    
  2. 데이터가 변경되면 영향을받는 항목 만 업데이트하십시오 . 예를 들어를 사용하여 전체 데이터 세트를 무효화하는 대신 notifyDataSetChanged()더 많은 항목을 추가 /로드 할 때 다음을 사용하십시오.

    adapter.notifyItemRangeInserted(rangeStart, rangeEnd);
    adapter.notifyItemRemoved(position);
    adapter.notifyItemChanged(position);
    adapter.notifyItemInserted(position);
    
  3. 에서 안드로이드 개발자 웹 사이트 :

마지막 수단으로 notifyDataSetChanged ()를 사용하십시오.

그러나 그것을 사용해야하는 경우 고유 ID로 항목을 유지하십시오 .

    adapter.setHasStableIds(true);

RecyclerView는이 메서드를 사용할 때 안정적인 ID를 가지고 있다고보고하는 어댑터에 대해 가시적 인 구조 변경 이벤트를 합성하려고 시도합니다. 이는 애니메이션 및 시각적 개체 지속성을 위해 도움이 될 수 있지만 개별 항목보기는 여전히 리 바인딩하고 다시 배치해야합니다.

모든 것을 올바르게 수행하더라도 RecyclerView가 원하는만큼 원활하게 작동하지 않을 가능성이 있습니다.


다음 플래그를 추가하여이 문제를 해결했습니다.

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#setHasStableIds(boolean)


당신의 공연을 죽일 수있는 패턴을 적어도 하나 발견했습니다. 자주onBindViewHolder() 호출 된다는 것을 기억하십시오 . 따라서 해당 코드에서 수행하는 모든 작업은 성능이 저하 될 가능성이 있습니다. RecyclerView가 사용자 지정을 수행하는 경우 실수로이 메서드에 느린 코드를 삽입하기가 매우 쉽습니다.

위치에 따라 각 RecyclerView의 배경 이미지를 변경했습니다. 그러나 이미지를로드하는 데 약간의 작업이 필요하므로 RecyclerView가 느리고 불안정합니다.

이미지에 대한 캐시를 만드는 것은 놀라운 일이었습니다. onBindViewHolder()이제는 처음부터로드하는 대신 캐시 된 이미지에 대한 참조를 수정합니다. 이제 RecyclerView가 압축됩니다.

모든 사람이이 정확한 문제를 가지고있는 것은 아니라는 것을 알고 있으므로 코드를로드하는 데 신경 쓰지 않습니다. 그러나 내에서 수행되는 작업은 onBindViewHolder()RecyclerView 성능 저하의 잠재적 인 병목 현상으로 간주하십시오 .


@Galya의 자세한 답변 외에도 최적화 문제 일 수 있지만 디버거를 활성화하면 상황이 많이 느려질 수 있다는 것도 사실입니다.

최적화를 위해 모든 작업을 수행 RecyclerView했지만 여전히 원활하게 작동하지 않는 경우 빌드 변형을로 전환 release하고 비 개발 환경 (디버거가 비활성화 된 상태)에서 어떻게 작동하는지 확인하십시오.

내 앱이 debug빌드 변형 에서 느리게 수행되는 것이 나에게 발생 했지만 변형으로 전환하자마자 release원활하게 작동했습니다. 이것은 release빌드 변형으로 개발해야한다는 것을 의미하지는 않지만 앱을 출시 할 준비가 될 때마다 제대로 작동한다는 것을 아는 것이 좋습니다.


setHasStableId플래그를 사용하면 문제가 해결 될지 잘 모르겠습니다 . 제공 한 정보에 따르면 성능 문제는 메모리 문제와 관련이있을 수 있습니다. 사용자 인터페이스 및 메모리 측면에서 애플리케이션 성능은 상당히 관련이 있습니다.

지난주에 내 앱이 메모리 누수를 발견했습니다. 내 앱을 사용한 지 20 분 후에 UI 성능이 정말 느리다는 것을 알았 기 때문에 이것을 발견했습니다. 활동 닫기 / 열기 또는 여러 요소가있는 RecyclerView 스크롤은 정말 느 렸습니다. http://flowup.io/를 사용하여 프로덕션의 일부 사용자를 모니터링 한 후 다음을 발견했습니다.

여기에 이미지 설명 입력

프레임 시간은 정말 높았고 초당 프레임 수는 정말 낮았습니다. 일부 프레임은 렌더링하는 데 약 2 초가 필요하다는 것을 알 수 있습니다. : S.

이 잘못된 프레임 시간 / fps의 원인을 알아 내려고 여기에서 볼 수 있듯이 메모리 문제가 있음을 발견했습니다.

여기에 이미지 설명 입력

평균 메모리 소비가 동시에 15MB에 가까웠을 때도 앱은 프레임을 떨어 뜨 렸습니다.

이것이 제가 UI 문제를 발견 한 방법입니다. 앱에서 메모리 누수가 발생하여 많은 가비지 수집기 이벤트가 발생했으며 Android VM이 모든 단일 프레임에서 메모리를 수집하기 위해 앱을 중지해야했기 때문에 UI 성능이 저하되었습니다.

코드를 살펴보면 Android Choreographer 인스턴스에서 리스너 등록을 취소하지 않았기 때문에 사용자 정의 뷰 내부에 누수가있었습니다. 수정 사항을 발표 한 후 모든 것이 정상화되었습니다. :)

메모리 문제로 인해 앱에서 프레임이 삭제되는 경우 다음 두 가지 일반적인 오류를 검토해야합니다.

앱이 초당 여러 번 호출되는 메서드 내에서 개체를 할당하는지 검토합니다. 이 할당은 응용 프로그램이 느려지는 다른 장소에서 수행 될 수 있습니다. 예를 들어 리사이클 러 뷰 뷰 홀더의 onBindViewHolder에있는 onDraw 사용자 정의 뷰 메서드 내에 객체의 새 인스턴스를 생성 할 수 있습니다. 앱이 Android SDK에 인스턴스를 등록하지만 릴리스하지 않는지 검토합니다. 리스너를 버스 이벤트에 등록하면 누수가 발생할 수도 있습니다.

면책 조항 : 내 앱을 모니터링하는 데 사용했던 도구는 개발 중입니다. 저는 개발자 중 하나이기 때문에이 도구에 액세스 할 수 있습니다. :)이 도구에 액세스하려면 곧 베타 버전을 출시 할 예정입니다! 웹 사이트 http://flowup.io/에 가입 할 수 있습니다 .

다른 도구를 사용하려면 traveview, dmtracedump, systrace 또는 Android Studio에 통합 된 Andorid 성능 모니터를 사용할 수 있습니다. 그러나이 도구는 연결된 장치를 모니터링하고 나머지 사용자 장치 나 Android OS 설치는 모니터링하지 않습니다.


RecyclerView의 공연 에 대해 이야기를했습니다 . 다음은 영어슬라이드러시아어로 녹화 된 비디오입니다 .

여기에는 일련의 기술이 포함되어 있습니다 (일부는 이미 @Darya의 답변에서 다루었습니다 ).

다음은 간략한 요약입니다.

  • Adapter항목의 크기가 고정 된 경우 다음을 설정하십시오.
    recyclerView.setHasFixedSize(true);

  • 데이터 엔터티를 long ( hashCode():)으로 나타낼 수있는 경우 다음을 설정
    adapter.hasStableIds(true);
    하고 구현합니다.
    // YourAdapter.java
    @Override
    public long getItemId(int position) {
    return items.get(position).hashcode(); //id()
    }
    이 경우 콘텐츠가 변경 Item.id()되어도 동일하게 유지되므로 작동하지 않습니다 Item.
    추신 DiffUtil을 사용하는 경우에는 필요하지 않습니다!

  • 올바르게 크기 조정 된 비트 맵을 사용하십시오. 바퀴를 재발 명하고 라이브러리를 사용하지 마십시오. 여기
    에서 선택하는 방법에 대한 자세한 정보 .

  • 항상 최신 버전의 RecyclerView. 예를 들어, 25.1.0프리 페치의 성능이 크게 향상되었습니다 . 여기에
    더 많은 정보가 있습니다 .

  • DiffUtill을 사용하십시오.
    DiffUtil은 필수 입니다.
    공식 문서 .

  • Simplify your item's layout!
    Tiny library to enrich TextViews - TextViewRichDrawable

See slides for more detailed explanation.


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.


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


I see in the comments that you are already implementing the ViewHolder pattern, but I will post an example adapter here that uses the RecyclerView.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 Please

Hope it resolves your problem.


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;


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.


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

Adding to @Galya's answer, in bind viewHolder,I was using Html.fromHtml() method. apparently this has performance impact.


I solved it by this line of code

recyclerView.setNestedScrollingEnabled(false);

참고 URL : https://stackoverflow.com/questions/27188536/recyclerview-scrolling-performance

반응형