Adam Šimek: Optimalizace skrolování, RecyclerView
Transcript of Adam Šimek: Optimalizace skrolování, RecyclerView
Pohádka o plynulém skrolování
ADAM ŠIMEK
Umět seznamy je důležité
Kulmanův test
Jakto?
¯\_(ツ)_/¯
16
16ms
“A to máte: user input, networking, data processing, database/disk I/O, view inflation, layout, drawing,… Jsou tam taky další aplikace, procesory jsou pomalý, flashky taky, všechno to strasně dlouho trvá. A ještě ke všemu jedeme na baterku.”
16ms?
Tak jdeme na to…
ListView
ListView
RecyclerView
Architektura RecyclerView
ViewHolder
ViewHolder
Best practice z ListViewTakový lightweight controller k jednotlivým položkámRecyclerView si jich drží omezený počet a recykluje jeJeden ViewHolder je použit pro víc položek (ne najednou)Má svůj životní cyklus
ViewHolder Lifecycle
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {}
@Override public void onBindViewHolder(ViewHolder holder, int position) {}
@Override public void onViewAttachedToWindow(ViewHolder holder) {}
@Override public void onViewDetachedFromWindow(ViewHolder holder) {}
@Override public void onViewRecycled(ViewHolder holder) {}
@Override public boolean onFailedToRecycleView(ViewHolder holder) {}
ViewHolder Lifecycle
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {}
@Override public void onBindViewHolder(ViewHolder holder, int position) {}
@Override public void onViewAttachedToWindow(ViewHolder holder) {}
@Override public void onViewDetachedFromWindow(ViewHolder holder) {}
@Override public void onViewRecycled(ViewHolder holder) {}
@Override public boolean onFailedToRecycleView(ViewHolder holder) {}
“Nejrychlejší kód je kód, který se nevolá.” Sokrates
Co nedělat?
Disk I/O - SQLite, SharedPreferences, Content Providersfor (Comment comment : item.getComments()) {}
Parsování textu, JSONu,…, složité formátování,..Html.fromHtml()
Složité layouty 🙂
“Když to běží na u mě plynule, tak v pohodě.”Kritický kód může být schovaný v abstrakci
Co dělat?
Složité operace musí bežet na pozadí (vše z předchozího slajdu)…,nebo už musí být předpřipravené
Není potřeba vše vykreslovat hned → placeholder na chvíli stačívoid onScrollStateChanged(…, int newState)
mHandler.postDelayed(deferredBind, DELAY }
☝ důležitá je vyvážennost
Testovat na různých zařízeních (low-end, tablety)…a taky ruzných datechStrictMode
Co je doma, to se počítá
Co je špatně?
//ViewHolder code called in onBindViewHolder()
@Override public void bindData(Object data, final int position) { mImageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mClickHandler.onItemClicked(v, position, photo); } }); }
Co je špatně?
//ViewHolder code called in onBindViewHolder()
@Override public void bindData(Object data, final int position) { mImageView.setOnClickListener(mClickListener); // use member var instead }
Co je špatně?
//ViewHolder code called in onBindViewHolder()
@Override public void bindData(Object data, final int position, ItemClickListener listener) { mImageView.setOnClickListener(listener); // even better.. }
Co je špatně?
//ViewHolder code called in onBindViewHolder()
@Override public void bindData(Object data, final int position) { mImageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mClickHandler.onItemClicked(v, position, photo); } }); }
Co je špatně?
//ViewHolder code called in onBindViewHolder()
@Override public void bindData(Object data, final int position) { mImageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mClickHandler.onItemClicked(v, getAdapterPosition(), photo); } }); }
Dvakrát měř, jednou řež.
AsyncTask
AsyncTask
Standardně běží tasky sériově → všechno trváHm…executeOnExecutor(THREAD_POOL_EXECUTOR); Je tam celkem velký overheadTasky se docela blbě přerušují
💥
java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@36e83ee rejected from java.util.concurrent.ThreadPoolExecutor@a54e08f[Running, pool size = 13, active threads = 13, queued tasks = 128, completed tasks = 1055]
DIY
Vlastní “taskmanager”
… mDecodeThreadPool = new ThreadPoolExecutor( NUMBER_OF_CORES, // Initial pool size NUMBER_OF_CORES, // Max pool size KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, mDecodeWorkQueue); // 128 size in AsyncTask …
mDecodeThreadPool.execute(DownloadTask); // execute task …
Další záludnosti
Další záludnosti
View.requestLayout() ——————— RecyclerView.setHasFixedSize( boolean hasFixedSize
) ——————— TextView.setAllCaps(boolean allCaps) ——————— notifyDatasetChanged()
View.requestLayout()
View.requestLayout() + hasFixedSize
void triggerUpdateProcessor() { if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) { ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable); } else { mAdapterUpdateDuringMeasure = true; requestLayout(); } }
Další záludnosti
View.requestLayout() ——————— RecyclerView.setHasFixedSize( boolean hasFixedSize
) ——————— TextView.setAllCaps(boolean allCaps) ——————— notifyDatasetChanged()
TextView.setAllCaps()
public void setAllCaps(boolean allCaps) { if (allCaps) { setTransformationMethod(new AllCapsTransformationMethod(getContext())); } else { setTransformationMethod(null); } }
Další záludnosti
View.requestLayout() ——————— RecyclerView.setHasFixedSize( boolean hasFixedSize
) ——————— TextView.setAllCaps(boolean allCaps) ——————— notifyDatasetChanged()
“Today I will do what others won't, so tomorrow I can accomplish what
others can’t"
Prefetch
Systrace
Systrace - missed frame
Prefetch OFF
Prefetch ON
Prefetch
create a bind “do zásoby”odhaduje jak dlouho ty operace trvají (aby se to stihlo)Lollipop+ (RenderThread), support lib 25.1setItemPrefetchEnabled(true) podporováno ve výchozích LayoutManagerech (API pro custom)pozor na onBindViewHolder - žádné animace,…setInitialPrefetchItemCount(N) rozdíl je fakt vidět
@simekadam strava.com/athletes/simekadam