Sunteți pe pagina 1din 44

Robert Győrödi

Profesor

Universitatea din Oradea, Departamentul de Calculatoare și Tehnologia Informației


Îmbunătățirea UI

Concepte Material Design cu librăria suport pentru design

Tratarea imaginilor

Gestionarea memoriei
Îmbunătățirea UI
Folosire CardView
Atribute layout în timpul design-ului
Folosirea de fonturi personalizate
Concepte Material Design cu librăria suport pentru design
TabLayout
Toolbar, action bar și app bar
CoordinatorLayout
Navigarea în sus
Pentru a simula mai bine un panou publicitar cu notițe putem
folosi widget-ul CardView

În plus pentru a face o diferență și mai mare, putem folosi un


font personalizat
https://developer.android.com/guide/topics/ui/layout/cardview

A fost introdus în Android 5.0


Un view cu colțuri rotunjite și umbră, simulând un carton de
prezentare
Combinarea cu un recycler view poate produce o listă de
elemente cu un impact vizual puternic
Pentru a putea folosi CardView, trebuie să adăugăm o
dependință la librăria corespunzătoare
dependencies {
...
implementation 'com.android.support:cardview-v7:27.1.1'
...
}

Pentru atribute specifice CardView trebuie importată


schema corespunzătoare în layout
xmlns:card_view="http://schemas.android.com/apk/res-auto"
Aceste atribute au fost introduse de la versiunea 0.2.11 ale AS
Permit afișarea de text sau a oricăror atribute în timpul design-
ului și care nu vor fi prezente în timpul rulării efective
Pentru asta trebuie să adăugăm spațiul de nume tools
Acesta este definit pe elementul rădăcină a layout-ului
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"

<TextView
android:id="@+id/rowJobOfferTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:text="Title of the job"
android:textColor="#555"
android:textSize="18sp"
android:layout_marginBottom="20dp"
/>
Pentru folosirea unor fonturi personalizate acestea trebuie
incluse în aplicație
Copiate în directorul /src/main/assets/fonts
Trebuie creat un obiect Typeface și setat pe componenta
dorită
Acest lucru ar trebui repetat pentru fiecare componentă
Duce la o degradare a performanțelor
Typeface type = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Regular.ttf");
myTextView.setTypeface(type);

Pentru simplificare există o librărie open-source numită


Calligraphy, dezvoltată de Chris Jenkins
Permite folosirea unui font implicit pentru toată aplicația
Permite specificarea în xml pentru diverse elemente a fontului
dorit
https://github.com/chrisjenx/Calligraphy
dependencies {
...
implementation 'uk.co.chrisjenx:calligraphy:2.3.0'
...

Pentru a putea fi folosit, trebuie adăugat la dependințe }

Pentru aplicarea unui font personalizat implicit, în onCreate()-ul aplicației trebuie


inițializată librăria
CalligraphyConfig.initDefault(
new CalligraphyConfig.Builder()
.setDefaultFontPath("fonts/Roboto-Regular.ttf")
.setFontAttrId(R.attr.fontPath)
.build());

În fișierul xml de layout trebuie specificat font-ul dorit folosind atributul fontPath
(fără prefix). Pentru a nu primi atenționări se poate specifica atributul ignore
<TextView
android:text="@string/hello_world"
android:layout_width=“wrap_content“ android:layout_height="wrap_content“
...
fontPath="fonts/Roboto-Bold.ttf"
tools:ignore="MissingPrefix"
/>

În fiecare activitate unde dorim afișarea fontului implicit trebuie inclus următorul cod:
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
<?xml version="1.0" encoding="utf-8"?> <TextView
<android.support.v7.widget.CardView android:id="@+id/rowJobOfferDesc"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_marginTop="5dp"
xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent"
xmlns:tools="http://schemas.android.com/tools" android:layout_height="wrap_content"
android:orientation="vertical" android:layout_width="match_parent" tools:text="Description of the job"
android:layout_height="170dp" android:textColor="#999"
android:layout_margin="10dp" android:textSize="16sp"
card_view:cardElevation="4dp" android:ellipsize="marquee"
card_view:cardCornerRadius="4dp" fontPath=“fonts/daniel.ttf"
> tools:ignore=“MissingPrefix"
/>
<LinearLayout
android:orientation="vertical" </LinearLayout>
android:layout_width="wrap_content"
android:padding="15dp" <ImageView
android:layout_height="wrap_content"> android:layout_gravity="right"
android:layout_marginRight="20dp"
<TextView android:layout_width="100dp"
android:id="@+id/rowJobOfferTitle" android:layout_height="100dp"
android:layout_width="fill_parent" android:src="@drawable/pin"/>
android:layout_height="wrap_content"
tools:text="Title of the job" </android.support.v7.widget.CardView>
android:textColor="#555"
android:textSize="18sp"
android:layout_marginBottom="20dp“
fontPath=“fonts/danielbd.ttf"
tools:ignore=“MissingPrefix"
/>
https://developer.android.com/topic/libraries/support-library/packages#design

Această librărie introduce componente de design material și este


compatibil cu toate versiunile de Android de la 2.1
Design-ul material este un nou limbaj de design introdus cu
Android Lollipop
Stabilește un model pentru aplicații
Trebuie inclus ca și dependință dependencies {
...
implementation 'com.android.support:design:27.1.1'
...
}

Această librărie include diverse componente vizuale


Input text cu floating text, floating action button
TabLayout
CoordinatorLayout
Acest layout permite folosirea de taburi fixe sau scrolabile, cu
text, icoane sau folosind view-uri personalizate
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">

<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="50dp"/>

<android.support.v4.view.ViewPager

android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">

</android.support.v4.view.ViewPager>

</LinearLayout>

Exemplul de mai sus înlocuiește PagerTabStrip cu TabLayout


Există două modalități de adăugare a tab-urilor
Crearea manuală și adăugarea lor
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));

Folosirea unui ViewPager (împreună cu un adapter) și setarea acestuia


pe TabLayout public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());


ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(adapter);

TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);


tabLayout.setupWithViewPager(viewPager);
}

@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
}
Action și app bar sunt una și aceeași componentă
App bar este doar un nume nou pentru action bar în material design

Toolbar – introdus în API 21, Android Lollipop


Este o generalizare a action bar
Nu trebuie neapărat să fie fixată în partea de sus a activității
Putem specifica dacă un anumit toolbar se comportă ca și action bar-ul activității cu metoda
setActionBar()
În acest caz trebuie să folosim o temă cu opțiunea .NoActionBar
AppBarLayout – un nou element destinat pentru a conține toolbar-ul și să afișeze
animații bazate pe evenimente de scroll
app:layout_scrollFlags
Este conceput a fi folosit în CoordinatorLayout
https://guides.codepath.com/android/Handling-Scrolls-with-CoordinatorLayout

Permite adăugarea unor efecte de mișcare, conectând


evenimentele touch și gesturile cu view-urile
Aceste evenimente touch sau gesturile sunt tratate de clasa
Coordinator.Behaviour
AppBarLayout are deja această clasă privată
Dacă dorim să folosim aceste mișcări cu un view personalizat, atunci
trebuie să creăm acest comportament
Modalitate de implementare
Pe nivelul superior al activității sau fragmentului
Astfel îl putem combina cu orice elemente
Ca și un container pentru a interacționa cu view-urile conținute
https://developer.android.com/topic/libraries/support-library/packages#v4

În aplicația exemplu, detaliile unei oferte vor fi afișate într-o activitate separată când
se dă click pe un card

Această activitate va conține un toolbar arătând titlul ofertei și logo-ul companiei

Dacă descrierea este lungă, atunci va trebui să putem derula în jos pentru a citit tot
În acest moment logo-ul va fi redus, deoarece nu mai este relevant.
Când se revine la început, logo-ul va fi readus la dimensiunea originală
Pentru acest lucru avem nevoie de CollapsingToolbarLayout

Descrierea va fi conținută în NestedScrollView


Acesta poate propaga evenimentele de scroll la toolbar
Trebuie inclus ca dependință cel puțin: dependencies
...
{

com.android.support-core-ui:27.1.1 implementation 'com.android.support:support-core-ui:27.1.1'


...
}
<android.support.design.widget.CoordinatorLayout <android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_height="fill_parent"
android:id="@+id/main_content" android:paddingLeft="20dp"
android:layout_width="match_parent" android:paddingRight="20dp"
android:layout_height="match_parent"> app:layout_behavior="@string/appbar_scrolling_view_behavior">

<android.support.design.widget.AppBarLayout <TextView
android:id="@+id/appbar" android:id="@+id/rowJobOfferDesc"
android:layout_height="250dp" android:layout_width="fill_parent"
android:layout_width="match_parent" android:layout_height="fill_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:text=“Long scrollable text ..."
android:fitsSystemWindows="true"> android:textColor="#999"
android:textSize="18sp"
<android.support.design.widget.CollapsingToolbarLayout />
android:id="@+id/collapsingtoolbar"
android:layout_width="match_parent" </android.support.v4.widget.NestedScrollView>
android:layout_height="match_parent"
android:fitsSystemWindows="true" </android.support.design.widget.CoordinatorLayout>
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?attr/colorPrimary">

<ImageView
android:id="@+id/logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/top_wooden_texture"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />

<android.support.v7.widget.Toolbar
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:id="@+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
app:layout_collapseMode="pin"/>

</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
Pentru a afișa detaliile trebuie să navigăm la OfferDetailActivity,
și să transmitem informațiile despre ofertă
public class MyViewHolder extends RecyclerView.ViewHolder public class OfferDetailActivity extends AppCompatActivity {
implements View.OnClickListener, View.OnLongClickListener{
@Override
public TextView textViewName; protected void onCreate(Bundle savedInstanceState) {
public TextView textViewDescription; super.onCreate(savedInstanceState);
setContentView(R.layout.activity_offer_detail);
public MyViewHolder(View v){
super(v); final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
textViewName = (TextView)v.findViewById(R.id.rowJobOfferTitle); setSupportActionBar(toolbar);
textViewDescription = (TextView)v.findViewById(R.id.rowJobOfferDesc); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
v.setOnClickListener(this);
v.setOnLongClickListener(this); String job_title = getIntent().getStringExtra("job_title");
}
CollapsingToolbarLayout collapsingToolbar =
@Override (CollapsingToolbarLayout) findViewById(R.id.collapsingtoolbar);
public void onClick(View view) { collapsingToolbar.setTitle(job_title);
Intent intent = new Intent(view.getContext(), OfferDetailActivity.class);
JobOffer selectedJobOffer = mOfferList.get(getPosition()); }
intent.putExtra("job_title", selectedJobOffer.getTitle());
intent.putExtra("job_description",selectedJobOffer.getDescription());
...
view.getContext().startActivity(intent);
}

Trebuie să vă asigurați că folosiți o temă fără action bar


<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!– Customize your theme here. -->
</style>
Există două modalități de navigare spre un ecran anterior
Back navigation – apăsând pe butonul back (hardware sau software)
Up navigation
Introdus cu action bar în Android 3.0
Apăsând pe o săgeată care arată la stânga, afișată în action bar
În anumite cazuri trebuie să suprascriem navigarea înapoi (back)
De exemplu dacă avem un WebView personalizat
@Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
return;
}
// Otherwise defer to system default behavior.
super.onBackPressed();
}

În afară de aceste cazuri, navigarea înapoi este implementată implicit


Această navigare nu este implementată implicit
Pentru aceasta avem nevoie de:
un action bar (sau un toolbar care se comportă ca și un action bar)
să activăm această navigare cu setDisplayHomeAsUpEnabled(true)
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

odată activat, trebuie să captăm evenimentul click pe săgeata înapoi


din action bar
Acesta va fi detectat în activitate ca și o selecție de acțiune din meniu cu ID-ul
android.R.id.home @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
Tratarea imaginilor
Descărcarea imaginilor de pe internet
Metoda tradițională
ImageDownloader cu Volley
Picasso
Glide
Imagini
Grafice vectoriale
Grafice vectoriale animate
Nine patch
Gestionarea memoriei
Detectarea și localizarea pierderilor de memorie
Prevenirea pierderilor de memorie
Pași
Crearea unei conexiuni folosind HttpURLConnection
Deschiderea unui input stream și consumarea informației, care trebuie
transformată într-o imagine Bitmap
BitmapFactory.decodeStream(InputStream istream)
Poate fi convertit într-un fișier și stocat pe disc
Pentru exemplul nostru, trebuie să trimitem un parametru suplimentar –
link-ul spre imagine
@Override
public void onClick(View view) {
Intent intent = new Intent(view.getContext(), OfferDetailActivity.class);
JobOffer selectedJobOffer = mOfferList.get(getPosition());
intent.putExtra("job_title", selectedJobOffer.getTitle());
intent.putExtra("job_description",selectedJobOffer.getDescription());
intent.putExtra("job_image",selectedJobOffer.getImageLink());
view.getContext().startActivity(intent);
}
Metoda care va fi folosită la public static Bitmap getImage(String urlString) {
URL url = null;

descărcarea unei imagini va fi try {


url = new URL(urlString);
o metodă statică, astfel că va } catch (MalformedURLException e) {
return null;
putea fi folosită de oriunde din }

aplicație HttpURLConnection connection = null;


try {
connection = (HttpURLConnection) url.openConnection();
Putem plasa această metodă connection.connect();
int responseCode = connection.getResponseCode();

într-o clasă ImageUtils în if (responseCode == 200) {


return BitmapFactory.decodeStream(connection.getInputStream());
} else
pachetul utils return null;
} catch (Exception e) {
return null;
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
Putem să creăm o metodă String imageLink = getIntent().getStringExtra("job_image");
ImageView imageViewLogo = (ImageView) findViewById(R.id.logo);

displayImageFromUrl() care primește displayImageFromUrl(imageViewLogo,imageLink);

un ImageView și un șir cu link-ul la


imagine public void displayImageFromUrl(ImageView imageView, String link){

new AsyncTask<Object,Void,Bitmap>(){

ImageView imageView;
String link;
Trebuie să ținem cont de faptul că nu @Override
putem să apelăm direct de pe firul protected Bitmap doInBackground(Object... params) {
imageView = (ImageView) params[0];
principal o metodă care face o link = (String) params[1];

conexiune pe internet, trebuie să }


return ImageUtils.getImage(link);

facem acest lucru pe un fir în fundal, @Override


protected void onPostExecute(Bitmap bitmap) {
altfel vom primi o excepție super.onPostExecute(bitmap);
imageView.setImageBitmap(bitmap);
}

}.execute(imageView,link);

}
Volley oferă două mecanisme pentru a prelua imagini
ImageRequest
ImageLoader
Tratează mai multe cereri, potrivit pentru listview
Poate folosi un mecanism pentru cache personalizat (memorie, disc)
Folosește un tip special de ImageView: NetworkImageView
public void displayImageWithVolley(final ImageView imageView, String url){ myNetworkImageView.setImageUrl(url, imageLoader);
myNetworkImageView.setDefaultImageResId(R.id.default_image);
ImageRequest request = new ImageRequest(url, myNetworkImageView.setErrorImageResId(R.id.image_not_found);
new Response.Listener<Bitmap>() {
@Override @Override
public void onResponse(Bitmap bitmap) { public void onCreate() {
imageView.setImageBitmap(bitmap); super.onCreate();
} sInstance = this;
}, 0, 0, null, mRequestQueue = Volley.newRequestQueue(this);
new Response.ErrorListener() { mImageLoader = new ImageLoader(mRequestQueue, new MyImageCache());
public void onErrorResponse(VolleyError error) {
//imageView.setImageResource(R.drawable.image_load_error);
}
});

MAApplication.getInstance().getRequestQueue().add(request);
}
public class LruBitmapCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
public LruBitmapCache(int maxSize) {
super(maxSize);
Alegerea }

implementării de public LruBitmapCache(Context ctx) {


this(getCacheSize(ctx));

cache este un }

@Override
proces manual și protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
trebuie comunicat }

constructorului @Override
public Bitmap getBitmap(String url) {
return get(url);
ImageLoader }

@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}

// Returns a cache size equal to approximately three screens worth of images


public static int getCacheSize(Context ctx) {
final DisplayMetrics displayMetrics = ctx.getResources().getDisplayMetrics();
final int screenWidth = displayMetrics.widthPixels;
final int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
final int screenBytes = screenWidth * screenHeight * 4;
return screenBytes * 3;
}
}
https://square.github.io/picasso
https://github.com/square/picasso
Cei de la Square, care au implementat și OkHttp au făcut public
și gratuit și implementarea Picasso
Picasso permite descărcarea și afișarea unei imagini printr-o
singură linie de cod și include
O implementare pentru cache care funcționează automat folosind disc
și memorie
Transformări de imagini
Reciclarea ImageView (detectarea automată)
Renunțarea la cereri
Indicarea în mod depanare a sursei imaginii (rețea, disc sau memorie)
Pentru a putea fi folosit trebuie inclus ca și dependință
dependencies {
...
implementation 'com.squareup.picasso:picasso:2.71828'
...
}

Modul de folosire:
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
https://github.com/bumptech/glide

Glide, ca și Picasso, permite descărcarea și afișarea unei imagini


printr-o singură linie de cod și include
Suportă descărcarea, decodarea și afișarea de frame-uri video, imagini,
GIF-uri animate.
Implicit folosește o stivă de comunicare bazată pe HttpUrlConnection,
dar poate fi configurat să folosească Volley sau OkHttp
Permite redimensionarea imaginii și specificarea unei imagini de tip
”plceholder” care este afișată în timp ce se descarcă imaginea dorită
Ține cont de ciclul de viață al activităților sau fragmentelor, dacă
acestea se specifică cu un apel la .with(activitate/fragment)
O implementare pentru cache care funcționează automat folosind disc
și memorie
https://bumptech.github.io/glide/doc/getting-started.html

Pentru a putea fi folosit trebuie inclus ca și dependință


dependencies {
...
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1‘
...
}

Modul de folosire
Utilizare simplă
Utilizare printr-un API fluent – necesită implementarea unui modul
care extinde AppGlideModule
În configurația de bază se poate apela Glide direct
Glide.with(fragment)
.load(myUrl)
.into(imageView);

Dacă dorim să anulăm o descărcare se poate simplu

Glide.with(fragment)
.clear(imageView);
Trebuie implementat un modul specific
package com.example.glidesample;

import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}

În același package se va genera un API cu numele GlideApp


Aplicația poate folosi acest API prin GlideApp.with() în loc de
Glide.with()
GlideApp.with(fragment)
.load(myUrl)
.placeholder(placeholder)
.fitCenter()
.into(imageView);

https://bumptech.github.io/glide/doc/getting-started.html
Imaginile pot fi plasate în mai multe directoare, în funcție de
densitatea ecranului
ldpi, mdpi, hdpi, xxxhdpi, …
Această duplicare a imaginilor duce la creșterea dimensiunii APK
Cu introducerea graficelor vectoriale în Android 5.0 acest
neajuns poate fi eliminat
Acestea se pot scala fără pierdere de calitate
Necesită doar o singură copie
Definite la fel cum se definesc formele (shape) într-un xml
Exemplu:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp" android:width="64dp"
android:viewportHeight="600" android:viewportWidth="600">
<group>
<path android:fillColor="@color/black_primary"
android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
</group>
</vector>

Șirul de date din pathData se poate obține dintr-un fișier svg


(folosind un editor de text), copiind atributul d a elementului
path: <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"
viewBox="0 0 48 48">
<path d="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z"/>
</svg>

Se pot folosi și în versiuni mai vechi prin librăria suport design


Disponibil din Android 5.0 folosind AnimatedVectorDrawable
Animații disponibile:
rotații, scalare, alfa – cele obișnuite din versiuni mai vechi de Android
transformări ale pathData – ne permite crearea unei imagini care își
schimbă forma, sau una care se schimbă într-o altă imagine
http://blog.sqisland.com/2014/10/first-look-at-animated-vector-drawable.html

https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable
Este o imagine care poate fi redimensionată pe baza conținutului
Implică păstrarea unor zone fără expandare
Poate fi creat dintr-o imagine PNG
Este același PNG doar că i se adaugă câte un pixel pe fiecare parte
Conține informații despre care zone se pot extinde și care nu
Se salvează cu extensia .9.png folosind unealta draw9patch
Android Studio oferă unelte pentru profilarea aplicațiilor și măsurarea performanțelor,
respectiv a consumului de resurse:
CPU
Memorie
Trafic rețea

Unealta se numește Android Profiler


View -> Tool Windows -> Android Profiler

Pentru includerea de informații avansate de profilare, pentru versiuni de Android < 8,


trebuie activată opțiunea
Run -> Edit configurations
Se selectează modulul aplicației
În tab-ul Profiling se selectează Enable advanced profiling
Garbage collector (GC) este un mecanism care eliberează în mod
automat resursele din memorie
În anumite cazuri putem preveni GC să elibereze anumite resurse
Dacă aceste resurse ne-eliberate cresc în dimensiune la un moment dat
sistemul va genera exepția OutOfMemoryError
Dacă așa ceva se întâmplă, atunci trebuie să localizăm pierderea de memorie și
să corectăm problema
Folosirea unor bune practici pentru prevenirea pierderii de
memorie este recomandată în toate proiectele
Android Studio oferă o modalitate rapidă pentru verificarea stării
memoriei
După lansarea Android Profiler
Se dă click oriunde în zona de Memory
Aici se poate:
Iniția GC
Efectua un memory dump și analiza obiectele
Urmări alocările efectuate

https://developer.android.com/studio/profile/memory-profiler
Întotdeauna este mai bine să se prevină decât să se caute
cauzele după aceea
Referințe la activitate și context
Aceste referințe sunt una dintre principalele cauze ale problemei
Este destul de frecvent cazul de a trimite o referință a activității la un listener
Dacă această referință este deținută într-un alt obiect, acest lucru va preveni
eliberarea activității de către GC
De exemplu la schimbarea orientării activitatea este creată din nou, iar
vechea activitate ar trebui distrusă
Deci țineți minte să vă ”dezabonați” de la listeneri în metoda onDestroy și să
urmăriți obiectele la care trimiteți contextul, căci acesta este o rerefință
puternică la activitate
Folosirea WeakReference
Implicit în Java când se creează un obiect, acesta este creat cu o referință
puternică
Obiectele diferite de null cu referințe puternice nu vor fi eliberate de GC
Un obiect care conține doar referințe slabe va fi eliberat în următorul ciclu
Același obiect poate avea mai multe referințe
Dacă avem nevoie de un obiect doar temporar, putem crea un referință slabă
Când toate referințele puternice la acel obiect sunt eliminate, acel obiect va fi
eliberat de GC
Exemplu, un popup are nevoie de o referință la un view ancoră,
dar odată afișat nu mai este nevoie de acesta:
private final WeakReference<View> mAnchorViewRef;

S-ar putea să vă placă și