Sunteți pe pagina 1din 38

Robert Győrödi

Profesor

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


Clasa Application
Accesarea datelor de pe un server
Soluții de transfer de date prin Internet
Google Volley
OkHttp
JSON și Gson
Concurență
Tipare de design software
În general cele mai multe aplicații în ziua de azi sunt aplicații
conectate
Pentru acest lucru ele accesează un serviciu web care publică un
API specific
Există mai multe metode de comunicare din aplicațiile Android
Google Volley
OkHttp/Retrofit
Datele care sunt transferate sunt codificate de obicei
JSON – obiectele pot fi parsate folosind biblioteca Gson
XML
În AndroidManifest.xml <uses-permission android:name="android.permission.INTERNET" />
De multe ori va trebui să folosim un tipar numit Singleton
Inițializarea unei singure instanțe pentru toate activitățile aplicației
Aceasta se poate face de exemplu în clasa aplicației
Clasa aplicației nu este creată implicit într-un proiect Android
Trebuie să creăm explicit această clasă și să o specificăm în manifestul
aplicației
Ca și exemplu putem crea:

public class MAApplication extends Application

Vom suprascrie metoda onCreate – apelată de fiecare dată când


aplicația este creată, deci locul potrivit pentru inițializări
În fișierul manifest trebuie să specificăm noua clasă a aplicației
în tag-ul <application>
<application
android:name="MAApplication"
...
android:icon="@drawable/ic_launcher"
android:label="@string/app_newname">

Clasa derivată din Application încapsulează totul în aplicație.


Activitățile sunt conținute în aplicație
Fragmentele sunt conținute în activități
Dacă avem nevoie de o variabilă globală accesibilă de toate
activitățile/fragmentele, în această clasă ar trebui plasată
Metoda tradițională de a comunica cu un server este de a folosi librăriile
Apache HTTP (eliminată de la 6.0+)
Pentru a transmite o imagine la un server ar trebui:
Creat o cerere de tip POST
Trimis un form folosind codificarea corectă
Atașarea imaginii ca și un obiect FileBody într-un MultiPartEntity
Tratarea erorilor …
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("URL TO SERVER");

// build.gradle (app) MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);


android { File file = new File(filePath);
... mpEntity.addPart("imgField", new FileBody(file, "application/octet"));
useLibrary 'org.apache.http.legacy'
... httppost.setEntity(mpEntity);
} HttpResponse response = httpclient.execute(httppost);
dependencies {
...
implementation group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.5'
...
}
Google Volley
este o librărie pentru gestionarea cererilor de rețea.

OkHttp
este un client HTTP foarte rapid

Cele două se pot combina pentru a obține performanțe ridicate pentru


transferul de date prin Internet

Retrofit
este un client de tip “type-safe” pentru Android și Java
folosește adnotări pentru a descrie cererile HTTP
https://developer.android.com/training/volley/

Volley este o librărie HTTP care face comunicarea în rețea pentru


aplicațiile Android mai ușoară și mai rapidă
Beneficii:
Planificare automată a cererilor de rețea
Conexiuni concurente multiple
Un caching transparent pe disc și memorie
Suport pentru prioritizarea cererilor
API pentru anularea cererilor
Anularea unei singure cereri
Setarea unor blocuri sau zone de cereri pentru anulare
Ușurința personalizării – pentru reîncercări de ex.
O ordonare puternică
Permite o populare ușoară a UI cu date preluate asincron de pe rețea
Unelte de depanare și urmărire
Fiecare cerere efectuată de Volley trebuie adăugată la coada de cereri
Trebuie să avem o singură coadă de cereri în aplicație
unde se pot adăuga toate cererile
care poate fi accesată de oriunde din aplicație
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);

Această coadă de cereri va folosi o stivă de conectare:


// If the device is running a version >= Gingerbread...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// ...use HttpURLConnection for stack.
} else {
// ...use AndroidHttpClient for stack.
}

Pentru folosirea librăriei, ea trebuie inclusă ca și o dependință în


build.gradle
dependencies {
implementation 'com.android.volley:volley:1.1.0'
}
După ce avem coada instanțiată, doar trebuie să adăugăm o cerere la ea:
String url ="https://www.google.com";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET,
url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
Log.d("Volley","Response is: "+ response.substring(0,500));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("Volley","That didn't work!");
}
});

// Add the request to the RequestQueue.


queue.add(stringRequest);

Cererea va fi efectuată și metoda onResponse() sau onErrorResponse() va


fi apelată în firul principal al aplicației – firul UI
https://square.github.io/okhttp/

Este un client HTTP și SPDY pentru Android și Java de la Square


Nu este alternativă pentru Volley
Nu include o coadă de cereri
Poate fi folosit ca și stivă de transport pentru Volley OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Dacă nu trebuie: Request request = new Request.Builder()
.url(url)
să tratăm cererile într-o coadă .build();
Response response = client.newCall(request).execute();
să prioritizăm cererile return response.body().string();
}
să planificăm cererile
Atunci putem folosi OkHttp direct în aplicație, fără Volley
Pentru folosirea librăriei, ea trebuie inclusă ca și o dependință în build.gradle
dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}
La instanțierea cozii de cereri putem specifica metoda HttpStack
pe care o dorim
RequestQueue queue = Volley.newRequestQueue(this, new OkHttpStack());

/**
* An HttpStack subclass
* using OkHttp as transport layer.
*/
public class OkHttpStack extends HurlStack {
private final OkUrlFactory mFactory;
public OkHttpStack() {
this(new OkHttpClient());
}
public OkHttpStack(OkHttpClient client) {
if (client == null) {
throw new NullPointerException("Null client.");
}
mFactory = new OkUrlFactory(client);
}
}
Multe dintre API-urile existente pe
{
Internet folosesc formatul JSON "firstName": "Antonio",
"lastName": "Smith",

În unele cazuri se folosește și XML "isDeveloper": true,


"age": 25,
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
Este important de știut },
{
"type": "office",
cum se face o cerere cu transmiterea de "number": "646 555-4567"
}
parametri în format JSON ],
"children": [],
"spouse": null
cum se consumă date în format JSON }

JSON = JavaScript Object Notation


//With Volley //With OkHttp
public void post(String param1, String param2, String url) { public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Map<String, String> params = new HashMap<String, String>(); String post(String url, String json) throws IOException {
params.put("param1",param1); RequestBody body = RequestBody.create(JSON, json);
params.put("param2",param2); Request request = new Request.Builder()
JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.POST, .url(url)
url, new JSONObject(params), .post(body)
new Response.Listener<JSONObject>() { .build();
@Override Response response = client.newCall(request).execute();
public void onResponse(JSONObject responseJSON) { return response.body().string();
} }
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
// Add the request to the RequestQueue.
requestQueue.add(jsonRequest); //To create a JSONObject from a string
} JSONObject responseJSON = new JSONObject(String json);
https://github.com/google/gson

Google Gson este o librărie open source Java folosită pentru


serializarea și deserializarea obiectelor Java în și din format JSON
Pentru a nu crea obiecte noi manual și a le seta parametri prin
preluarea lor din JSON putem crea un obiect de tip JobOffer din
JSON – deserializare
Pentru a folosi această librărie, ea trebuie inclusă ca și o
dependință în build.gradle
dependencies { {
implementation 'com.google.code.gson:gson:2.8.4' "title": "Senior Android developer",
} "description": "A developer is needed for…",
"salary": "25.000 € per year",
.
.
.
}
Gson are două metode utile:
fromJSON – deserializare
toJSON – serializare
Pentru preluarea unui singur obiect:
Gson gson = new Gson();
JobOffer offer = gson.fromJson(JSONString, JobOffer.class);

Pentru preluarea unei liste:


Gson gson = new Gson();
Type listType = new TypeToken<List<JobOffer>>(){}.getType();
List<JobOffer> listOffers = gson.fromJson(JSONString, listType);

Dacă dorim câmpuri din clasa noastră să aibă alte nume decât câmpurile
din JSON, putem folosi adnotări import com.google.gson.annotations.SerializedName;
public class JobOffer {
@SerializedName("title")
private String title;
@SerializedName("description")
private String desc;
@SerializedName("salary")
private String salary;
}
Un dezvoltator, ar trebui ca la scrierea de cod să folosească modele/tipare
de design larg utilizate
Respectând aceste tipare alți dezvoltatori pot să înțeleagă mai ușor ceea ce este
deja dezvoltat
În acest sens vom discuta despre:
Concurență
Handlere și fire de execuție (thread)
AsyncTask
Service
IntentService
Loader
Tipare în Android
Singleton
Adapter și holder
Observer
Activity Not Responding (ANR)
cod care rulează în UI / în thread-ul principal, care blochează
interacțiunea cu utilizatorul mai mult de 5 secunde
O aplicație Android implicit rulează un singur thread – thread-ul
de UI
Mesajele trimise de aplicații sunt depuse într-o coadă numită
MessageQueue, care este unică pentru fiecare thread
Un handler poate trimite mesaje în această coadă
Când este creat un handler, el este asociat automat cu MessageQueue-
ul thread-ului unde este creat

https://developer.android.com/guide/components/processes-and-threads
Un handler este folosit în două situații:
Trimiterea unui mesaj întârziat în thread-ul curent
Trimiterea unui mesaj în alt thread
Un exemplu de folosire postDelayed(Runnable, time):
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
}
},3000);

Metoda runOnUiThread(Runnable action) permite să trimitem


un obiect care va fi executat pe thread-ul de UI
În acest caz nu trebuie creat un handler public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
De obicei firele sunt create manual când dorim să efectuăm o
procesare în fundal
Thread thread = new Thread(){
@Override
public void run() {
super.run();
}
};
thread.start();

Dezavantajul creării de thread-uri și handlere pentru efectuarea


de procesări în fundal este gestionarea manuală a acestora
Android are și alte mecanisme de efectuare de procesări în
fundal, cum ar fi AsyncTask
https://developer.android.com/reference/android/os/AsyncTask

Este bazat pe un thread și un handler


Este gândit a fi un mod simplu de:
a executa operațiuni în fundal și
a putea actualiza interfața utilizator
Trebuie extins pentru a putea fi utilizat
Următoarele metode pot fi suprscrise:
onPreExecute
doInBackground
onProgressUpdate
onPostExecute
Metoda onPreExecute
este apelată înainte de a efectua orice operațiune în fundal
executat încă pe thread-ul UI
folosit pentru a inițializa variabile și progresul înainte de a porni procesarea în fundal
Metoda doInBackground
executat în thread-ul de fundal
din acesta se poate apela metoda onProgressUpdate
Metoda onProgressUpdate
postează o actualizare pe thread-ul UI (de ex. crește valoarea unui ProgressBar pentru a afișa
progresul procesării)
Metoda onPostExecute
este apelată când procesarea din fundal s-a terminat
se execută pe thread-ul UI
new MyAsyncTask( progressBar ).execute(new Integer[]{10});
public class MyAsyncTask extends AsyncTask<Integer,Integer,Void> {
ProgressBar pB;
MyAsyncTask(ProgressBar pB) { new MyAsyncTask( progressBar ).executeOnExecutor(
this.pB = pB; AsyncTask.THREAD_POOL_EXECUTOR, new Integer[]{10});
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Dacă folosim primul mod de
}
pB.setProgress(0); execuție pentru mai multe acțiuni
@Override
protected void onProgressUpdate(Integer... values) { în același timp, aceste acțiuni
super.onProgressUpdate(values);

}
pB.setProgress(values[0]); defapt vor fi executate secvențial
@Override
protected Void doInBackground(Integer... values) {
pentru Android > 3.0
for (int i = 0; i < values[0]; i++){
try {
Thread.sleep(1000);
Cel de-al doilea model va executa
} catch (InterruptedException e) {
e.printStackTrace();
în paralel task-urile
}

}
onProgressUpdate(new Integer[]{i + 1}); Metodele execute a AsyncTask
}
return null;
trebuie executate din thread-ul
@Override
protected void onPostExecute(Void o) { principal
super.onPostExecute(o);

}
Log.d("AsyncTask", "Completed");
O singură metodă execute poate fi
}
apelată din același obiect
https://developer.android.com/guide/components/services

Un serviciu va rula chiar dacă aplicația nu este pe ecran


Execuția se face în fundal
Implicit însă FOLOSEȘTE TOT THREAD-ul PRINCIPAL !!!
Deci pentru a executa operațiuni lungi trebuie să creăm un thread în interiorul
serviciului
Trebuie să îl declarăm în manifest
Poate fi folosit și din alte aplicații dacă este declarat public
Spre deosebire de AsyncTask, serviciile pot fi pornite din orice thread
onStartService()
onStopService()
Serviciile pot fi legate de o componentă
onBind()
O interfață este pusă la dispoziția componentei pentru interacționare cu serviciul
https://developer.android.com/reference/android/app/IntentService

Este o subclasă de servicii care pot fi pornite printr-un Intent


Creează un thread și include callback-uri pentru a ști când s-a
terminat
Acest serviciu primește intent-uri și le execută în mod secvențial

Un exemplu:
Aplicație de citit știri
Stochează știrile offline
Când un articol nou este publicat, utilizatorii primesc o notificare push
Această notificare va genera un intent pentru a descărca articolul
https://developer.android.com/guide/components/loaders

Scopul unui loader este de a face mai ușoară încărcarea de date


în mod asincron într-o activitate și deci și într-un fragment.
De la Android 3.0 fiecare activitate are acces la LoaderManager
pentru a gestiona loaderele folosite
Într-o aplicație bazată pe navigare între fragmente este posibil să
executăm operațiuni în fundal la nivelul activității la trecerea de
la un fragment la altul
Loaderele încarcă date dintr-o sursă, când sursa se schimbă
(inclusiv conținutul), în mod automat împrospătează informația
Ele sunt perfecte pentru folosirea împreună cu o bază de date
Un exemplu este clasa CursorLoader
În loc să ”reinventăm roata” este mai bine să cunoaștem și să
implementăm aceste tipare
Dezvoltând aplicații Android folosim tipare chiar dacă nu ne dăm
seama
Tiparele specifice Android
De ex:
Implementarea tratării unui click – OnClickListener (apelul metodei onClick)
Folosim o implementare a unui tipar de tip Observer
Implementarea unui popup folosind AlertDialog
Folosim AlertDialog.Builder – care folosește defapt tiparul Builder
https://en.wikipedia.org/wiki/Software_design_pattern#Classification_and_list

În principiu avem 4 categorii.


Câteva exemple:
Creation Structural
Singleton Adapter
Builder Facade
Factory method Decorator
Behavioral Concurrency
Observer Lock
Strategy Scheduler
Iterator Read-write lock

Tiparele sunt de obicei reprezentate prin diagrame UML


Acest tipar restricționează crearea unui obiect la o singură
instanță
Idea este de a accesa global acest singur obiect
Acest tipar este implementat prin crearea obiectului dacă acesta
nu a fost creat încă sau întoarcerea instanței deja create altfel
Diagrama UML corespunzătoare:
În cazul în care avem nevoie să folosim de exemplu Volley:
am dori să avem o singură coadă de cereri în toată aplicația
am dori să avem acces la această coadă din orice activitate sau fragment
public class MySingleton {
private static MySingleton sInstance;
public static MySingleton getInstance(){
if (sInstance == null) {
sInstance = new MySingleton();
}
return sInstance;
}
}

Modul de apel: MySingleton.getInstance();

Se poate adapta implementarea unui Singleton la Android


Datorită faptului că
metoda onCreate din clasa Application este apelată o singură dată, când se
pornește aplicația
instanța aplicației nu este distrusă decât la terminarea aplicației
putem implementa o metodă getInstance
Folosind această abordare, avem un public class MAApplication extends Application {
private static MAApplication sInstance;
private RequestQueue mRequestQueue;
singur singleton @Override

clasa noastă derivată din Application public void onCreate() {


super.onCreate();
sInstance = this;
Restul obiectelor accesibile global }
mRequestQueue = Volley.newRequestQueue(this);

sunt variabile membre public static MAApplication getInstance(){

}
return sInstance;

public RequestQueue getRequestQueue(){


return mRequestQueue;

A NU SE FOLOSI ACEASTĂ TEHNICĂ }


}

PENTRU A PĂSTRA DATE DOAR ÎN MAAplication.getInstance().getRequestQueue();

CLASA APLICAȚIEI !!!


Instanța poate fi distrusă și recreată
de Android
Acest tipar este folosit în multe locuri în Android
Tiparul este bazat pe un obiect care înregistrează alte obiecte pe
care le va notifica despre o schimbare de stare
Acest tipar poate fi folosit pentru a crea sisteme de tip
publish/subscribe
Un exemplu de implementare care înregistrează mai mulți observeri
public class MyObserved { public class MyObserver
public interface ObserverInterface{ implements MyObserved.ObserverInterface {
public void notify(); @Override
} public void notify(){
List<ObserverInterface> observersList; //Do something
public MyObserved(){ }
observersList = new ArrayList<ObserverInterface>(); }
}
public void addObserver(ObserverInterface observer){
observersList.add(observer);
}
public void removeObserver(ObserverInterface observer){
observersList.remove(observer);
}
public void notifyAllObservers(){
for (ObserverInterface observer : observersList){
observer.notify();
}
}
}

Dacă comparăm această implementare cu modul de tratare a unui click


Adăugăm un observator care implementează onClick și așteaptă un click:
myButton.setOnClickListener(observer);
Acesta este un element care se utilizează la folosirea unui
ListView sau a unui ViewPager, dar este și un tipar binecunoscut
Adapter este un tipar care se comportă ca și o legătură între
două interfețe incompatibile
Permite ca cele două interfețe să lucreze împreună
Adaptorul este apelat cu metoda cerută, dar intern apelează vechea
metodă din interfața adaptată
În Android un Adapter din android.widget.Adapter este un
obiect care este folosit pentru a crea un view pentru fiecare rând
dintr-o listă sau pentru fiecare page dintr-un viewpager
Deci adaptează date, un set de elemente și un set de view-uri
Pentru a implementa un adapter, trebuie să extindem
BaseAdapter și să suprascriem metodele:
getView()
getCount()
ViewHolder este un tipar particular folosit în Android în legătură
cu liste și adaptoare

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