Spouštění služeb androidu ve vzdálených procesech

Obecně platí, že všechny komponenty v aplikaci pro Android budou běžet ve stejném procesu. Služby Androidu jsou z této výjimky znatelné v tom, že je možné je nakonfigurovat tak, aby se spouštěl ve vlastních procesech a sdílely s jinými aplikacemi, včetně těch od jiných vývojářů androidu. Tato příručka popisuje, jak vytvořit a používat vzdálenou službu pro Android pomocí Xamarinu.

Přehled služeb mimo proces

Když se aplikace spustí, Android vytvoří proces, ve kterém se aplikace spustí. Obvykle se v tomto procesu spustí všechny komponenty, které bude aplikace spouštět. Služby Androidu jsou z této výjimky znatelné v tom, že je možné je nakonfigurovat tak, aby se spouštěl ve vlastních procesech a sdílely s jinými aplikacemi, včetně těch od jiných vývojářů androidu. Tyto typy služeb se označují jako vzdálené služby nebo služby mimo proces. Kód pro tyto služby bude obsažen ve stejném souboru APK jako hlavní aplikace. Při spuštění služby ale Android vytvoří nový proces pouze pro službu. Naproti tomu služba, která běží ve stejném procesu jako zbytek aplikace, se někdy označuje jako místní služba.

Obecně platí, že aplikace nemusí implementovat vzdálenou službu. Místní služba je v mnoha případech dostatečná (a žádoucí) pro požadavky aplikace. Mimo proces má vlastní paměťový prostor, který musí spravovat Android. Přestože to přináší celkové aplikaci větší režii, existují určité scénáře, ve kterých může být výhodné spustit službu ve vlastním procesu:

  1. Funkce sdílení – někteří vývojáři aplikací mohou mít více aplikací a funkcí, které jsou sdíleny mezi všemi aplikacemi. Zabalení této funkce do služby pro Android, která běží ve vlastním procesu, může zjednodušit údržbu aplikací. Službu je také možné zabalovat do samostatného souboru APK a nasadit ji odděleně od zbytku aplikace.

  2. Vylepšení uživatelského prostředí – Existují dva možné způsoby, jak může mimo procesová služba zlepšit uživatelské prostředí aplikace. První způsob, jak se zabývá s pamětí. Když dojde k cyklu uvolňování paměti, Android pozastaví všechny aktivity v procesu, dokud se uvolňování paměti nedokoní. Uživatel může tuto pauzu vnímat jako "zamířící" nebo "jank". Když je služba spuštěná ve vlastním procesu, je to proces služby, který je pozastavený, nikoli proces aplikace. Toto pozastavení bude pro uživatele mnohem méně patrné, protože proces aplikace (a tedy uživatelské rozhraní) není pozastavený.

    Za druhé, pokud se požadavky na paměť procesu stanou příliš velkými, android může tento proces u označuje zahodit, aby se uchytly prostředky pro zařízení. Pokud má služba velké nároky na paměť a běží ve stejném procesu jako uživatelské rozhraní, pak když Android vynucuje vynucené získání těchto prostředků, uživatelské rozhraní se vypne a vynutí uživateli spuštění aplikace. Pokud je ale služba spuštěná v jejím vlastním procesu vypnutá Androidem, proces uživatelského rozhraní zůstane neovlvlovaný. Uživatelské rozhraní může vytvořit vazbu (a restartovat) službu transparentní pro uživatele a obnovit normální fungování.

  3. Zvýšení výkonu aplikace – Proces uživatelského rozhraní se může ukončit nebo vypnout nezávisle na procesu služby. Přesunutím zdlouhavých úloh po spuštění do služby mimo proces je možné, že se doba spuštění uživatelského rozhraní možná zlepšila (za předpokladu, že proces služby je mezi spuštěním uživatelského rozhraní zachován).

V mnoha ohledech je vazba na službu spuštěnou v jiném procesu stejná jako vazba na místní službu. Klient vyvolá vazbu BindService (a v případě potřeby ji spustí). Vytvoří se objekt pro správu připojení mezi klientem a Android.OS.IServiceConnection službou. Pokud se klient úspěšně sváže se službou, android vrátí objekt prostřednictvím , který lze použít k vyvolání metod IServiceConnection ve službě. Klient pak komunikuje se službou pomocí tohoto objektu. Pokud si to chcete zkontrolovat, tady je postup vytvoření vazby ke službě:

  • Create an Intent (Vytvořit záměr) – k vytvoření vazby na službu je nutné použít explicitní záměr.
  • Implementace a vytvoření instance objektu – Objekt funguje jako prostředník mezi klientem IServiceConnection a službou. Zodpovídá za monitorování připojení mezi klientem a serverem.
  • Vyvolání metody – Volání odešle záměr a připojení služby vytvořené v předchozích krocích do Androidu, který se postará o spuštění služby a navázání komunikace mezi klientem a BindService službou.

Potřeba křížových hranic procesů přináší další složitost: komunikace je jednosměnná (z klienta na server) a klient nemůže přímo vyvolat metody ve třídě služby. Vzpomeňte si, že když je ve službě spuštěný stejný proces jako klient, Android poskytuje objekt, který může umožnovat IBinder dvousměnné komunikace. To ale není případ, kdy služba běží ve svém vlastním procesu. Klient komunikuje se vzdálenou službou pomocí Android.OS.Messenger třídy .

Když klient požádá o vazbu se vzdálenou službou, Android vyvolá metodu životního cyklu, která vrátí interní objekt Service.OnBindIBinder zapouzdřený objektem Messenger . je Messenger tenká obálka přes speciální IBinder implementaci, kterou poskytuje Android SDK. Se Messenger postará o komunikaci mezi těmito dvěma různými procesy. Vývojář není s podrobnostmi serializace zprávy, zařazování zprávy přes hranice procesu a poté deserializaci na straně klienta. Tuto práci zpracovává Messenger objekt . Tento diagram znázorňuje komponenty Androidu na straně klienta, které jsou součástí, když klient iniciuje vazbu na službu mimo proces:

Diagram znázorňuje kroky asoučásti pro vazbu klienta na diagram služby, který znázorňuje kroky a součásti pro

Třída ve vzdáleném procesu bude procházet stejnými zpětnými voláními životního cyklu, přes která bude procházet vázaný služba v místním procesu, a mnoho z těchto rozhraní API Service je stejných. Service.OnCreate slouží k inicializaci objektu a Handler vložení do Messenger objektu . Podobně je OnBind přepsána, ale místo vrácení objektu IBinder služba vrátí Messenger . Tento diagram znázorňuje, co se ve službě stane, když je s ní klient navázání:

Diagram znázorňujekroky a komponenty, které služba prochází, když je svázána se vzdáleným klientským diagramem, který znázorňuje kroky a komponenty, které služba prochází, když je vázána

Když Message je přijat službou, zpracovává ji v instanci Android.OS.Handler . Služba bude implementovat vlastní Handler podtřídu, která musí přepsat HandleMessage metodu . Tato metoda je vyvolána objektem Messenger a přijímá jako parametr Message . Objekt prověří meta data a tyto informace použije Handler k vyvolání metod ve Message službě.

K jednosměnné komunikaci dochází, když klient vytvoří objekt a odešle ho do služby Message pomocí Messenger.Send metody . Messenger.Send serializuje a předá serializovaná data do Androidu, který bude směrovat zprávu přes hranice procesu a Message do služby. Objekt Messenger hostovaný službou vytvoří objekt Message z příchozích dat. Ten Message se umístí do fronty, kde se zprávy posílají do Handler . Handlerobjekt prověří meta data obsažená v a Message vyvolá příslušné metody v Service . Následující diagram znázorňuje tyto různé koncepty v akci:

Diagram znázorňující, jak se zprávy předá mezi procesyDiagram znázorňující,

Tato příručka popisuje podrobnosti implementace služby mimo proces. Probereme, jak implementovat službu, která má běžet ve vlastním procesu, a jak s ní klient může komunikovat pomocí Messenger architektury . Stručně se také probírá dvouseberná komunikace: klient posílá zprávu službě a služba odesílá zprávu zpět klientovi. Vzhledem k tomu, že služby je možné sdílet mezi různými aplikacemi, tato příručka také popisuje jednu techniku omezení přístupu klientů ke službě pomocí oprávnění Androidu.

Důležité

Bugzilla 51940/GitHub 1950 – Službám s izolovaným procesem a vlastní třídou aplikace se nepodaří správně vyřešit přetížení, nahlásí to, že se služba Xamarin.Android nespustí správně, pokud je nastavená na true . Tato příručka je k dispozici pro referenci. Aplikace Xamarin.Android by stále měla být schopná komunikovat s mimo procesovou službou, která je napsaná v Javě.

Požadavky

Tato příručka předpokládá znalost vytváření služeb.

I když je možné používat implicitní záměry s aplikacemi, které cílí na starší rozhraní API Androidu, tato příručka se zaměří výhradně na použití explicitních záměrů. Aplikace, které cílí na Android 5.0 (úroveň rozhraní API 21) nebo vyšší, musí pro vazbu se službou použít explicitní záměr. Toto je technika, kterou si ukážeme v tomto průvodci.

Vytvoření služby, která běží v samostatném procesu

Jak je popsáno výše, skutečnost, že služba běží ve vlastním procesu, znamená, že se zapojuje několik různých rozhraní API. Tady je rychlý přehled kroků pro vytvoření vazby se vzdálenou službou a jejich používání:

  • Vytvořte podtřídu – podtřídu Service typu a implementujte metody životního cyklu pro vázané služby. Je také potřeba nastavit meta data, která budou informovat Android, že služba se má spustit ve vlastním procesu.
  • Implementace – zodpovídá za analýzu požadavků klientů, extrahování všech parametrů, které byly předány z klienta, a vyvolání příslušných metod Handler ve službě.
  • Vytvoření instance – Jak je popsáno výše, každý z nich musí udržovat instanci třídy, která bude směrovat požadavky klientů na objekt , který byl vytvořen ServiceMessenger v předchozím Handler kroku.

Služba, která má běžet ve vlastním procesu, je v podstatě stále vázaným procesem. Třída služby rozšíří základní třídu a je dekorována objektem obsahujícím meta data, která Android potřebuje ServiceServiceAttribute zabalit do manifestu Androidu. Začněte následujícími vlastnostmi objektu , které jsou důležité pro službu ServiceAttribute mimo proces:

  1. Exported – Tato vlastnost musí být nastavená na true , aby ostatní aplikace mohly se službou pracovat. Výchozí hodnota této vlastnosti je false .
  2. Process – Tato vlastnost musí být nastavená. Slouží k zadání názvu procesu, ve které bude služba spuštěna.
  3. IsolatedProcess – Tato vlastnost povolí dodatečné zabezpečení a řekne Androidu, aby službu spouštěl v izolovaném sandboxu s minimálním oprávněním k interakci se zbytkem systému. Viz Bugzilla 51940 –Služby s izolovaným procesem a vlastní třídou aplikace, které správně překládaly přetížení.
  4. Permission – Je možné řídit klientský přístup ke službě zadáním oprávnění, které klienti musí požadovat (a musí jim být uděleno).

Pokud chcete spustit službu vlastním procesem, musí být vlastnost objektu ProcessServiceAttribute nastavená na název služby. Pokud chcete pracovat s externími aplikacemi, Exported měla by být vlastnost nastavená na true . Pokud je , budou moct se službou pracovat pouze klienti ve stejném souboru Exported APK (tj. stejná aplikace) a spuštěný ve stejném false procesu.

Typ procesu, ve které bude služba spuštěna, závisí na hodnotě Process vlastnosti. Android identifikuje tři různé typy procesů:

  • Privátní proces – privátní proces je proces, který je k dispozici pouze pro aplikaci, která ho s začala. Pokud chcete proces identifikovat jako soukromý, musí jeho název začíná na : (středník). Služba znázorněná v předchozím fragmentu kódu a snímek obrazovky je privátní proces. Následující fragment kódu je příkladem ServiceAttribute :

    [Service(Name = "com.xamarin.TimestampService",
             Process=":timestampservice_process",
             Exported=true)]
    
  • Globální proces – služba spuštěná v globálním procesu je přístupná všem aplikacím běžící na zařízení. Globální proces musí být plně kvalifikovaný název třídy, který začíná malými písmeny. (Pokud se pro zabezpečení služby nepokusí podnikat kroky, mohou se s ní jiné aplikace svázat a pracovat s ní. Zabezpečení služby před neoprávněným použitím bude popsáno dále v této příručce.)

    [Service(Name = "com.xamarin.TimestampService",
             Process="com.xamarin.xample.messengerservice.timestampservice_process",
             Exported=true)]
    
  • Izolovaný proces – izolovaný proces je proces, který běží ve vlastním sandboxu, izolovaný od zbytku systému a bez vlastních zvláštních oprávnění. Pokud chcete spustit službu v izolovaném procesu, vlastnost objektu je nastavená IsolatedProcess na , jak je ServiceAttributetrue znázorněno v tomto fragmentu kódu:

    [Service(Name = "com.xamarin.TimestampService",
             IsolatedProcess= true,
             Process="com.xamarin.xample.messengerservice.timestampservice_process",
             Exported=true)]
    

Důležité

Viz Bugzilla 51940 – Služby s izolovaným procesem a vlastní třídou aplikace, které správně nevyřeší přetížení.

Izolovaná služba je jednoduchý způsob, jak zabezpečit aplikaci a zařízení před nedůvěryhodným kódem. Aplikace může například stáhnout a spustit skript z webu. V takovém případě tento postup v izolovaném procesu poskytuje další vrstvu zabezpečení před nedůvěryhodným kódem, který by narušoval zařízení s Androidem.

Důležité

Po exportu služby by se název služby neměl měnit. Změna názvu služby může způsobit přerušení jiných aplikací, které službu používají.

Pokud chcete zobrazit účinek vlastnosti, následující snímek obrazovky ukazuje Process službu spuštěnou ve vlastním privátním procesu:

Snímek obrazovky znázorňuje službu spuštěnou v privátním procesu Snímekobrazovky

Tento další snímek Process="com.xamarin.xample.messengerservice.timestampservice_process" obrazovky ukazuje a službu spuštěnou v globálním procesu:

Snímek obrazovky se službou spuštěnou v globálním procesuSnímek

Po ServiceAttribute nastavení musí služba implementovat Handler .

Implementace obslužné rutiny

Aby služba zpracuje požadavky klientů, musí implementovat a Handler přepsat HandleMessage metodu . Tato metoda přebírá instanci, která zapouzdřuje volání metody z klienta a překládá volání do určité akce nebo úlohy, které Message bude služba provádět. Objekt zpřístupňuje vlastnost s názvem , což je celočíselná hodnota, jehož význam je sdílen mezi klientem a službou a souvisí s nějakým úkolem, který má služba pro klienta MessageWhat provést.

Následující fragment kódu z ukázkové aplikace ukazuje jeden příklad HandleMessage . V tomto příkladu existují dvě akce, které si klient může vyžádat od služby:

  • První akcí je zpráva Hello, World. Klient do služby odeslal jednoduchou zprávu.
  • Druhá akce vyvolá ve službě metodu a načte řetězec. V tomto případě je řetězec zprávou, která vrátí čas spuštění služby a dobu, po kterou je spuštěná:
public class TimestampRequestHandler : Android.OS.Handler
{
    // other code omitted for clarity

    public override void HandleMessage(Message msg)
    {
        int messageType = msg.What;
        Log.Debug(TAG, $"Message type: {messageType}.");

        switch (messageType)
        {
            case Constants.SAY_HELLO_TO_TIMESTAMP_SERVICE:
                // The client has sent a simple Hello, say in the Android Log.
                break;

            case Constants.GET_UTC_TIMESTAMP:
                // Call methods on the service to retrieve a timestamp message.
                break;
            default:
                Log.Warn(TAG, $"Unknown messageType, ignoring the value {messageType}.");
                base.HandleMessage(msg);
                break;
        }
    }
}

Parametry služby je také možné zabalovat do souboru Message . Toto bude probíráno dále v tomto průvodci. Dalším tématem, které je třeba zvážit, je vytvoření Messenger objektu pro zpracování příchozích Message s.

Vytvoření instance pro kaskádu

Jak jsme již uvedli, deserializace objektu a vyvolání je MessageHandler.HandleMessage odpovědností Messenger objektu. Třída Messenger také poskytuje IBinder objekt, který klient použije k odesílání zpráv do služby.

Při spuštění služby se vytvoří instance a vloží MessengerHandler se . Vhodné místo k provedení této inicializace je OnCreate metoda služby. Tento fragment kódu je jedním z příkladů služby, která inicializuje vlastní a HandlerMessenger :

private Messenger messenger; // Instance variable for the Messenger

public override void OnCreate()
{
    base.OnCreate();
    messenger = new Messenger(new TimestampRequestHandler(this));
    Log.Info(TAG, $"TimestampService is running in process id {Android.OS.Process.MyPid()}.");
}

V tomto okamžiku je posledním krokem přepsání Service . OnBind

Implementace Service.OnBind

Všechny vázané služby, ať už běží ve vlastním procesu nebo ne, musí implementovat OnBind metodu . Návratovou hodnotou této metody je nějaký objekt, který může klient použít k interakci se službou. Přesně to, co tento objekt je, závisí na tom, jestli se jedná o místní nebo vzdálenou službu. Zatímco místní služba vrátí vlastní implementaci, vzdálená služba vrátí zapouzdřený objekt , ale objekt , který byl vytvořen IBinderIBinder v předchozí Messenger části:

public override IBinder OnBind(Intent intent)
{
    Log.Debug(TAG, "OnBind");
    return messenger.Binder;
}

Po provedení těchto tří kroků je možné vzdálenou službu považovat za dokončenou.

Využívání služby

Všichni klienti musí implementovat nějaký kód, aby mohli vytvořit vazbu a využívat vzdálenou službu. Z pohledu klienta je koncepčně mezi vazbou na místní službu nebo vzdálenou službou velmi málo rozdílů. Klient vyvolá metodu a předá explicitní záměr identifikovat službu a , který pomáhá spravovat připojení mezi klientem a BindServiceIServiceConnection službou.

Tento fragment kódu je příkladem vytvoření explicitního záměru pro vazbu na vzdálenou službu. Záměr musí identifikovat balíček, který obsahuje službu, a název služby. Jedním ze způsob, jak tyto informace nastavit, je použít objekt a Android.Content.ComponentName poskytnout ho záměru. Tento fragment kódu je jedním z příkladů:

// This is the package name of the APK, set in the Android manifest
const string REMOTE_SERVICE_COMPONENT_NAME = "com.xamarin.TimestampService";
// This is the name of the service, according the value of ServiceAttribute.Name
const string REMOTE_SERVICE_PACKAGE_NAME   = "com.xamarin.xample.messengerservice";

// Provide the package name and the name of the service with a ComponentName object.
ComponentName cn = new ComponentName(REMOTE_SERVICE_PACKAGE_NAME, REMOTE_SERVICE_COMPONENT_NAME);
Intent serviceToStart = new Intent();
serviceToStart.SetComponent(cn);

Když je služba svázaná, IServiceConnection.OnServiceConnected vyvolá se metoda a poskytne IBinder klientovi . Klient však nebude přímo používat IBinder . Místo toho vytvoří instanci objektu Messenger z tohoto IBinder objektu . Toto je Messenger , který klient použije k interakci se vzdálenou službou.

Následuje příklad velmi základní implementace, která ukazuje, jak by klient zvládl připojení ke IServiceConnection službě a odpojení od služby. Všimněte si, že metoda přijme a a klient z této metody OnServiceConnectedIBinder vytvoří MessengerIBinder :

public class TimestampServiceConnection : Java.Lang.Object, IServiceConnection
{
    static readonly string TAG = typeof(TimestampServiceConnection).FullName;

    MainActivity mainActivity;
    Messenger messenger;

    public TimestampServiceConnection(MainActivity activity)
    {
        IsConnected = false;
        mainActivity = activity;
    }

    public bool IsConnected { get; private set; }
    public Messenger Messenger { get; private set; }

    public void OnServiceConnected(ComponentName name, IBinder service)
    {
        Log.Debug(TAG, $"OnServiceConnected {name.ClassName}");

        IsConnected = service != null;
        Messenger = new Messenger(service);

        if (IsConnected)
        {
            // things to do when the connection is successful. perhaps notify the client? enable UI features?
        }
        else
        {
            // things to do when the connection isn't successful.
        }
    }

    public void OnServiceDisconnected(ComponentName name)
    {
        Log.Debug(TAG, $"OnServiceDisconnected {name.ClassName}");
        IsConnected = false;
        Messenger = null;

        // Things to do when the service disconnects. perhaps notify the client? disable UI features?

    }
}

Po vytvoření připojení služby a záměru může klient volat a BindService zahájit proces vazby:

var serviceConnection = new TimestampServiceConnection(this);
BindService(serviceToStart, serviceConnection, Bind.AutoCreate);

Jakmile se klient úspěšně sváže se službou a je k dispozici, je možné, že klient odešle MessengerMessages do služby.

Odesílání zpráv do služby

Jakmile je klient připojený a má objekt , je možné komunikovat se službou odesláním MessengerMessage objektů prostřednictvím Messenger . Tato komunikace je jednosměnná, klient odešle zprávu, ale ze služby klientovi není žádná návratová zpráva. V tomto ohledu se Message jedná o mechanismus "fire-and-forget".

Preferovaným způsobem, jak vytvořit Message objekt, je použít Message.Obtain metodu factory. Tato metoda Message vyžádá objekt z globálního fondu, který udržuje Android. Message.Obtain má také některé přetížené metody, které umožňují inicializovat objekt s hodnotami a Message parametry požadovanými službou. Po vytvoření instance se odešle do služby Message voláním Messenger.Send . Tento fragment kódu je jedním z příkladů vytvoření a odeslání do Message procesu služby:

Message msg = Message.Obtain(null, Constants.SAY_HELLO_TO_TIMESTAMP_SERVICE);
try
{
    serviceConnection.Messenger.Send(msg);
}
catch (RemoteException ex)
{
    Log.Error(TAG, ex, "There was a error trying to send the message.");
}

Metoda má několik různých Message.Obtain forem. Předchozí příklad používá Message.Obtain(Handler h, Int32 what) . Vzhledem k tomu, že se jedná o asynchronní požadavek na mimo procesovou službu; Služba nebude reagovat, takže je Handler nastavená na null . Druhý parametr Int32 what , bude uložen ve .What vlastnosti objektu Message . Vlastnost .What je používána kódem v procesu služby k vyvolání metod ve službě.

Třída Message také zpřístupňuje další dvě vlastnosti, které může být pro příjemce možné použít: a Arg1Arg2 . Tyto dvě vlastnosti jsou celočíselné hodnoty, které mohou mít určité speciální odsouhlasené hodnoty, které mají význam mezi klientem a službou. Může například obsahovat ID zákazníka a může obsahovat číslo Arg1 nákupní objednávky pro tohoto Arg2 zákazníka. Lze Method.Obtain(Handler h, Int32 what, Int32 arg1, Int32 arg2) použít k nastavení dvou vlastností při vytvoření Message . Dalším způsobem, jak naplnit tyto dvě hodnoty, je nastavit vlastnosti a přímo na .Arg.Arg2 objekt po jeho Message vytvoření.

Předávání dalších hodnot službě

Složitější data je možné službě předat pomocí Bundle . V takovém případě lze do objektu umístit dodatečné hodnoty a odeslat spolu s parametrem nastavením BundleMessage vlastnosti Bundle před odesláním.

Bundle serviceParameters = new Bundle();
serviceParameters.

var msg = Message.Obtain(null, Constants.SERVICE_TASK_TO_PERFORM);
msg.Data = serviceParameters;

messenger.Send(msg);

Poznámka

Obecně platí, že by datová část neměla být Message větší než 1 MB. Omezení velikosti se může lišit v závislosti na verzi Androidu a u všech proprietárních změn, které dodavatel mohl provést v implementaci Open Source Project (AOSP) Androidu, který je součástí zařízení.

Vracení hodnot ze služby

Architektura zasílání zpráv, která byla probírána v tomto okamžiku, je jednosměnná a klient odešle zprávu do služby. Pokud je nezbytné, aby služba vrátila hodnotu klientovi, pak se vše, co bylo probíráno v tomto okamžiku, vrátí zpět. Služba musí vytvořit Message , zabalí všechny návratové hodnoty a odešle Message do klienta přes Messenger . Služba ale nevytváří vlastní ; místo toho spoléhá na vytvoření instance klienta a zabalení jako součást MessengerMessenger počátečního požadavku. Služba zobrazí Send zprávu pomocí tohoto klienta Messenger .

Pořadí událostí pro dvousekvenční komunikaci je toto:

  1. Klient se váže ke službě. Když se služba a klient připojí, bude IServiceConnection obsahovat odkaz na Messenger objekt, který se používá k přenosu Message služby s. Aby nedocházelo k omylům, bude se tato služba označovat jako Kurýrní služba.
  2. Klient vytvoří instanci Handler (označovanou jako Handler) a použije ji k inicializaci vlastního Messenger ( Messenger). Všimněte si, že Kurýrní služba a klient služby Messenger jsou dva různé objekty, které zpracovávají provoz ve dvou různých směrech. Kurýrní služba zpracovává zprávy od klienta ke službě, zatímco Kurýrní služba klienta bude zpracovávat zprávy ze služby klientovi.
  3. Klient vytvoří Message objekt a nastaví ReplyTo vlastnost pomocí kurýrní služby klienta. Zpráva se pak pošle službě pomocí kurýrní služby.
  4. Služba obdrží zprávu od klienta a provede požadovanou práci.
  5. Když je čas, kdy služba odesílá odpověď klientovi, použije Message.Obtain k vytvoření nového Message objektu.
  6. Chcete-li odeslat tuto zprávu klientovi, služba vyextrahuje klienta kurýrní služby z .ReplyTo vlastnosti zprávy klienta a použije ji pro .SendMessage zpátky do klienta.
  7. Když klient obdrží odpověď, má svoji vlastní zprávu Handler , která zpracuje Message kontrolu .What vlastnosti (a v případě potřeby také extrakci parametrů obsažených v rámci Message ).

Tento ukázkový kód ukazuje, jak bude klient vytvářet instance Message a balíčku Messenger , které by měla služba použít pro svou odpověď:

Handler clientHandler = new ActivityHandler();
Messenger clientMessenger = new Messenger(activityHandler);

Message msg = Message.Obtain(null, Constants.GET_UTC_TIMESTAMP);
msg.ReplyTo = clientMessenger;

try
{
    serviceConnection.Messenger.Send(msg);
}
catch (RemoteException ex)
{
    Log.Error(TAG, ex, "There was a problem sending the message.");
}

Služba musí udělat nějaké změny vlastní Handler pro extrakci Messenger a použití, které slouží k odesílání odpovědí klientovi. Tento fragment kódu je příkladem toho, jak by služba Handler vytvořila Message a odeslala ji zpátky klientovi:

// This is the message that the service will send to the client.
Message responseMessage = Message.Obtain(null, Constants.RESPONSE_TO_SERVICE);
Bundle dataToReturn = new Bundle();
dataToReturn.PutString(Constants.RESPONSE_MESSAGE_KEY, "This is the result from the service.");
responseMessage.Data = dataToReturn;

// The msg object here is the message that was received by the service. The service will not instantiate a client,
// It will use the client that is encapsulated by the message from the client.
Messenger clientMessenger = msg.ReplyTo;
if (clientMessenger!= null)
{
    try
    {
        clientMessenger.Send(responseMessage);
    }
    catch (Exception ex)
    {
        Log.Error(TAG, ex, "There was a problem sending the message.");
    }
}

Všimněte si, že ve výše uvedených vzorcích kódu Messenger instance, která je vytvořena klientem, Messenger stejný objekt, který je službou přijat. Jedná se o dva různé Messenger objekty, které jsou spuštěny ve dvou samostatných procesech, které představují komunikační kanál.

Zabezpečení služby pomocí oprávnění pro Android

Služba, která běží v globálním procesu, je přístupná pro všechny aplikace běžící na zařízení s Androidem. V některých situacích je tato otevřená a dostupnost nežádoucí a je nutné zabezpečit službu proti přístupu z neautorizovaných klientů. Jedním ze způsobů, jak omezit přístup ke vzdálené službě, je použít oprávnění pro Android.

Oprávnění lze identifikovat Permission vlastností ServiceAttribute , která upraví Service dílčí třídu. Tím se pojmenuje oprávnění, které musí klient udělit při vytváření vazby na službu. Pokud klient nemá příslušná oprávnění, systém Android vyvolá výjimku, Java.Lang.SecurityException když se klient pokusí vytvořit službu.

Android nabízí čtyři různé úrovně oprávnění:

  • Normal (normální ) – Toto je výchozí úroveň oprávnění. Slouží k identifikaci méně rizikové oprávnění, která může Android automaticky udělit klientům, kteří si je vyžádají. Uživatel nebude muset tato oprávnění výslovně udělit, ale oprávnění lze zobrazit v nastavení aplikace.
  • Signatura – jedná se o speciální kategorii oprávnění, která se automaticky udělí Androidem pro aplikace, které jsou všechny podepsané se stejným certifikátem. Toto oprávnění je určené k tomu, aby vývojář aplikace mohl snadno sdílet součásti nebo data mezi aplikacemi, aniž by přestane uživatele pro konstantní schvalování.
  • signatureOrSystem – to je velmi podobné oprávnění podpisů popsaných výše. Kromě automatického udělení pro aplikace, které jsou podepsané stejným certifikátem, se toto oprávnění udělí taky aplikacím, které mají podepsaný stejný certifikát, který se použil k podepsání aplikací nainstalovaných s imagí systému Android. Toto oprávnění se obvykle používá jenom vývojáři pro Android ROM, aby mohly své aplikace pracovat s aplikacemi třetích stran. Běžně se nepoužívá v aplikacích, které jsou určeny pro širokou distribuci veřejnosti.
  • nebezpečná – nebezpečná oprávnění jsou ta, která by mohla způsobit problémy uživatele. Z tohoto důvodu musí být nebezpečná oprávnění explicitně schválena uživatelem.

Vzhledem k tomu, signaturenormal že jsou oprávnění automaticky udělena v době instalace Androidu, je důležité, aby se APK hostující služba nainstalovala signature APK obsahující klienta. Pokud je klient nástroje nainstalován jako první, zařízení s Androidem tato oprávnění nebudou udělena. V takovém případě bude nutné odinstalovat klienta APK, nainstalovat APK služby a pak znovu nainstalovat klienta APK.

Existují dva běžné způsoby, jak zabezpečit službu pomocí oprávnění Androidu:

  1. Implementace zabezpečení na úrovni podpisů – zabezpečení na úrovni podpisů znamená, že k aplikacím, které jsou podepsané stejným klíčem, který se použil k podepsání služby APK, která je držitelem, se automaticky udělí oprávnění. Toto je jednoduchý způsob, jak můžou vývojáři zabezpečit svou službu, ale budou je mít k dispozici z vlastních aplikací. Oprávnění na úrovni podpisu jsou deklarována nastavením Permission vlastnosti na ServiceAttributesignature :

    [Service(Name = "com.xamarin.TimestampService",
             Process="com.xamarin.TimestampService.timestampservice_process",
             Permission="signature")]
    public class TimestampService : Service
    {
    }
    
  2. Vytvoření vlastního oprávnění – vývojář služby může vytvořit vlastní oprávnění ke službě. To je vhodné, když chce vývojář sdílet svoji službu s aplikacemi od jiných vývojářů. Vlastní oprávnění vyžaduje pro implementaci trochu větší úsilí a bude se pokrýt níže.

Zjednodušený příklad vytvoření vlastního normal oprávnění bude popsán v následující části. Další informace o oprávněních pro Android najdete v dokumentaci ke službě Google, kde najdete osvědčené postupy zabezpečení. Další informace o oprávněních pro Android najdete v části oprávnění v dokumentaci k Androidu pro manifest aplikace, kde najdete další informace o oprávněních pro Android.

Poznámka

Obecně platí, že Google nedoporučuje používat vlastní oprávnění , protože se jim můžou odmítají uživatelé.

Vytvoření vlastního oprávnění

Chcete-li použít vlastní oprávnění, je deklarována službou, zatímco klient výslovně požaduje toto oprávnění.

Chcete-li vytvořit oprávnění ve službě APK, permission prvek je přidán do manifest prvku v permission. Toto oprávnění musí mít nameprotectionLevellabel nastavené atributy, a. nameAtribut musí být nastaven na řetězec, který jednoznačně identifikuje oprávnění. název se zobrazí v zobrazení informací o aplikaciNastavení androidu (jak je znázorněno v další části).

protectionLevelAtribut musí být nastaven na jednu ze čtyř hodnot řetězce, které byly popsány výše. labelA description musí odkazovat na řetězcové prostředky a slouží k poskytnutí uživatelsky přívětivého názvu a popisu uživateli.

Tento fragment kódu je příkladem deklarace vlastního permission atributu v permission APK, který obsahuje službu:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          android:versionCode="1"
          android:versionName="1.0"
          package="com.xamarin.xample.messengerservice">

    <uses-sdk android:minSdkVersion="21" />

    <permission android:name="com.xamarin.xample.messengerservice.REQUEST_TIMESTAMP"
                android:protectionLevel="signature"
                android:label="@string/permission_label"
                android:description="@string/permission_description"
                />

    <application android:allowBackup="true"
            android:icon="@mipmap/icon"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">

    </application>
</manifest>

Pak AndroidManifest.xml APK klienta musí explicitně požádat o toto nové oprávnění. To se provádí přidáním users-permission atributu do users-permission:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          android:versionCode="1"
          android:versionName="1.0"
          package="com.xamarin.xample.messengerclient">

    <uses-sdk android:minSdkVersion="21" />

    <uses-permission android:name="com.xamarin.xample.messengerservice.REQUEST_TIMESTAMP" />

    <application
            android:allowBackup="true"
            android:icon="@mipmap/icon"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">
    </application>
    </manifest>

Zobrazit oprávnění udělená aplikaci

chcete-li zobrazit oprávnění udělená aplikaci, otevřete aplikaci pro Android Nastavení a vyberte aplikace. Vyhledejte a vyberte aplikaci v seznamu. Na obrazovce informace o aplikaci klepněte na oprávnění , která zobrazí zobrazení obsahující všechna oprávnění udělená aplikaci:

Snímky obrazovky ze zařízení s Androidem ukazující, jak najít oprávnění udělená aplikaci

Souhrn

Tato příručka je pokročilá diskuze o tom, jak spustit službu pro Android ve vzdáleném procesu. Rozdíly mezi místními a vzdálenými službami byly vysvětlené a s některými důvody, proč může být Vzdálená služba užitečná pro stabilitu a výkon aplikace pro Android. Po objasnění postupu implementace vzdálené služby a způsobu, jakým může klient komunikovat se službou, se zobrazí průvodce, který poskytuje jeden ze způsobů, jak omezit přístup ke službě jenom z autorizovaných klientů.