Concurrency pe Android cu Service

În acest tutorial vom explora Serviciu componentă și superclase sale, IntentService. Veți afla când și cum să utilizați această componentă pentru a crea soluții de concurrency excelente pentru operațiile de fundal de lungă durată. De asemenea, vom analiza rapid IPC (Inter Process Communication), pentru a învăța cum să comunicați cu serviciile care rulează pe diferite procese.

Pentru a urma acest tutorial, veți avea nevoie de o înțelegere a concurenței pe Android. Dacă nu știți prea multe despre asta, ați putea dori să citiți mai întâi unele dintre celelalte articole despre acest subiect.

1. Componenta de serviciu

Serviciu componenta este o parte foarte importantă a cadrului concurenței Android. Acesta îndeplinește necesitatea de a efectua o operațiune de lungă durată în cadrul unei aplicații sau oferă anumite funcționalități pentru alte aplicații. În acest tutorial ne vom concentra exclusiv pe Serviciude lungă durată și capacitatea de a folosi această putere pentru a îmbunătăți concurența.

Ce este un serviciu?

Serviciu este o componentă simplă, care este instanțiată de sistem pentru a efectua o activitate de lungă durată care nu depinde în mod necesar de interacțiunea utilizatorului. Aceasta poate fi independentă de ciclul de viață al activității și poate, de asemenea, să se desfășoare într-un proces complet diferit.

Înainte de a scufunda într-o discuție despre ce a Serviciu reprezintă, este important să subliniem că, deși serviciile sunt utilizate în mod obișnuit pentru operațiunile de fundal de lungă durată și pentru a executa sarcini pe diferite procese, A Serviciu nu reprezintă a Fir sau un proces. Acesta se va executa numai într-un fir de fundal sau într-un proces diferit, dacă este explicit solicitat să facă acest lucru.

A Serviciu are două caracteristici principale:

  • O facilitate de aplicare pentru a spune sistemului despre ceva ce vrea să facă în fundal.
  • O facilitate pentru o aplicație pentru a expune o parte din funcționalitatea sa la alte aplicații.

Servicii și subiecte

Există o mulțime de confuzii cu privire la servicii și fire. Când un Serviciu este declarat, nu conține a Fir. De fapt, în mod implicit, rulează direct pe firul principal și orice lucrare efectuată pe acesta poate îngheța o aplicație. (Cu excepția cazului în care este a IntentService, A Serviciu subclasa care vine deja cu un thread de lucrător configurat.)

Deci, cum oferă serviciile o soluție de concurență? Ei bine, a Serviciu nu conține în mod implicit un fir, dar poate fi ușor configurat pentru a lucra cu firul propriu sau cu o mulțime de fire. Vom vedea mai multe despre asta mai jos.

Dacă nu luați în considerare lipsa unui fir încorporat, a Serviciu este o soluție excelentă pentru problemele de concurență în anumite situații. Principalele motive pentru a alege o Serviciu peste alte soluții de concurență cum ar fi AsyncTask sau cadrul HaMeR sunt:

  • A Serviciu poate fi independent de ciclurile de viață ale activității.
  • A Serviciu este adecvată pentru derularea operațiunilor lungi.
  • Serviciile nu depind de interacțiunea utilizatorului.
  • Atunci când rulează pe diferite procese, Android poate încerca să mențină serviciile în viață chiar și atunci când sistemul nu are resurse.
  • A Serviciu poate fi repornit pentru a-și relua activitatea.

Tipuri de servicii

Există două tipuri de Serviciu, a început și legat.

a început serviciul este lansat prin Context.startService (). În general, acesta efectuează o singură operație și va funcționa pe termen nelimitat până la terminarea operațiunii, apoi se închide. În mod normal, acesta nu returnează niciun rezultat interfeței cu utilizatorul.

legat de serviciu este lansat prin Context.bindService (), și permite o comunicare bidirecțională între client și client Serviciu. De asemenea, se poate conecta cu mai mulți clienți. Se distruge atunci când nu există niciun client conectat la el.

Pentru a alege între cele două tipuri,  Serviciu trebuie să pună în aplicare câteva apeluri de apel: onStartCommand () pentru a rula ca serviciu început, și onBind () pentru a rula ca serviciu limitat. A Serviciu poate alege să implementeze doar unul dintre aceste tipuri, dar poate adopta ambele în același timp fără probleme. 

2. Implementarea serviciului

Pentru a utiliza un serviciu, extindeți Serviciu și suprascrie metodele de apel invers, în funcție de tipul de Serviciu . Așa cum am menționat anterior, pentru serviciile începute onStartCommand () trebuie aplicată metoda și pentru serviciile obligatorii, onBind () metodă. De fapt, onBind ()metoda trebuie declarată pentru fiecare tip de serviciu, dar poate reveni la nul pentru serviciile pornite.

clasa publica CustomService extinde serviciul @Override public int onStartCommand (Intent intent, int flags, int startId) // Executa operatiunile dumneavoastra // Serviciul nu va fi incheiat automat returneaza Service.START_NOT_STICKY;  @Nullable @Override publice IBinder onBind (Intent intent) // Creează o conexiune cu un client // utilizând o interfață implementată la IBinder return null; 
  • onStartCommand (): lansat de Context.startService (). Acest lucru este de obicei chemat dintr-o activitate. Odată ce ați sunat, serviciul se poate desfășura pe o perioadă nedeterminată și depinde de dvs. să îl opriți, fie prin apel stopSelf () sau stopService ().
  • onBind (): apelat când o componentă dorește să se conecteze la serviciu. Chemat pe sistem de către Context.bindService (). Se întoarce IBinder care oferă o interfață pentru a comunica cu clientul.

Ciclul de viață al serviciului este de asemenea important să fie luat în considerare. onCreate ()  și onDestroy () ar trebui implementate metode de inițializare și închidere a oricăror resurse sau operațiuni ale serviciului.

Declararea unui serviciu pe manifeste

 Serviciu componentă trebuie să fie declarată pe manifest cu  element. În această declarație este posibil, dar nu obligatoriu, să se stabilească un alt proces pentru Serviciu a alerga în.

...  ...  

2.2. Lucrul cu serviciile începute

Pentru a iniția un serviciu început, trebuie să apelați Context.startService () metodă. scop trebuie să fie create cu Context si Serviciu clasă. Orice informație sau date relevante trebuie să fie transmise și în acest sens scop.

Intent serviceIntent = intenție nouă (acest lucru, CustomService.class); // Transmiterea datelor care urmează a fi procesate pe datele pachetului de servicii = nou pachet (); data.putInt ("Tip de operare", 99); data.putString ("DownloadURL", "http://mydownloadurl.com"); serviceIntent.putExtras (date); // Pornirea serviciului startService (serviceIntent);

În dvs. Serviciu clasă, metoda pe care ar trebui să o faceți este onStartCommand (). În această metodă, trebuie să apelați orice operație pe care doriți să o executați la serviciul inițiat. Veți procesa scop pentru a capta informațiile trimise de client. startId reprezintă un ID unic, creat automat pentru această cerere specifică și steaguri poate conține informații suplimentare despre el.

 @Override public int peStartCommand (Intent intent, int flags, int startId) Bundle data = intent.getExtras (); dacă (date! = null) int operație = data.getInt (KEY_OPERATION); // Verificați ce operație să efectuați și trimiteți un msg dacă (operație == OP_DOWNLOAD) // efectuați descărcarea returnați START_STICKY; 

onStartCommand () returnează o constantă int care controlează comportamentul:

  • Service.START_STICKY: Serviciul este repornit dacă se termină.
  • Service.START_NOT_STICKY: Serviciul nu este repornit.
  • Service.START_REDELIVER_INTENT: Serviciul este repornit după un accident și intențiile de prelucrare vor fi redeschise.

După cum am menționat mai sus, serviciul inițiat trebuie oprit, altfel acesta va dura nelimitat. Acest lucru se poate face fie de către Serviciu apel stopSelf () pe sine sau prin apelul unui client stopService () pe el.

void someOperation () // efectuați o operație de lungă durată // și opriți serviciul atunci când este terminat stopSelf (); 

Legarea serviciilor

Componentele pot crea conexiuni cu serviciile, stabilind o comunicare bidirecțională cu aceștia. Clientul trebuie să sune Context.bindService (), trecerea unui scop, A ServiceConnection interfață și a steag ca parametri. A Serviciu poate fi legat la mai mulți clienți și va fi distrusă odată ce nu are clienți conectați la acesta.

void bindWithService () Intent intent = intenție nouă (aceasta, PlayerService.class); // obligați cu Service bindService (intent, mConnection, Context.BIND_AUTO_CREATE); 

Este posibil să trimiteți Mesaj obiecte pentru servicii. Pentru a face acest lucru va trebui să creați o Mesager pe partea clientului într-un ServiceConnection.onServiceConnected implementarea interfeței și folosiți-o pentru a trimite Mesaj obiecte la Serviciu.

private ServiceConnection mConnection = nou ServiceConnection () @Override public void onServiceConnected (ComponentName className, serviciul IBinder) // utilizați IBinder-ul primit pentru a crea un Messenger mServiceMessenger = nou Messenger (serviciu); mBound = adevărat;  @Override publice void onServiceDisconnected (ComponentName arg0) mBound = false; mServiceMessenger = null; ;

De asemenea, este posibil să transmiteți un răspuns Mesager la Serviciu pentru ca clientul să primească mesaje. Ferește-te, totuși, pentru că clientul nu mai este în jur pentru a primi mesajul serviciului. Ați putea, de asemenea, să utilizați BroadcastReceiver sau orice altă soluție difuzată.

 managerul privat mResponseHandler = manipulator nou () @Override public void handleMessage (Mesaj mesaj) // răspunsul mânerului de la Serviciu; Mesaj msgReply = Message.obtain (); msgReply.replyTo = Messenger nou (mResponseHandler); încercați mServiceMessenger.send (msgReply);  captură (RemoteException e) e.printStackTrace (); 

Este important să dezactivați Serviciul când clientul este distrus.

@Override protejate void onDestroy () super.onDestroy (); // deconectați de la serviciu dacă (mBound) unbindService (mConnection); mBound = false; 

Pe Serviciu parte, trebuie să pună în aplicare Service.onBind () metoda, oferind un IBinder furnizate de la Mesager. Aceasta va transmite un răspuns manipulant să se ocupe de Mesaj obiecte primite de la client.

 IncomingHandler (PlayerService playerService) mPlayerService = new WeakReference <> (playerService);  @Override public void handleMessage (Mesaj mesaj) // mesaje de manipulare public IBinder onBind (intent intent) // a trece un Binder folosind Messenger creat întoarcere mMessenger.getBinder ();  final Messenger mMessenger = Messenger nou (noul IncomingHandler (acest));

3 Concurrency Utilizarea serviciilor

În cele din urmă, este timpul să vorbim despre cum să rezolvăm problemele de concurrency folosind serviciile. După cum am menționat mai devreme, un standard Serviciu nu conține nicio extra fire și va rula pe principalele Fir în mod implicit. Pentru a depăși această problemă, trebuie să adăugați un lucrător Fir, o mulțime de fire sau executați Serviciu pe un alt proces. Ați putea folosi și o subclasă de Serviciu denumit IntentService care conține deja a Fir.

Efectuarea unui serviciu de rulare pe un fir de lucru

Pentru a face Serviciu executați pe fundal Fir ai putea crea doar un extra Fir și conduceți slujba acolo. Cu toate acestea, Android ne oferă o soluție mai bună. O modalitate de a profita cât mai bine de sistem este implementarea cadrului HaMeR în interiorul sistemului Serviciu, de exemplu prin looparea a Fir cu o coadă de mesaje care poate procesa mesaje pe termen nelimitat.

Este important să înțelegeți că această implementare va procesa secvențial sarcinile. Dacă trebuie să primiți și să procesați mai multe sarcini în același timp, ar trebui să utilizați o mulțime de fire. Folosirea piscinelor de filete nu face parte din acest tutorial și nu vom mai vorbi astăzi. 

Pentru a utiliza HaMeR trebuie să furnizați Serviciu cu maieză, A manipulant și a HandlerThread.

 Looper privat mServiceLooper; privat ServiceHandler mServiceHandler; // Handler pentru a primi mesaje de la clasa finală privată client ServiceHandler extinde Handler ServiceHandler (looper looper) super (looper);  @Override public void handleMessage (Mesaj msg) super.handleMessage (msg); // gestionarea mesajelor // oprirea serviciului folosind oprirea startId stopSelf (msg.arg1);  @Override public void onCreate () HandlerThread thread = noul HandlerThread ("ServiceThread", Process.THREAD_PRIORITY_BACKGROUND); thread.start (); mServiceLooper = thread.getLooper (); mServiceHandler = nou serviciuHandler (mServiceLooper);  

În cazul în care cadrul HaMeR nu vă este cunoscut, citiți tutorialele noastre despre concursul HaMer pentru Android.

IntentService

Dacă nu este nevoie de Serviciu pentru a fi păstrat viu pentru o lungă perioadă de timp, ați putea folosi IntentService, A Serviciu subclasa care este gata să execute sarcini pe fire de fundal. Intern, IntentService este a Serviciu cu o implementare foarte asemănătoare cu cea propusă mai sus. 

Pentru a utiliza această clasă, tot ce trebuie să faceți este extinderea acesteia și implementarea acesteia onHandleIntent (), o metodă de cârlig care va fi apelată de fiecare dată când un client solicită startService () pe aceasta Serviciu. Este important să rețineți că IntentService se va opri de îndată ce se termină activitatea.

clasa publica MyIntentService extinde IntentService public MyIntentService () super ("MyIntentService");  @Override protejat void onHandleIntent (Intent intent) // handle Intents trimite prin startService

IPC (comunicare inter-proces)

A Serviciu poate rula pe un complet diferit Proces, independent de toate sarcinile care se întâmplă în procesul principal. Un proces are propria sa alocare de memorie, grup de fire și priorități de procesare. Această abordare poate fi foarte utilă atunci când trebuie să lucrați independent de procesul principal.

Comunicarea între diferitele procese se numește IPC (Inter Process Communication). Într-un Serviciu există două modalități principale de a face IPC: folosind a Mesager sau implementarea unui AIDL interfață. 

Am învățat cum să trimiteți și să primiți mesaje între servicii. Tot ce trebuie să faceți este să utilizați creați un Mesager folosind IBinder instanță primită în timpul procesului de conectare și folosiți-o pentru a trimite un răspuns Mesager înapoi la Serviciu.

 managerul privat mResponseHandler = manipulator nou () @Override public void handleMessage (Mesaj mesaj) // răspunsul mânerului de la Serviciu; private ServiceConnection mConnection = nou ServiceConnection () @Override public void onServiceConnected (ComponentName className, serviciul IBinder) // utilizați IBinder-ul primit pentru a crea un Messenger mServiceMessenger = nou Messenger (serviciu); Mesaj msgReply = Message.obtain (); msgReply.replyTo = Messenger nou (mResponseHandler); încercați mServiceMessenger.send (msgReply);  captură (RemoteException e) e.printStackTrace (); 

 AIDL interfața este o soluție foarte puternică care permite apeluri directe Serviciu metodele care rulează pe diferite procese și este potrivit să se utilizeze atunci când dvs. Serviciu este într-adevăr complexă. in orice caz, AIDL este complicat să se implementeze și este rar folosit, astfel încât utilizarea sa nu va fi discutată în acest tutorial.

4. Concluzie

Serviciile pot fi simple sau complexe. Depinde de nevoile aplicației dvs. Am încercat să acoperez cât mai mult posibil pe acest tutorial, totuși m-am concentrat doar pe utilizarea serviciilor în scopuri concurente și există mai multe posibilități pentru această componentă. Vreau să studiez mai mult, să aruncăm o privire la ghidul de documentație și Android. 

Ne vedem în curând!

Cod