Egy alkalmazás nem mindig egyetlen képernyőből áll. Például létrehoztunk egy nagyon hasznos programés a felhasználó tudni akarja, hogy ki a szerző. Rákattint az "A programról" gombra, és egy új képernyőre kerül, ahol hasznos információk találhatók a program verziójáról, a szerzőről, a webhely címéről, hány macskája van a szerzőnek stb. Képzelje el a tevékenység képernyőt úgy, mint egy weboldalt, amely egy másik oldalra mutató hivatkozást tartalmaz. Ha megnézed a kódot a fájlban MainActivity.java az előző leckékből látni fogja, hogy osztályunk Fő tevékenység is vonatkozik Tevékenység(vagy örökösei), pontosabban tőle örököltek.
A MainActivity nyilvános osztály kiterjeszti az AppCompatActivity-t
Ahogy sejtheti, létre kell hoznunk egy új osztályt, ami így nézhet ki Fő tevékenység majd a gomb megnyomásakor valahogy átváltani rá.
A kísérlethez az első lecke programját vesszük, és a kísérletekhez (vagy létrehozáshoz) használjuk a gombot új projekt egy gombbal a képernyőn). Ezután hozzunk létre új forma megjeleníteni hasznos információ... Például mutassuk meg a felhasználónak, mit csinál a macska, amikor jobbra-balra jár. Egyetértek, ez nagyon fontos információ, támpontot adva az univerzumhoz.
Egy új tevékenységet manuálisan fogunk létrehozni, bár a stúdióban vannak kész sablonok. De nincs semmi bonyolult, és a jobb megértés érdekében hasznos mindent kézzel csinálni.
Hozzon létre egy új XML jelölőfájlt activity_about.xml a mappában res / elrendezés... Kattintson jobb gombbal a mappára elrendezésés válassz közülük helyi menü Új | Elrendezési erőforrásfájl... Megjelenik egy párbeszédpanel. Az első mezőbe írja be a fájl nevét tevékenység_körül... A másodikban meg kell adnia a gyökérelemet. Alapértelmezés szerint van ConstraintLayout... Törölje a szöveget és írja be ScrollView... Néhány karakter beírása elegendő ahhoz, hogy a stúdió kész lehetőségeket javasoljon, azonnal nyomja meg az Entert, anélkül, hogy megvárná a szó teljes bevitelét:
Megkapjuk a megfelelő ürességet, amelybe beillesztjük az elemet. TextView.
Az információk forrásokból, nevezetesen egy karakterlánc-erőforrásból kerülnek lekérésre about_text... Most pirossal van kiemelve, jelezve az információhiányt. Nyomhatnál Alt + Enterés írjon be szöveget a párbeszédpanelbe. De a mi példánkban ez a módszer nem fog működni, mivel a szövegünk többsoros lesz, vezérlőkaraktereket használva. Tehát csináljuk másként. Nyissuk meg a fájlt res / értékek / strings.xmlés kézzel írja be a következő szöveget:
A legegyszerűbb HTML szövegformázó címkéket használtuk, mint pl , , ... Példánkhoz elég vastag betűvel kiemelni a macskára és a mozgásirányra utaló szavakat. Szöveg fordításához új sor szimbólumokat használjon \ n... Adjunk hozzá egy másik karakterlánc-erőforrást az új képernyő címéhez:
Kitaláltuk a jelölést. Ezután létre kell hoznia egy osztályt az ablakhoz AboutActivity.java... Válasszon a menüből Fájl | Új | Java osztályés töltse ki a kötelező mezőket. Eleinte elég csak a nevet feltüntetni. Akkor foglalkozz más területekkel.
Vegyünk egy ürességet.
Az osztály már majdnem üres. Adjuk hozzá a kódot manuálisan. Az osztálynak örökölnie kell az absztrakt osztálytól Tevékenység vagy rokonai hasonlók FragmentActivity, AppCompatActivity stb. tesszük hozzá kiterjeszti a tevékenységet... A tevékenységosztálynak rendelkeznie kell metódussal onCreate ()... Az egérkurzort az osztályba helyezzük, és kiválasztjuk a menüből Kód | Módszerek felülbírálása(Ctrl + O). A párbeszédablakban keressük a kívánt osztályt, amelyhez a billentyűzet első karaktereit beírhatja gyors keresés... A létrehozott metódusban meg kell hívni a metódust setContentView () amely betölti az elkészített jelölést a képernyőre. Meg fogjuk kapni ezt a lehetőséget.
ru.alexanderklimov.helloworld csomag; import android.app.Activity; import android.os.Bundle; / ** * Készítette: Alexander Klimov 2014.12.01. * / public class Az AboutActivity kiterjeszti az aktivitást (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_about);))
Most kezdődik a legfontosabb. A feladatunk az, hogy az első képernyő gombjára kattintva új képernyőre lépjünk. Menj vissza az osztályba Fő tevékenység... Írjunk egy gombkattintás-kezelőt:
Public void onClick (nézet nézet) (Intent intent = new Intent (MainActivity.this, AboutActivity.class); startActivity (intent);)
Itt azt a módszert alkalmaztam a gombkattintások kezelésére, amelyet a leckében leírtam.
Új képernyő indításához létre kell hoznia az osztály példányát Elszántés az első paraméterben adja meg az aktuális osztályt, a második paraméterben pedig az átmenet osztályát, ez megvan AboutActivity... Ezt követően a módszer ún tevékenység indítása () amely új képernyőt indít el.
Ha most megpróbálja tesztelni az alkalmazás működését az emulátorban, hibaüzenetet fog kapni. Mit csináltunk rosszul? Kihagytunk egy fontos lépést. Újat kell regisztrálnia Tevékenység a manifesztben AndroidManifest.xml... Keresse meg ezt a fájlt a projektben, és kattintson rá duplán. Megnyílik a fájlszerkesztő ablak. Új címke hozzáadása
Szóval jól jött a karakterlánc erőforrás about_title... Indítsa el az alkalmazást, kattintson a gombra, és nyissa meg az ablakot A programról... Így megtanultuk, hogyan lehet új ablakot létrehozni és egy gombnyomással meghívni. És rendelkezésünkre áll egy nagyon kényelmes program - most mindig kéznél lesz egy tipp, hogy mit csinál a macska, amikor balra megy.
Még egyszer felhívom a figyelmet arra, hogy a második létrehozott tevékenységosztálynak örökölnie kell az osztálytól Tevékenység vagy hozzá hasonló ( ListActivityés mások), rendelkezzen XML-jelölőfájllal (ha szükséges), és szerepeljen a jegyzékben.
A módszer felhívása után tevékenység indítása ()új tevékenység indul (ebben az esetben AboutActivity), láthatóvá válik, és a futó komponenseket tartalmazó verem tetejére kerül. A módszer hívásakor Befejez () egy új tevékenységből (vagy ha egy kemény visszatérési billentyűt lenyomnak) bezárul és kikerül a veremből. A fejlesztő ugyanezzel a módszerrel navigálhat az előző (vagy bármely más) tevékenységhez. tevékenység indítása ().
Hogyan hozzunk létre egy harmadik képernyőt - egy módja a lustáknak
A programozók, akárcsak a macskák, lusta lények. Mindig ne feledje, hogy a tevékenységhez jelölést és egy osztályt kell létrehoznia, amely örököl Tevékenység, majd ne felejtsd el regisztrálni az osztályt a manifestben - hát nafig.
Ebben az esetben válassza ki a menüből Fájl | Új | Tevékenység | Alaptevékenység(vagy más sablon). Ezután megjelenik az ismerős ablak egy új tevékenység létrehozásához. A kötelező mezőket kitöltjük.
Kattintson a gombra Befejezés kész lesz a tevékenység. Ennek ellenőrzéséhez nyissa meg a jegyzékfájlt, és keressen új bejegyzést. Nem az osztály- és jelölőfájlokról beszélek, ezek maguk fognak megjelenni előtted.
Adjon hozzá egy új gombot a fő tevékenységi képernyőhöz, és írja be a kódot a létrehozott tevékenységhez való ugráshoz.
Először azt tanácsolom, hogy manuálisan hozza létre az összes szükséges összetevőt egy új tevékenységhez, hogy megértse az osztály, a jelölés és a jegyzék közötti kapcsolatot. Ha pedig kézbe veszi, a Tevékenység-létrehozó varázsló segítségével felgyorsíthatja munkáját.
Adatátvitel a tevékenységek között
A legegyszerűbb példát használtuk egy másik tevékenység képernyő meghívására. Néha nemcsak új képernyőt kell hívni, hanem adatokat is át kell vinni rá. Például felhasználónév. Ebben az esetben speciális területet kell használnia extraData hogy az osztály rendelkezik Elszánt.
Vidék extraData a párok listája kulcs érték amit a szándékkal együtt adnak át. A karakterláncokat kulcsként használják, és értékekhez bármilyen primitív adattípust, primitív tömböt, osztályobjektumot használhat Csomag satöbbi.
Az adatok másik tevékenységbe való átviteléhez használja a módszert putExtra ():
Intent.putExtra ("Kulcs", "Érték");
A fogadó tevékenységnek valamilyen megfelelő módszert kell hívnia: getIntExtra (), getStringExtra () stb.:
Int count = getIntent (). GetIntExtra ("név", 0);
Ismételjük meg az előző példát. Már három tevékenységünk van. Az első tevékenységnek két szövegmezője és egy gombja lesz. A megjelenés a következő lehet:
A második tevékenység SecondActivity telepítse az elemet TextView, amelyben az első tevékenységből kapott szöveget jelenítjük meg. Írjuk a metódushoz a következő kódot onCreate () a második tevékenységnél.
@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_second); String user = "Élő"; String gift = "fánklyuk"; TextView infoTextView = (TextView) R.ViewById R.View id.textViewInfo); infoTextView.setText (felhasználó + ", kaptál" + ajándék);)
Ha most elindítjuk a programot, és csak a második ablakot hívjuk meg, a cikk első részében leírtak szerint, akkor az alapértelmezett címkét fogjuk látni Zhivotnoe, kaptál egy fánkot... Egyetértek, kár ilyen üzeneteket kapni.
Javítjuk a helyzetet. Adja hozzá az első tevékenység kódját:
Public void onClick (Nézet megtekintése) (EditText userEditText = (EditText) findViewById (R.id.editTextUser); EditText giftEditText = (EditText) findViewById (R.id.editTextGift); /Új Intentity (osztály) / az első szövegmező szövegét benyomni a felhasználónév kulcsába intent.putExtra ("felhasználónév", userEditText.getText (). toString ()); // a szöveget a második szövegmezőből benyomni az ajándékkulcsba intent.putExtra ( "ajándék", ajándékEditText.getText (). toString ()); startActivity (intent);)
Elhelyeztünk egy tárgyat egy speciális tartályba Elszánt két kulcs szövegmezőkből vett értékekkel. Amikor a felhasználó adatokat ír be a szövegmezőkbe, ebbe a tárolóba kerül, és átkerül a második tevékenységbe.
A második tevékenységnek készen kell állnia meleg üzenetek fogadására az alábbiak szerint (félkövéren).
// Alapértelmezett értékek String user = "Élő"; String ajándék = "fánk lyuk"; user = getIntent (). getExtras (). getString ("felhasználónév"); ajándék = getIntent (). getExtras (). getString ("ajándék"); TextView infoTextView = (TextView) findViewById (R.id.textViewInfo); infoTextView.setText (felhasználó + ", kaptál" + ajándék);
Most az üzenet nem tűnik olyan sértőnek, de egyesek számára még kellemes is. Összetett példákban kívánatos az érvényesítés hozzáadása az adatok feldolgozásakor. Vannak helyzetek, amikor a második tevékenységet üres adatokkal kezdi, mint pl nulla ami összeomolhatja az alkalmazást.
Esetünkben tudjuk, hogy egy karakterlánc értékre várunk, így a kód így átírható:
Intent intent = getIntent (); user = intent.getStringExtra ("felhasználónév");
User = getIntent (). GetStringExtra ("felhasználónév");
A programnak van egy hátránya - nem világos, hogy kitől kapunk üdvözletet. Egyetlen jól nevelt majom sem fogad el ajándékot névtelen forrásból. Tehát házi feladatként adjon hozzá egy másik szövegmezőt az üzenetet küldő felhasználó nevének megadásához.
A Google a következő formátum használatát javasolja a kulcsokhoz: a csomag neve előtagként, majd maga a kulcs. Ebben az esetben biztos lehet benne, hogy a kulcs egyedi, amikor más alkalmazásokkal kommunikál. Valami ilyesmi:
Public final static String USER = "ru.alexanderklimov.myapp.USER";
Ki keretezte Vaska macskát – visszakapjuk az eredményt
Nem mindig elegendő egyszerűen átadni az adatokat egy másik tevékenységnek. Néha szükség van arra, hogy információkat szerezzenek vissza egy másik tevékenységből, amikor az le van zárva. Ha korábban használtuk a módszert startActivity (Intent intent), akkor van egy kapcsolódó módszer startActivityForResult (Intent intent, int RequestCode)... A metódusok közötti különbség az extra paraméter RequestCode... Ez alapvetően csak egy egész szám, amelyet magad is kitalálhatsz. Azért van rá szükség, hogy meg lehessen különböztetni, kitől származott az eredmény. Tegyük fel, hogy van öt további képernyője, és 1 és 5 közötti értékeket rendel hozzájuk, és ebből a kódból meghatározhatja, hogy melyik eredményt kell feldolgoznia. Használhatja a -1 értéket, akkor az ugyanaz lesz, mint a metódus meghívása tevékenység indítása (), azaz semmi eredményt nem érünk el.
Ha használja a módszert startActivityForResult (), akkor felül kell írnia a kódban szereplő metódust, hogy megkapja az eredményt onActivityResult ()és feldolgozza az eredményt. Zavaros? Vegyünk egy példát.
Tegyük fel, hogy nyomozó vagy. Információk szerint egy étteremben egy befolyásos személy asztaláról loptak el két darab kolbászt és egyéb termékeket. Három gyanúsítottra esett a gyanú - egy varjúra, egy kibaszott kutyusra és Vaskára a macskára.
Az egyik látogató egy fotósorozatot adott ponty iPhone-járól:
Van egy másik tanú vallomása is: Vaska pedig hallgat, de eszik.
Hozzon létre egy új projektet Sherlock két tevékenységgel. Az első képernyőn lesz egy gomb, amellyel a második képernyőre válthat, és egy szöveges címke, amelyen a tolvaj neve jelenik meg.
A második képernyőn egy rádiógombcsoport található:
Mivel a második képernyőtől várunk választ, a módszert kell használnunk startActivityForResult () az első képernyőn, amelyen átadjuk a változót CHOOSE_THIEF paraméterként RequestCode.
Statikus végső privát int CHOOSE_THIEF = 0; public void onClick (View v) (Intent questionIntent = új szándék (MainActivity.this, ChooseActivity.class); startActivityForResult (questionIntent, CHOOSE_THIEF);)
Vessen egy pillantást a kódra. Ha a gombra kattintunk, a második képernyővel fogunk dolgozni Válassza a Tevékenység lehetőségetés indítsa el a második képernyőt, várja meg az eredményt.
Lépjen a második képernyőre, és írja be a második tevékenység kódját.
Nyilvános végleges statikus String THIEF = "ru.alexanderklimov.sherlock.THIEF"; public void onRadioClick (v. nézet) (Intent answerIntent = új Intent (); kapcsoló (v.getId ()) (R.id.radioDog eset: answerIntent.putExtra (THIEF, "Kibaszott kutyus"); szünet; R.id eset .radioCrow: answerIntent.putExtra (THIEF, "Crow"); break; case R.id.radioCat: answerIntent.putExtra (THIEF, "Przewalski lova"); break; alapértelmezett: break;) setResult (RESULT_OK, answerIntent); befejezni (;)
Itt minden egyszerű, amikor a nyomozó kiválasztja a bűnöző nevét, akkor a módszer segítségével putExtra ()átadjuk a kulcs nevét és értékét.
A kényelem kedvéért a kijelölés után azonnal bezárjuk a második ablakot, és bezárás előtt átadjuk az értéket RESULT_OK egyértelművé tenni, hogy a választás megtörtént. Ha a felhasználó bezárja a képernyőt a Vissza gombbal, az érték átadásra kerül RESULT_CANCELED.
Módszer setResult () két paramétert vesz fel: az eredménykódot és magát az eredményt, amelyet szándékként ábrázolnak. Az eredményül kapott kód megmondja, hogy a tevékenység milyen eredménnyel végződött, általában az is Tevékenység.RESULT_OK vagy Activity.RESULT_CANCELED... Egyes esetekben előfordulhat, hogy saját visszatérési kódját kell használnia az alkalmazásra jellemző változatok kezelésére. Módszer setResult () bármilyen egész értéket támogat.
Ha kifejezetten egy gombon keresztül ad át adatokat, akkor jó lenne hozzáadni egy metódust Befejez () hogy szükségtelenként bezárja a második tevékenységet. Ha az átmenet a Vissza gombon keresztül történik, akkor erre nincs szükség.
Ha a tevékenységet a felhasználó a hardveres visszatérés gomb megnyomásakor zárta le, vagy ha a metódus Befejez () módszer előtt hívták setResult (), az eredményül kapott kód a következőre lesz állítva RESULT_CANCELEDés a visszaadott szándék megmutatja az értéket nulla.
Visszatérünk az első képernyőre. Az első képernyő a második képernyő válaszára vár, ezért hozzá kell adnia a módszert a kódhoz onActivityResult ().
@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) (super.onActivityResult (requestCode, resultCode, data); TextView infoTextView = (TextView) findViewById (R.id.textViewInfo); if (requestCode == CHOOSE) (_T if (resultCode == RESULT_OK) (String thiefname = data.getStringExtra (ChooseActivity.THIEF); infoTextView.setText (thiefname);) else (infoTextView.setText (""); // törli a szöveget)))
A metódus kóddal várja a bejövő adatokat CHOOSE_THIEF, és ha ilyen adat érkezik, akkor kivonja az értéket a kulcsból Válassza az Activity.THIEF lehetőséget módszer segítségével getStringExtra... A kapott értéket adjuk ki TextView(változó infoTextView). Ha a Vissza gombbal visszatértünk a képernyőre, akkor egyszerűen töröljük a szöveget.
Amikor a gyermek tevékenység bezárul a szülőkomponensen belül, a kezelő aktiválódik onActivityResult ()... Kezelő onActivityResult () több paramétert igényel.
- Kérjen kódot. Az eredményt visszaadó tevékenység elindításához használt kód
- Eredmény kódja. A gyermektevékenység által beállított eredménykód, amely jelzi, hogy a tevékenység hogyan végződött. Bármilyen egész érték lehet, de általában bármelyik Tevékenység.RESULT_OK vagy Activity.RESULT_CANCELED
- Adat. A visszaküldött adatok csomagolásának szándéka. Az alárendelt tevékenység céljától függően tartalmazhat egy URI-útvonalat, amely a kiválasztott tartalmat reprezentálja. Alternatív megoldásként (vagy kiegészítésképpen) a gyermek tevékenység egyszerű értékekként is visszaadhat információkat, egy intent paraméterbe csomagolva. extrák
Ha az utódtevékenység váratlanul leállt, vagy ha a bezárás előtt nem adtak meg eredménykódot, akkor ez a paraméter lesz Activity.RESULT_CANCELED.
Elindítjuk a projektet, kattintsunk a gombra, és menjünk a második képernyőre. Ott kiválasztunk egyet a lehetőségek közül. Ha a varjút választja, a képernyő bezárul, és az első képernyőn megjelenik a tettes neve. Ha kiválaszt egy kutyát, megjelenik a neve.
Egyébként ha kiválasztasz egy macskát, a neve nem jelenik meg! Nézze meg, és győződjön meg saját szemével. Megkérdezed, hogy miért? Elemi Watson! Az elkövető egy fontos részletet nem vett figyelembe. Az éttermet videokamerákkal figyelték, és a videón látható volt, hogy valójában ki lopta el a kolbászt és keretezte a macskát. Vaska, tarts ki!
P.S. Ha először valami érthetetlennek tűnt, akkor gyakorlással sok minden kiderül. A képernyők közötti adatátvitel gyakori az alkalmazásokban, és a példát újra és újra tanulmányozni fogja.
P.P.S. A legjobb hal a kolbász. Ismerve ezt a gyengeséget, nem volt nehéz bekeretezni a macskát.
Szűrők használata
A cikkben bemutattam egy gyakori módot arra, hogy a módszerben más tevékenységre váltsunk tevékenység indítása () az aktuális osztály és az átmenet osztálya látható. Egyébként a tevékenységosztálynak nem kell az alkalmazás részének lennie. Ha ismeri az osztály nevét egy másik alkalmazásból, átválthat rá. De egy másik tevékenységre más módon is léphetsz.
A gyakorlatban ritkábban fordul elő, de hasznos lehet. Tegyük fel, hogy már van egy második tevékenysége. Adjon hozzá egy speciális szűrőt a jegyzékben:
A második tevékenységet pedig a gombra kattintva indítjuk el ilyen módon.
Public void onClick (nézet megtekintése) (startActivity (új Intent ("ru.alexanderklimov.testapplication.SecondActivity"));)
Cseréljük le a hosszú karakterláncot konstansra.
Public static final String ACTION_SECOND_ACTIVITY = "ru.alexanderklimov.testapplication.SecondActivity"; public void onClick (Nézet megtekintése) (startActivity (új Intent (ACTION_SECOND_ACTIVITY));)
Szóval mit csináltunk. A második tevékenységhez hozzáadtunk egy szűrőt, és megadtuk a nevét akció attribútumban android: név... A kényelem kedvéért csak a tevékenység teljes nevét írtam bele a csomag nevével együtt. Osztály konstruktor Elszánt több túlterhelt változata van. Az egyik verzióban megadhat egy karakterláncot a művelethez. Létrehozott akciónkat jeleztük, mely a második tevékenységben van nyilvántartva. A rendszer működés közben megnézi az összes telepített alkalmazás jegyzékét. Egyezés keresésekor a rendszer megtalálja a szűrőnket és elindítja a kívánt tevékenységet.
Ugyanezen elv alapján más tevékenységek is indíthatók. Vessen egy pillantást egy példára. Ha lemásolod magadnak a példát, és megnézed a dokumentációt android.provider.Settings.ACTION_AIRPLLANE_MODE_SETTINGS, látni fogja, hogy ez a kód megfelel a karakterlánc állandónak public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLLANE_MODE_SETTINGS"... Hasonlítsa össze a kódunkkal. Feltételezheti, hogy az offline mód beállításainál ez a sor szerepel a szűrőben.
Szűrőkategória neve android.intent.category.DEFAULT utasítja a rendszert, hogy hajtsa végre az alapértelmezett műveletet, azaz indítsa el a tevékenységet. Vannak más nevek, amelyek még nem érdekelnek bennünket.
És most egy kérdés a kitöltéshez. Mi történik, ha létrehoz egy másik tevékenységet, és ugyanazt a szűrőt adja meg, mint a második tevékenység? Nézzük meg. Hozz létre magadban egy harmadik tevékenységet, és másold át oda a blokkot a szűrővel a második tevékenységből.
Kattintson a gombra az első tevékenységnél. A rendszer kérni fogja, hogy válassza ki a kívánt opciót.
Ha kiválasztja az elemet MINDIG, legközelebb nem kell választanod. A kijelölés visszaállításához nyissa meg az alkalmazás tulajdonságait a Beállításokban, és keresse meg a gombot Alapértelmezések törlése.
Tevékenység futtatása a neve alapján
A konstruktorban Elszánt a második paraméter az osztály. De tegyük fel, hogy van valamilyen adatbázis, ahol a tevékenységek nevei megadva vannak, és a szükséges tevékenységet a nevével kell elindítanunk. A karakterlánc változó alapján megkaphatjuk magát az osztályt és elindíthatjuk a tevékenységet.
Próbáld meg (// A tevékenységosztály teljes neve String activityName = "ru.alexanderklimov.testapplication.SecondActivity"; // a Class Class objektum lekérése>myClass = Class.forName (tevékenységnév); Intent intent = new Intent (ez, myClass); startActivity (szándék); ) fogás (ClassNotFoundException e) (e.printStackTrace ();)
Valahogy olyan feladatom volt, hogy adatokat vigyek át a szolgáltatásból a tevékenységbe. Elkezdtek megoldást keresni a szabványos SDK-ban, de mivel nem volt időm, rossz megoldást készítettem adatbázis használat formájában. De a kérdés nyitott volt, és egy idő után rájöttem egy korrektebb módszerre, ami az SDK-ban van - a Message, Handler, Messenger osztályok használatával.
Ötlet
Adatokat kell átvinnünk tevékenységről szolgáltatásra és fordítva. Hogyan csináljuk ezt? Már megvan minden, ami a problémánk megoldásához szükséges. Csak annyit kell tennie, hogy a bindService segítségével köti a szolgáltatást a tevékenységhez, átadja a szükséges paramétereket és egy kis varázslatot a Message osztályok használatával. És a varázslat az üzenetpéldányváltozók és különösen a replyTo használata. Erre a változóra azért van szükségünk, hogy az aktiválástól kezdve hivatkozhassunk a szolgáltatás Messenger-példányára, a szolgáltatásban pedig az aktiválás Messenger-példányára. Valójában ez nem is olyan egyszerű. Legalábbis az én nem a legtehetségesebb elmémnek. Részben csak javítom a már meglévő dokumentációt – a Services-t, valamint a StackOverflow-n is van egy jó példa. Mindenesetre remélem, hogy a cikk legalább valakinek hasznos lesz, és nem dolgoztam hiába.
Példa
Példaként megvalósítunk egy szolgáltatást, amely növeli és csökkenti a számláló értékét, és az eredményt visszaadja a tevékenységnek, a TextView-nak. Az elrendezési kódot elhagyom, mert két gomb és egy szövegmező van - minden egyszerű.
Végrehajtás
Megadom a teljes aktiváló kódot:
Public Class MainActivity kiterjeszti az aktivitást (public static final String TAG = "TestService"; TestServiceConnection testServConn; TextView testTxt; végleges Messenger messenger= új Messenger (új 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 (új osztály, TestState) ), (testServConn = new TestServiceConnection ()), Context.BIND_AUTO_CREATE);) @Override public void onDestroy () (super.onDestroy (); unbindService (testServConn);) public void countIncrClick (View gomb) (Üzenet. msg. get (null, TestService.COUNT_PLUS); msg.replyTo = messenger; try (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) public void countDecrClick (Nézet gomb) (Üzenet üzenet = Message.obtain (null, TestService.COUNT_MINUS); msg.replyTo = messenger; try (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) privát osztály Az IncomingHandler kiterjeszti a kezelőt (@Override public) void handleMessage (Üzenetüzenet) (kapcsoló (msg.what) (case TestServic e.GET_COUNT: Log.d (TAG, "(activity) ... get count"); testTxt.setText ("" + msg.arg1); szünet; ))) privát osztály A TestServiceConnection megvalósítja a ServiceConnection-t (@Override public void onServiceConnected (ComponentName név, IBinder szolgáltatás) (toServiceMessenger = new Messenger (szolgáltatás); // küldés kezdő érték számláló Message msg = Message.obtain (null, TestService.SET_COUNT); msg.replyTo = üzenetküldő; msg.arg1 = 0; // számlálópróbánk (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) @Public void felülbírálása onServiceDisconnected (ComponentName name) ()))
Hadd magyarázzam. Egy tevékenység létrehozásakor azonnal kötődünk a szolgáltatáshoz, implementálva a ServiceConnection felületet és ebben üzenetet küldünk a szolgáltatásnak "állítsd be a számlálóértéket", nullát adva és létrehozva a toServiceMessanger-t, átadva az IBinder felületet a konstruktornak. Egyébként a szolgáltatásnak vissza kell adnia ezt a példányt, különben NPE lesz. Ezt az osztályt használjuk üzenetek küldésére a szolgáltatásnak. És itt a varázslat - a replyTo változóban elmentjük a másik Messenger példányunkat -, amelyik választ kap a szervertől, és ezen keresztül történik a kapcsolattartás a tevékenységgel.
Ahhoz, hogy üzenetet kapjunk a szolgáltatástól, használjuk a Kezelőnket, és egyszerűen megkeressük a szükséges változókat, és műveleteket hajtunk végre rajtuk. A gombokra kattintva (metódusok countIncrClick, countDecrClick) kéréseket küldünk a szolgáltatásnak, az msg.what változóban jelezve a kívánt műveletet.
com.example.servicetest csomag; import android.app.Service; android.content importálása *; import android.os. *; import android.os.Process; import android.util.Log; public class TestService kiterjeszti a szolgáltatást (nyilvános statikus végső int COUNT_PLUS = 1; nyilvános statikus végső int COUNT_MINUS = 2; nyilvános statikus végső int SET_COUNT = 0; nyilvános statikus végső int GET_COUNT = 3; int count = 0; IncomingHandler inHandler; Messenger messenger; Messenger toActivityMessenger; @Override public void onCreate () (super.onCreate (); HandlerThread szál = új HandlerThread ("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start (); inHandler = új IncomingHandler (új szál) (inHandler);) @Public IBinder onBind felülbírálása (Intent arg0) (messanger.getBinder ();) @public int onStartCommand felülbírálása (Intent intent, int flags, int startId) (START_STICKY;) // üzenetkezelő privát osztálya Incommand kiterjeszti Handler (public IncomingHandler (Looper looper) (super (loooper);) @Override public void handleMessage (Message msg) (//super.handleMessage(msg); toActivityMess enger = msg.replyTo; switch (msg.what) (SET_COUNT: count = msg.arg1; Log.d (MainActivity.TAG, "(service) ... set count"); break; case COUNT_PLUS: count ++; Log.d (MainActivity) .TAG , "(szolgáltatás) ... szám plusz"); szünet; COUNT_MINUS eset: Log.d (MainActivity.TAG, "(szolgáltatás) ... szám mínusz"); count--; break;) // küldés a számláló értéke a tevékenységben Message outMsg = Message.obtain (inHandler, GET_COUNT); outMsg.arg1 = szám; outMsg.replyTo = üzenetküldő; try (if (toActivityMessenger! = null) toActivityMessenger.send (outMsg);) catch (RemoteException e) (e.printStackTrace ();))))
Mindezt a tevékenység logikájával analóg módon. Nem is tudom, el kell-e magyaráznom valamit. Az egyetlen dolog, hogy azonnal visszaküldök egy kérést a handleMessage tevékenységéhez, a magic replyTo változó segítségével és fent a kívánt Messengert húzva. És a második pont, amit már említettem:
@A nyilvános IBinder onBind felülbírálása (Intent arg0) (return messanger.getBinder ();)
ami nélkül minden eldől. Az interfésznek ez a példánya lesz átadva a ServiceConnectionnek
Következtetés
Összességében. Ilyen a tevékenységek és szolgáltatások közötti interakció kiagyalt példája. Számomra úgy tűnik, hogy ez egy meglehetősen nem triviális interakció, bár valakinek másnak tűnhet.
Kérdések, pontosítások és egyebek személyesen. Bármely vonatkozásban előfordulhatnak pontatlanságok, ezért nyugodtan írjon és javítson.
Remélem, hogy a bejegyzés hasznos volt az olvasóknak.
Helló.
Az UART-on keresztül kapott adatokat át kell vinni az Activity-be. Ezt úgy teheti meg, hogy létrehoz egy szálat az Activity-ben, amelyben egy time (! IsInterrupted ()) ciklust szervez, és adatokat olvas ki az UART pufferből. Ezt követően az Activity - MainActivity.this.runOnUiThread (új Runnable ()) felhasználói felületének meghívásával hajtsa végre a szükséges műveleteket ezzel a tevékenységgel. De ha a fő tevékenységből hívunk meg más tevékenységeket, akkor a szervezett szál nem teszi lehetővé az átvitelt. adatok az újonnan létrehozott Tevékenységhez.Ha jól értem, hogy ahhoz, hogy a streamből az adatok átkerüljenek bármelyik Tevékenységbe, a streamet nem a Tevékenységben, hanem a Szolgáltatásban kell létrehozni.
Kérdés: UART-on keresztül érkeztek adatok, egy adatfolyamban (ami a Servce-ben jön létre) át kell vinni az adatokat az Activity-be, ami most aktív, hogyan lehet ezt megtenni és egyáltalán megtörténik?
1 válasz
Minden tevékenységben hozzon létre egy kezelőt. Ennek a tevékenységnek az onResume () metódusában a bindService (). Ott az egyik paraméter a ServiceConnection interfész. Végezze el legalább ugyanazzal a tevékenységgel. Valósítsa meg benne az onServiceConnected () metódust. Ebben a visszahívásban maga a Szolgáltatás szerepel az egyik paraméterként. Tehát hívja ezt a szolgáltatást saját setHandler () metódusának. Add át az aktuális tevékenységben lévő Kezelőt. De küldje el a bejövő adatokat az UART-on keresztül a szolgáltatásnak ezen a kezelőn. Egyébként a Handler hagyományosan a fő szálon fut, így nem kell futtatnia az OnUiThread programot a végrehajtáshoz.