O aplicație nu constă întotdeauna dintr-un singur ecran. De exemplu, am creat o foarte program util iar utilizatorul dorește să știe cine este autorul. Face clic pe butonul „Despre program” și ajunge la un nou ecran, unde există informații utile despre versiunea programului, autor, adresa site-ului, câte pisici are autorul etc. Gândiți-vă la un ecran de activitate ca la o pagină web cu un link către o altă pagină. Dacă te uiți la codul din fișier MainActivity.java din lecțiile anterioare, veți vedea că clasa noastră Activitate principala se aplica si la Activitate(sau moștenitorii săi) sau, mai precis, moștenit de la el.
Clasa publică MainActivity extinde AppCompatActivity
După cum ați putea ghici, ar trebui să creăm o nouă clasă, care ar putea arăta Activitate principalași apoi cumva comutați la el când este apăsat butonul.
Pentru experiment, vom lua programul din prima lecție și vom folosi butonul pentru experimente (sau pentru a crea proiect nou cu un singur buton pe ecran). În continuare, să creăm formă nouă a afișa Informatii utile... De exemplu, să arătăm utilizatorului ce face pisica când merge în stânga și în dreapta. De acord, asta este foarte Informații importante, dând un indiciu despre univers.
Vom crea o activitate nouă manual, deși studioul are șabloane gata făcute. Dar nu este nimic complicat și pentru o mai bună înțelegere este util să faci totul manual.
Să creăm un nou fișier de marcare XML activity_about.xmlîn dosar res / layout... Faceți clic dreapta pe folder aspect si alege din meniul contextual Nou | Fișier cu resurse de aspect... Va apărea o casetă de dialog. În primul câmp, introduceți numele fișierului activitate_despre... În al doilea, trebuie să introduceți elementul rădăcină. În mod implicit există ConstraintLayout... Ștergeți textul și introduceți ScrollView... Introducerea câtorva caractere este suficientă pentru ca studioul să sugereze opțiuni gata făcute, puteți apăsa imediat Enter, fără a aștepta introducerea completă a cuvântului:
Vom obține spațiul liber corespunzător, în care vom introduce elementul. TextView.
Informațiile vor fi preluate din resurse, și anume dintr-o resursă șir despre_text... Acum este evidentiat cu rosu, semnaland lipsa de informatii. Ai putea apăsa Alt + Enterși introduceți text în caseta de dialog. Dar, pentru exemplul nostru, această metodă nu va funcționa, deoarece textul nostru va fi pe mai multe linii, folosind caractere de control. Deci hai să o facem altfel. Să deschidem fișierul res / values / strings.xmlși introduceți manual următorul text:
Am folosit cele mai simple etichete de formatare a textului HTML, cum ar fi , , ... Pentru exemplul nostru, este suficient să evidențiem cu aldine cuvintele care se referă la pisică și la direcția de mișcare. Pentru a traduce textul în linie nouă utilizați simboluri \ n... Să adăugăm o altă resursă șir pentru titlul noului ecran:
Ne-am dat seama de marcaj. Apoi, trebuie să creați o clasă pentru fereastră DespreActivity.java... Alegeți din meniu Fișier | Nou | Clasa Javași completați câmpurile obligatorii. La început, este suficient să indicați doar numele. Apoi ocupă-te de alte domenii.
Să luăm un gol.
Clasa este acum aproape goală. Să adăugăm manual codul. Clasa trebuie să moștenească din clasa abstractă Activitate sau rudele lui ca FragmentActivity, AppCompatActivity etc. Adaugam extinde Activitatea... Clasa de activitate trebuie să aibă o metodă onCreate ()... Punem cursorul mouse-ului în interiorul clasei și selectăm din meniu Cod | Metode de anulare(Ctrl + O). În caseta de dialog, căutăm clasa dorită, puteți introduce primele caractere de pe tastatură pentru cautare rapida... În metoda creată, trebuie să apelați metoda setContentView () care va încărca pe ecran marcajul pregătit. Vom primi această opțiune.
Pachetul ru.alexanderklimov.helloworld; import android.app.Activity; import android.os.Bundle; / ** * Creat de Alexander Klimov la 01.12.2014. * / clasa publică AboutActivity extinde Activitatea (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_about);))
Acum începe cel mai important lucru. Sarcina noastră este să mergem la un nou ecran când se face clic pe butonul de pe primul ecran. Întoarce-te la clasă Activitate principala... Să scriem un handler de clic pe buton:
Public void onClick (Vizualizare vizualizare) (intenție de intenție = intenție nouă (MainActivity.this, AboutActivity.class); startActivity (intent);)
Aici am folosit metoda de gestionare a clicurilor pe butoane pe care am descris-o în lecție.
Pentru a începe un nou ecran, trebuie să creați o instanță a clasei Intențieși specificați clasa curentă în primul parametru și clasa pentru tranziție în al doilea parametru, avem aceasta DespreActivitate... După aceea, metoda este numită startActivity () care lansează un nou ecran.
Dacă încercați acum să testați funcționarea aplicației în emulator, veți primi un mesaj de eroare. Ce am greșit? Am ratat un pas important. Trebuie să înregistrați unul nou Activitateîn manifest AndroidManifest.xml... Găsiți acest fișier în proiectul dvs. și faceți dublu clic pe el. Se va deschide fereastra de editare a fișierului. Adăugați o nouă etichetă
Deci resursa șir a fost utilă despre_titlu... Lansați aplicația, faceți clic pe butonul și obțineți fereastra Despre program... Astfel, am învățat cum să creăm o fereastră nouă și să o apelăm la clic pe un buton. Și avem la dispoziție un program mega-convenient - acum va fi mereu la îndemână un indiciu ce face pisica când merge spre stânga.
Încă o dată vă atrag atenția asupra faptului că a doua clasă de activitate creată trebuie să moștenească din clasă Activitate sau similar cu el ( ListActivityși altele), să aibă un fișier de markup XML (dacă este necesar) și să fie scris în manifest.
După apelarea metodei startActivity () va începe o nouă activitate (în acest caz DespreActivitate), devine vizibil și se deplasează în partea de sus a stivei care conține componentele care rulează. La apelarea metodei finalizarea () dintr-o activitate nouă (sau când este apăsată o tastă de revenire tare) va fi închisă și scoasă din stivă. Dezvoltatorul poate naviga și la activitatea anterioară (sau la orice altă activitate) folosind aceeași metodă. startActivity ().
Cum să creați un al treilea ecran - o modalitate pentru leneși
Programatorii, ca și pisicile, sunt creaturi leneșe. Amintiți-vă întotdeauna că pentru activitate trebuie să creați un marcaj și o clasă de la care moștenește Activitate, și apoi nu uitați să înregistrați clasa în manifest - bine nafig.
În acest caz, selectați din meniu Fișier | Nou | Activitate | Activitate de bază(sau alt șablon). În continuare, va apărea fereastra familiară pentru crearea unei noi activități. Completam campurile obligatorii.
Faceți clic pe butonul finalizarea iar activitatea va fi gata. Pentru a verifica acest lucru, deschideți fișierul manifest și verificați dacă există o nouă intrare. Nu vorbesc despre fișierele de clasă și de markup, ele înșiși vor apărea în fața ta.
Adăugați singur un buton nou pe ecranul principal de activitate și scrieți codul pentru a merge la activitatea creată.
La început, v-aș sfătui să creați manual toate componentele necesare pentru o nouă activitate pentru a înțelege relația dintre clasă, markup și manifest. Și când puneți mâna pe el, puteți utiliza Expertul de creare a activității pentru a vă accelera munca.
Transferul de date între activități
Am folosit cel mai simplu exemplu pentru a apela un alt ecran de activitate. Uneori este necesar nu numai să apelați un nou ecran, ci și să transferați date către acesta. De exemplu, numele de utilizator. În acest caz, trebuie să utilizați o zonă specială extraData pe care o are clasa Intenție.
Regiune extraData este o listă de cupluri valoare cheie care se transmite împreună cu intenția. Șirurile sunt folosite ca chei, iar pentru valori puteți folosi orice tip de date primitive, matrice de primitive, obiecte de clasă Pachet si etc.
Pentru a transfera date către o altă activitate, utilizați metoda puneExtra ():
Intent.putExtra ("Cheie", "Valoare");
Activitatea de recepție ar trebui să apeleze la o metodă adecvată: getIntExtra (), getStringExtra () etc.:
Int count = getIntent (). GetIntExtra ("nume", 0);
Să refacem exemplul anterior. Avem deja trei activități. Prima activitate va avea două câmpuri de text și un buton. Aspectul poate fi după cum urmează:
A doua activitate A doua activitate instalați elementul TextView, în care vom afișa textul primit de la prima activitate. Să scriem următorul cod pentru metodă onCreate () la a doua activitate.
@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_second); String user = „Live”; String gift = „goașă”; TextView infoTextView = (TextView) findViewById R. id.textViewInfo); infoTextView.setText (utilizator + „, vi s-a oferit” + cadou);)
Dacă pornim programul acum și doar apelăm a doua fereastră, așa cum este descris în prima parte a articolului, vom vedea eticheta implicită Zhivotnoe, ți s-a dat o gaură pentru gogoși... De acord, este destul de păcat să primești astfel de mesaje.
Reparăm situația. Adăugați codul pentru prima activitate:
Public void onClick (Vizualizare vizualizare) (EditText userEditText = (EditText) findViewById (R.id.editTextUser); EditText giftEditText = (EditText) findViewById (R.id.editTextGift); Intenție = noua clasă Intenție /SecondActivity); / împingeți textul din primul câmp de text în cheia numelui de utilizator intent.putExtra ("nume de utilizator", userEditText.getText (). toString ()); // împinge textul din al doilea câmp de text în cheia cadou intent.putExtra ( „cadou”, giftEditText.getText (). toString ()); startActivity (intent);)
Am plasat un obiect într-un recipient special Intenție două chei cu valori preluate din câmpurile de text. Când utilizatorul introduce date în câmpurile de text, va merge în acest container și va fi transferat la a doua activitate.
A doua activitate ar trebui să fie pregătită pentru a primi mesaje calde după cum urmează (în aldine).
// Valori implicite String user = "Live"; String gift = „găuri pentru gogoși”; utilizator = getIntent (). getExtras (). getString ("nume utilizator"); cadou = getIntent (). getExtras (). getString ("cadou"); TextView infoTextView = (TextView) findViewById (R.id.textViewInfo); infoTextView.setText (utilizator + „, vi s-a oferit” + cadou);
Acum mesajul nu pare atât de ofensator, dar chiar plăcut pentru unii oameni. În exemplele complexe, este de dorit să adăugați validare la procesarea datelor. Există situații când începeți a doua activitate cu date goale, cum ar fi nul care ar putea bloca aplicația.
În cazul nostru, știm că așteptăm o valoare șir, așa că codul poate fi rescris astfel:
Intent intent = getIntent (); user = intent.getStringExtra („nume utilizator”);
Utilizator = getIntent (). GetStringExtra ("nume utilizator");
Programul are un dezavantaj - nu este clar de la cine primim salutări. Orice maimuță educată nu va accepta un cadou dintr-o sursă anonimă. Deci, ca temă pentru acasă, adăugați un alt câmp de text pentru a introduce numele utilizatorului care trimite mesajul.
Google recomandă utilizarea următorului format pentru chei: numele pachetului dvs. ca prefix, urmat de cheia însăși. În acest caz, puteți fi sigur că cheia este unică atunci când interacționați cu alte aplicații. Ceva de genul:
Public final static String USER = "ru.alexanderklimov.myapp.USER";
Cine a înrămat pisica Vaska - obținem rezultatul înapoi
Nu este întotdeauna suficient să transmiteți pur și simplu date unei alte activități. Uneori doriți să obțineți informații din altă activitate când o închideți. Dacă mai devreme am folosit metoda startActivity (intenție de intenție), atunci există o metodă înrudită startActivityForResult (intenție de intenție, int RequestCode)... Diferența dintre metode este parametrul suplimentar RequestCode... Practic, este doar un număr întreg pe care îl poți inventa singur. Este necesar pentru a distinge de la cine a venit rezultatul. Să presupunem că aveți cinci ecrane suplimentare și le atribuiți valori de la 1 la 5, iar din acest cod puteți determina al cui rezultat trebuie să procesați. Puteți utiliza valoarea -1, atunci va fi la fel cu apelarea metodei startActivity (), adică nu vom obține niciun rezultat.
Daca folosesti metoda startActivityForResult (), atunci trebuie să înlocuiți metoda din cod pentru a primi rezultatul onActivityResult ()și procesează rezultatul. Confuz? Să luăm un exemplu.
Să presupunem că ești detectiv. Au existat informații că două bucăți de cârnați și alte produse au fost furate de pe masa unei persoane influente dintr-un restaurant. Suspiciunea a căzut asupra a trei suspecți - o cioară, un nenorocit de cățeluș și pisica Vaska.
Unul dintre vizitatori a oferit o serie de fotografii de pe iPhone-ul său ponty:
Există și mărturie de la un alt martor: Și Vaska ascultă, dar mănâncă.
Creați un nou proiect Sherlock cu două activități. Pe primul ecran va apărea un buton pentru a trece la al doilea ecran și o etichetă text în care va fi afișat numele hoțului.
Al doilea ecran va avea un grup de butoane radio:
Deoarece ne vom aștepta la un răspuns de la al doilea ecran, trebuie să folosim metoda startActivityForResult () pe primul ecran în care trecem variabila ALEGE_HOȚUL ca parametru RequestCode.
Static final privat int CHOOSE_THIEF = 0; public void onClick (View v) (Intenție questionIntent = new Intent (MainActivity.this, ChooseActivity.class); startActivityForResult (questionIntent, CHOOSE_THIEF);)
Aruncă o privire la cod. Când se face clic pe butonul, vom lucra cu al doilea ecran Alegeți Activitateși lansați al doilea ecran, așteptând rezultatul.
Accesați al doilea ecran și scrieți codul pentru a doua activitate.
Public final static String THIEF = "ru.alexanderklimov.sherlock.THIEF"; public void onRadioClick (Vizualizare v) (Intenție answerIntent = noua Intenție (); comutator (v.getId ()) (caz R.id.radioDog: answerIntent.putExtra (THIEF, „Fucking dog”); break; case R.id .radioCrow: answerIntent.putExtra (THIEF, „Crow”); break; case R.id.radioCat: answerIntent.putExtra (THIEF, „Calul lui Przewalski”); break; implicit: break;) setResult (RESULT_OK, answerIntent); finish; ();)
Totul este simplu aici, când detectivul alege numele criminalului, apoi prin metoda puneExtra () trecem numele cheii și valoarea acesteia.
Pentru comoditate, după selectare, închidem imediat a doua fereastră și transmitem valoarea înainte de a o închide RESULT_OK să fie clar că alegerea a fost făcută. Dacă utilizatorul închide ecranul prin butonul Înapoi, valoarea va fi transmisă RESULT_CANCELED.
Metodă setResult () ia doi parametri: codul rezultat și rezultatul însuși, reprezentat ca o intenție. Codul rezultat vă spune cu ce rezultat s-a încheiat activitatea, de regulă, acesta este fie Activitate.RESULT_OK sau Activitate.RESULT_CANCELED... În unele cazuri, poate fi necesar să utilizați propriul cod de returnare pentru a gestiona variațiile specifice aplicației dvs. Metodă setResult () acceptă orice valoare întreagă.
Dacă veți trece datele în mod explicit printr-un buton, atunci ar fi bine să adăugați o metodă finalizarea () pentru a închide a doua activitate ca fiind inutilă. Dacă tranziția are loc prin butonul Înapoi, atunci acest lucru nu este necesar.
Dacă activitatea a fost închisă de utilizator când a fost apăsat butonul de revenire hardware sau dacă metoda finalizarea () a fost numit înainte de metodă setResult (), codul rezultat va fi setat la RESULT_CANCELED iar intenția returnată va afișa valoarea nul.
Revenim la primul ecran. Primul ecran așteaptă un răspuns de la al doilea ecran, așa că trebuie să adăugați metoda la cod onActivityResult ().
@Override protejat void onActivityResult (int requestCode, int resultCode, date Intent) (super.onActivityResult (requestCode, resultCode, date); TextView infoTextView = (TextView) findViewById (R.id.textViewInfo); if (requestCode == CHOOSE_THIEF) if (resultCode == RESULT_OK) (String thiefname = data.getStringExtra (ChooseActivity.THIEF); infoTextView.setText (thiefname);) else (infoTextView.setText (""); // șterge textul)))
Metoda așteaptă date primite cu cod ALEGE_HOȚUL, iar dacă astfel de date ajung, atunci extrage valoarea din cheie AlegeActivitate.HOȚ folosind metoda getStringExtra... Emitem valoarea rezultată în TextView(variabil infoTextView). Dacă ne-am întors la ecran prin butonul Înapoi, atunci pur și simplu ștergem textul.
Când activitatea copilului este închisă în interiorul componentei părinte, handlerul este declanșat onActivityResult ()... Handler onActivityResult () ia mai mulți parametri.
- Solicitați codul. Codul care a fost folosit pentru a începe activitatea returnând rezultatul
- Cod rezultat. Codul rezultat stabilit de activitatea copilului care indică modul în care s-a încheiat activitatea. Poate fi orice valoare întreagă, dar de obicei oricare Activitate.RESULT_OK sau Activitate.RESULT_CANCELED
- Date. Intenția utilizată pentru a împacheta datele returnate. În funcție de scopul activității copilului, aceasta poate include o cale URI reprezentând piesa de conținut selectată. Alternativ (sau suplimentar), activitatea copilului poate returna informații sub formă de valori simple, împachetate într-un parametru de intenție in plus
Dacă activitatea copilului s-a încheiat în mod neașteptat sau dacă nu a fost specificat niciun cod de rezultat înainte de a o închide, acest parametru va deveni Activitate.RESULT_CANCELED.
Lansăm proiectul, facem clic pe butonul și mergem la al doilea ecran. Acolo alegem una dintre variante. Dacă selectezi cioara, ecranul se va închide și numele vinovatului va fi afișat pe primul ecran. Dacă selectați un câine, numele acestuia va fi afișat.
Apropo, dacă selectați o pisică, numele acesteia nu va fi afișat! Verificați-l și vedeți singur. Vei întreba de ce? Primar Watson! Făptuitorul nu a ținut cont de un detaliu important. Restaurantul a fost monitorizat cu camere video, iar videoclipul a arătat cine a furat de fapt cârnatul și a înrămat pisica. Vaska, stai!
P.S. Dacă la început ceva părea de neînțeles, atunci cu practică multe vor deveni clare. Transferul de date între ecrane este obișnuit în aplicații și veți studia exemplul din nou și din nou.
P.P.S. Cel mai bun pește este cârnații. Cunoscând această slăbiciune, nu a fost greu să încadrezi pisica.
Folosind filtre
În articol, am arătat o modalitate obișnuită de a trece la o altă activitate, atunci când sunteți în metodă startActivity () sunt indicate clasa curentă și clasa pentru tranziție. Apropo, clasa de activitate nu trebuie să facă parte din aplicația dvs. Dacă știți numele clasei dintr-o altă aplicație, puteți trece la ea. Dar poți merge la altă activitate într-un mod diferit.
În practică, este mai puțin obișnuit, dar poate fi util. Să presupunem că aveți deja o a doua activitate. Adăugați un filtru special în manifest:
Și lansăm a doua activitate făcând clic pe butonul în acest fel.
Public void onClick (Vizualizare vizualizare) (startActivity (nouă intenție ("ru.alexanderklimov.testapplication.SecondActivity"));)
Să înlocuim șirul lung cu o constantă.
Public static final String ACTION_SECOND_ACTIVITY = "ru.alexanderklimov.testapplication.SecondActivity"; public void onClick (Vizualizare vizualizare) (startActivity (Intenție nouă (ACTION_SECOND_ACTIVITY));)
Deci ce am făcut. Pentru a doua activitate, am adăugat un filtru și am specificat un nume pentru acțiuneîn atribut android: nume... Pentru comoditate, am pus doar numele complet al activității cu numele pachetului în ea. Constructor de clasă Intenție are mai multe versiuni supraîncărcate. Într-o versiune, puteți specifica un șir pentru acțiune. Am indicat acțiunea noastră creată, care este înregistrată în a doua activitate. Sistemul analizează manifestele tuturor aplicațiilor instalate în timpul funcționării. Când caută o potrivire, sistemul găsește filtrul nostru și lansează activitatea dorită.
Alte activități pot fi lansate folosind același principiu. Aruncă o privire la un exemplu. Dacă copiați exemplul pentru dvs. și aruncați o privire la documentația pt android.provider.Settings.ACTION_AIRPLANE_MODE_SETTINGS, veți vedea că acest cod corespunde constantei șir public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS = „android.settings.AIRPLANE_MODE_SETTINGS”... Comparați cu codul nostru. Puteți presupune că activitatea setărilor pentru modul offline are această linie scrisă în filtru.
Filtrați numele categoriei android.intent.category.DEFAULT spune sistemului să ia acțiunea implicită, care este să înceapă activitatea. Mai sunt si alte nume care nu ne intereseaza inca.
Și acum o întrebare pentru completare. Ce se întâmplă dacă creați o altă activitate și specificați același filtru ca a doua activitate? Hai să verificăm. Creează o a treia activitate în tine și copiază blocul cu filtrul din a doua activitate în el.
Faceți clic pe butonul din prima activitate. Sistemul vă va cere să selectați opțiunea dorită.
Dacă selectați elementul MEREU, nu va trebui să alegeți data viitoare. Pentru a reseta selecția, accesați proprietățile aplicației din Setări și găsiți butonul Ștergeți setările implicite.
Desfășurarea unei activități după numele ei
În constructor Intenție al doilea parametru este clasa. Dar să presupunem că există un fel de bază de date în care sunt specificate numele activităților și trebuie să lansăm activitatea necesară după numele acesteia. Putem obține clasa în sine pe baza variabilei șir și începe activitatea.
Încercați (// Numele complet al clasei de activitate String activityName = "ru.alexanderklimov.testapplication.SecondActivity"; // obțineți obiectul Class Class>myClass = Class.forName (activityName); Intent intent = new Intent (this, myClass); startActivity (intenție); ) catch (ClassNotFoundException e) (e.printStackTrace ();)
Cumva aveam sarcina de a transfera date de la serviciu în activitate. Au început să caute o soluție în SDK-ul standard, dar din moment ce nu era timp, am făcut o soluție proastă sub forma utilizării unei baze de date. Dar întrebarea era deschisă și după un timp mi-am dat seama de o modalitate mai corectă, care este în SDK - folosind clasele Message, Handler, Messenger.
Idee
Trebuie să transferăm date de la activitate la serviciu și invers. Cum facem asta? Avem deja tot ce ne trebuie pentru a ne rezolva problema. Tot ce trebuie să faceți este să legați serviciul de activitate folosind bindService, să treceți parametrii necesari și puțină magie sub forma utilizării claselor Message. Și magia este să folosiți variabile de instanță Message și, în special, replyTo. Avem nevoie de această variabilă pentru a ne referi la instanța Messanger a serviciului de la activare și în serviciu la instanța Messanger a activării. De fapt, nu este atât de simplu. Cel puțin pentru mintea mea nu cea mai înzestrată. În parte, doar îmbunătățesc documentația care există deja - Servicii. De asemenea, există un exemplu bun pe StackOverflow. In orice caz, sper ca articolul sa fie de folos macar cuiva si nu am lucrat degeaba.
Exemplu
De exemplu, vom implementa un serviciu care va crește și scade valoarea contorului și va returna rezultatul activității, la TextView. Voi omite codul de aspect, deoarece există două butoane și un câmp de text - totul este simplu.
Implementarea
Voi da codul de activare complet:
Clasa publică MainActivity extinde activitatea (public static final String TAG = "TestService"; TestServiceConnection testServConn; TextView testTxt; final Messenger messenger= nou Messenger (nou IncomingHandler ()); Messenger toServiceMessenger; @Override public void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); testTxt = (TextView) findViewById (R.id.test_txt); bindService (new Intent (this, TestService.class). ), (testServConn = nou TestServiceConnection ()), Context.BIND_AUTO_CREATE);) @Override public void onDestroy () (super.onDestroy (); unbindService (testServConn);) public void countIncrClick (butonul Vizualizare) (Message msg = Message. obține (null, TestService.COUNT_PLUS); msg.replyTo = messenger; încercați (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) public void countDecrClick (butonul Vizualizare) (Message msg = Message.obtain (null, TestService.COUNT_MINUS); msg.replyTo = messenger; încercați (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) clasă privată IncomingHandler extinde Handler (@Override public void handleMessage (Message msg) (comutator (msg.what) (caz TestServic) e.GET_COUNT: Log.d (TAG, "(activitate) ... obține număr"); testTxt.setText ("" + msg.arg1); pauză; ))) clasa privată TestServiceConnection implementează ServiceConnection (@Override public void onServiceConnected (nume ComponentName, serviciu IBinder) (toServiceMessenger = new Messenger (serviciu); // trimite valoarea initiala counter Message msg = Message.obtain (null, TestService.SET_COUNT); msg.replyTo = mesager; msg.arg1 = 0; // încercarea noastră de contor (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) @Override public void onServiceDisconnected (nume ComponentName) ()))
Lasă-mă să explic. La crearea unei activități, ne legăm imediat de serviciu, implementând interfața ServiceConnection și în ea trimitem un mesaj către serviciul „set the counter value”, trecând zero și creând toServiceMessanger, pasând interfața IBinder constructorului. Apropo, serviciul trebuie să returneze această instanță, altfel va fi NPE. Folosim această clasă pentru a trimite mesaje către serviciu. Și aici este magic - în variabila replyTo salvăm cealaltă instanță a noastră Messenger - cea care primește un răspuns de la server și prin aceasta se va realiza conexiunea cu activitatea.
Pentru a primi un mesaj de la serviciu, folosim Handler-ul nostru și pur și simplu căutăm variabilele de care avem nevoie și facem acțiuni asupra lor. Făcând clic pe butoanele (metode countIncrClick, countDecrClick), trimitem solicitări către serviciu, indicând acțiunea dorită în variabila msg.what.
Pachetul com.example.servicetest; import android.app.Service; import android.content.*; import android.os *; import android.os.Process; import android.util.Log; clasă publică TestService extinde Serviciul (public static final int COUNT_PLUS = 1; public static final int COUNT_MINUS = 2; public static final final SET_COUNT = 0; public static final int GET_COUNT = 3; int count = 0; IncomingHandler inHandler; Messenger mesager; Messenger toActivityMessenger; @Override public void onCreate () (super.onCreate (); thread HandlerThread = nou HandlerThread ("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start (); inHandler = new IncomingHandler (thread.getLooper) (inHandler);) @Override public IBinder onBind (Intent arg0) (return messanger.getBinder ();) @Override public int onStartCommand (intenție, int flags, int startId) (return START_STICKY;) // clasa privată de gestionare a mesajelor IncomingHandler extinde Handler (public IncomingHandler (Looper looper) (super (looper);) @Override public void handleMessage (Message msg) (//super.handleMessage(msg); toActivityMess enger = msg.replyTo; comutați (msg.what) (case SET_COUNT: count = msg.arg1; Log.d (MainActivity.TAG, „(serviciu) ... set count”); break; case COUNT_PLUS: count ++; Log.d (MainActivity) .TAG , "(serviciu) ... numărare plus"); pauză; caz COUNT_MINUS: Log.d (MainActivity.TAG, "(serviciu) ... numărare minus"); numărare--; pauză;) // trimite valoarea contorului din activitatea Message outMsg = Message.obtain (inHandler, GET_COUNT); outMsg.arg1 = număr; outMsg.replyTo = mesager; încercați (dacă (toActivityMessenger! = null) toActivityMessenger.send (outMsg);) catch (RemoteException e) (e.printStackTrace ();))))
Toate prin analogie cu logica din activitate. Nici nu știu dacă trebuie să explic ceva. Singurul lucru este că trimit imediat o solicitare înapoi la activitatea din handleMessage, folosind variabila magic replyTo și trăgând Messenger-ul dorit de mai sus. Și al doilea punct pe care l-am menționat deja este:
@Override public IBinder onBind (Intenție arg0) (return messanger.getBinder ();)
fără de care totul va cădea. Această instanță a interfeței va fi transmisă ServiceConnection
Concluzie
În întregime. Acesta este exemplul inventat al interacțiunii dintre activități și servicii. Mi se pare că aceasta este o interacțiune destul de netrivială, deși cuiva poate părea diferit.
Întrebări, clarificări și multe altele în mod personal. Pot exista inexactități cu privire la orice aspect, așa că nu ezitați să scrieți și să corectați.
Sper că postarea a fost utilă cititorilor.
Buna ziua.
Este necesar să transferați datele primite prin UART către Activitate. Acest lucru se poate face prin crearea unui fir în Activitate în care să organizați o buclă while (! IsInterrupted ()) și să citiți datele din bufferul UART. După aceea, apelând firul de interfață al activității - MainActivity.this.runOnUiThread (noul Runnable (), efectuați acțiunile necesare cu această Activitate. Dar dacă apelăm alte Activități din Activitatea principală, atunci firul organizat nu permite transferul date către Activitatea nou creată. Dacă înțeleg corect că, pentru ca datele din flux să fie transferate la orice Activitate, fluxul trebuie creat nu în Activitate, ci în Serviciu.
Întrebare: datele au venit prin UART, într-un flux (care este creat în Servce) este necesar să transferați date către Activitate, care este acum activă, cum se poate face acest lucru și se face deloc?
1 raspuns
În fiecare activitate, creați un Handler. În metoda onResume () a acestei activități, bindService (). Acolo, unul dintre parametrii este interfața ServiceConnection. Implementați-l cel puțin cu aceeași Activitate. Implementați metoda onServiceConnected () în ea. În acest apel invers, Serviciul în sine vine ca unul dintre parametri. Deci, numiți acest serviciu propria metodă setHandler (). Treceți acolo Handler-ul care se află în Activitatea curentă. Dar trimiteți datele primite prin UART către Serviciul de pe acest Handler. Apropo, Handler rulează în mod tradițional pe firul principal, așa că nu va trebui să rulațiOnUiThread pentru a executa.