Dicas para Interfaces Performáticas no seu App Android

Post on 10-May-2015

1.852 views 0 download

description

Slides sobre dicas para interfaces Android mais responsivas, que utilizei na minha apresentação do Intel Software Day 2013

Transcript of Dicas para Interfaces Performáticas no seu App Android

DICAS PARA INTERFACES PERFORMÁTICAS

NO SEU APP ANDROID INTEL SOFTWARE DAY 2013

CONTEXTO

INTERAÇÕES COM A UISonho de consumo são 60 FPS...

A cada 16ms, um FPS certamente se perde

Quanto mais FPSs perdidos, mais a experiência do usuário com a UI degrada...

COMO PERDER FPSs UI Thread pausada na hora da interação

UI Thread está executando alguma operação (lenta) no momento da interação

GARBAGE COLLECTIONPOR QUÊ ELE IMPORTA

GARBAGE COLLECTIONAndroid 2.3+ implementa variante de CMS

Pausas prolongadas e/ou frequentes durante a interação do usuário PRECISAM ser evitadas

FORÇA PAUSAS DA UI THREAD PARA GC

NÃO GERE LIXO DESNECESSÁRIO

AUTO BOXING Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1024, "Android"); map.put(2048, "performance"); map.put(5096, "matters"); List<Double> values = new ArrayList<Double>(); values.add(3.1415);

map.put(1024, "Android");

new Integer(1024);

SPARSE ARRAYS

SparseArray<String> sparseArray = new SparseArray<String>(); sparseArray.put(665, "Android"); sparseArray.put(666, "rocks");

STRINGS String facebookAvatarURL =

"http://graph.facebook.com/" + facebookID + "/picture?type=large";

String numberStr = "" + number;

DON’T

STRINGS String facebookAvatarURL =

"http://graph.facebook.com/" + facebookID + "/picture?type=large";

....

String numberStr = "" + number;

new StringBuilder();

SEMPRE ÓTIMO ??? StringBuilder builder = new StringBuilder(); for (int i = 0; i < count; i++) { builder.append("ANDROID"); } return builder.toString();

FATOSStringBuilder trabalha com um array de caracteres de tamanho pré-fixado...

Estourar o limite significa criar um novo array de caracteres e concatenar no já existente...

BOAS PRÁTICAS final String androidRocks = "android" + "rocks";

// Constantes serão otimizadas pelo compilador // e concatenadas em tempo de compilação

final String facebookAvatarURL = GRAPH_BASE_URL.concat(facebookID);

// String.concat() melhor para uma variável String

StringBuilder builder = new StringBuilder(200); // Garantir que StringBuilder aloca caracteres suficientes // evita a criação de novos arrays de caracteres

ARRAY LISTS @Override public void onCompleted(List<GraphUser> users, Response response) {

List<FacebookFriend> friends = new ArrayList<FacebookFriend>();

if (users != null) { for (GraphUser user : users) { FacebookFriend friend = new FacebookFriend(user);

friends.add(friend) }

mAdapter = new FriendsAdapter(this, friends); mFriendsList.setAdapter(mAdapter); } }

FATOSArrayList, HashMaps, TreeMaps trabalham sobre Object[], uma estrutura imutável

Se o tamanho pré-definido da Collection estoura, arrays subjacentes serão substituídos por novas instâncias

BOAS PRÁTICASDIMENSIONE o tamanho das suas listas

EVITE adicionar elementos em uma posição específica da lista

List<FacebookFriend> friends = new ArrayList<FacebookFriend>(500);

friends.add(10,friend);

ABSTRAÇÕES EM EXCESSO ...

REUSAR SEMPREQUE POSSÍVEL

OBJECT POOL

Reusar instâncias de objetos ao invés de criar e destruir com (muita!) frequência

http://en.wikipedia.org/wiki/Object_pool_pattern

public class BlocksEngine extends CCLayer {

public void blocksEngine(float dt) { if (new Random().nextInt(300) == 0) {

getDelegate().createBlock( new Block(Assets.block).generate(), 1, 1); } } }

Um random por bloco

Novo bloco a cada chamada da engine

public static final Random sRANDOM = new Random(); public class BlocksEngine extends CCLayer {

public void blocksEngine(float dt) { if (sRANDOM.nextInt(300) == 0) { final Block b = BlocksPool.acquire(); // configurar seu bloco getDelegate().createBlock(b); } } }

Random agora é constante

Reuse um bloco previamente existente !!!

private static final int MSG_DESIRED_EVENT = 0xb0b0;

public void sendMessage(Handler handler, Object eventInfo) { final Message message = new Message(); message.what = MSG_DESIRED_EVENT; message.obj = eventInfo; handler.sendMessage(message); }

Uma mensagem nova para cada evento de interesse

private static final int MSG_DESIRED_EVENT = 0xb0b0;

public void sendMessage(Handler handler, Object eventInfo) { final Message message = Message.obtain(); message.what = MSG_DESIRED_EVENT; message.obj = eventInfo; handler.sendMessage(message); }

Obtém uma mensagem de um pool, ou cria uma nova

ADAPTERS @Override public View getView(int position, View convertView, ViewGroup parent) {

final View cellView = mInflater.inflate(R.layout.list_item, parent, false);

final MovieInfo i = getItem(position);

((TextView) cellView.findViewById(R.id.title)).setText(i.getTitle()); ((TextView) cellView.findViewById(R.id.subtitle)).setText(i.getSubs()); return cellView; } DO

N’T

@Override public View getView(int position, View convertView, ViewGroup parent) {

if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, parent, false);

} final MovieInfo i = getItem(position);

((TextView) convertView.findViewById(R.id.title)) .setText(i.getTitle());

((TextView)convertView.findViewById(R.id.subtitle)) .setText(i.getSubs()); return convertView; }

Reusa uma View já criada se possível !

E PARA ESSE TIPO DE LISTA ????

EXECUÇÃO ÓTIMACODIFIQUE PARA A PERFORMANCE

FOR LOOP List<FacebookFriend> friends = new ArrayList<FacebookFriend>();

if (users != null) { for (GraphUser user : users) { FacebookFriend friend = new FacebookFriend(user); friends.add(friend) } }

Otimizada pelo compilador !!!

PARCELLABLE E SERIALIZABLE

http://www.developerphil.com/parcelable-vs-serializable/

PARCELLABLE E SERIALIZABLESerializable é muito mais fácil de implementarParcellable é muito mais eficiente na prática

Serializable para um objetoParcellable para coleções de objetos

CACHE DE OPERAÇÕESstatic class ViewHolder {

public TextView friendName; public TextView friendStatus; public ImageView friendImage; }

Cachear a posição das Views na hierarquia de Views da linha !

public abstract class FasterArrayAdapter<T> extends ArrayAdapter<T> {

@Override public View getView(int position, View convertView, ViewGroup parent) {

Object viewHolder = null;

if (convertView == null) {

viewHolder = new ViewHolder(); convertView = mInflater.inflate(layoutResourceForItem(), parent, false); setupHolder(convertView, viewHolder); convertView.setTag(viewHolder);

} else { viewHolder = convertView.getTag(); }

fillHolder(viewHolder, position); return convertView; } }

public abstract class FasterArrayAdapter<T> extends ArrayAdapter<T> {

@Override public View getView(int position, View convertView, ViewGroup parent) {

Object viewHolder = null;

if (convertView == null) { convertView = mInflater.inflate(layoutResourceForItem(), parent, false);

viewHolder = new ViewHolder(); setupHolder(convertView, viewHolder); convertView.setTag(viewHolder);

} else { viewHolder = convertView.getTag(); }

fillHolder(viewHolder, position); return convertView; } }

Recupera o cache

Calcula posições e faz cache

Preenche seu item

LEMBRETES GERAIS

EVITE annotations em tempo de execução

EVITE malabarismos com java.lang.reflect

OTIMIZE o acesso à suas variáveis

http://developer.android.com/training/articles/perf-tips.html

MULTITHREADINGHARD WORK FORA DA UI THREAD

JAVA THREADSUSE CASO TENHA CERTEZA ABSOLUTA DO QUE ESTÁ FAZENDO !!Se o item anterior for cumprido, então priorize a UI Thread

private static final int MSG_DONE = 0xcafe; private Handler mHandler = new Handler(Looper.getMainLooper());

private void workOnBigFile(final String filePath) {

new Thread(new Runnable() { @Override public void run() { Process.setThreadPriority(

Process.THREAD_PRIORITY_BACKGROUND);

File f = new File(filePath); Data d = DataUtils.extractFrom(f);

Message.obtain(mHandler, MSG_DONE, d).sendToTarget(); } }).start(); } Prioridade mais baixa para sua Thread

ASYNCTASK private class DownloadFilesTask extends AsyncTask<URL, Void , Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); } return totalSize; }

protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }

PROBLEMAS COM ASYNCTASKComportamento é inconsistente ao longo das versões de API do Android

Difícil para cancelar tarefas já executando em background ...

USANDO ASYNCTASKAssegure-se de que tarefas serão executadas em paralelo (backporting API14+)

Recomendada para tarefas curtas (segundos)

Cuidados ao declarar como inner class ou ao associar callbacks após a execução

INTENT SERVICEService com uma Thread Worker

MUITO BOM para executar tarefas únicas

Finaliza sozinho após o trabalho executado

Callback mais burocrático após tarefa executada (BroadcastReceiver)

public class HardWorkIntentService extends IntentService {

public HardWorkIntentService() { super(“HardWorkIntentService”); }

@Override protected void onHandleIntent(Intent intent) { // EXECUTE O SEU TRABALHO PESADO AQUI !!!! }

}

Intent toHardWork = new Intent(this, HardWorkIntentService.class); // Coloque seus parâmetros como extras! startService(toHardWork);

LAYOUTSVOCÊ JÁ OTIMIZOU O SEU HOJE?

HIERARQUIA DE VIEWS

A Crazy TitleA simple subtitle

Nested Linear Layout

Relative Layout

PROCESSAMENTO DE LAYOUTSMais lento conforme

Profundidade da hierarquia de ViewsQuantidade de Views por hierarquia

Profilling via delay nos métodos onMeasure( ), onLayout( ), onDraw( )

OTIMIZAÇÕES EM LAYOUTSPelo menos dois pontos básicos

Conversão de declarações em XML em hierarquias Recuperação de itens dentro de uma hierarquia

NA PRÁTICA Precisamos de hierarquias mais leves e planas !!!

FLATTENING

GRID LAYOUTRELATIVE LAYOUT

COMPOUND DRAWABLES

LAZY LOADING DE VIEWS ...

<ViewStub android:id="@+id/stub" android:layout_width="fill_parent" android:layout_height="fill_parent" android:inflatedId="@+id/inflatedLayout" android:layout="@layout/lazy_layout" />

View inflated = ((ViewStub) findViewById(R.id.stub)).inflate();

IMAGENSSão redimensionados em tempo de execução

SEMPRE FORNEÇA TODOS OS CONJUNTOS DE IMAGENS

Para imagens obtidas via IO, procure cachear os Bitmaps já processados

CONCLUSÕESA HORA DE PRESTAR ATENÇÃO !!!

A EXPERIÊNCIA COM A UI CAI

UM FPS POR VEZ

NÃO GERE LIXO DESNECESSÁRIO

REUSE SEMPRE QUE POSSÍVEL

CODIFIQUE PARA O DESEMPENHO

HARD WORK FORADA UI THREAD

OTIMIZE SEUS LAYOUTS

“DON T̀ AIM CORRECT, AIM FOR AWESOME”

Lucas Rocha, Firefox for Android

ANDROID DEVELOPER

@ubiratanfsoares

gplus.to/ubiratanfsoares

ubiratansoares.com.br/blog