添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

通常,Android 应用程序中的所有组件都将在同一进程中运行。 Android 服务是一个值得注意的例外,因为它们可以配置为在自己的进程中运行,并与其他应用程序(包括来自其他 Android 开发人员的应用程序)共享。 本指南将讨论如何使用 Xamarin 创建和使用 Android 远程服务。

进程外服务概述

应用程序启动时,Android 会创建运行应用程序的进程。 通常,应用程序将在此进程中运行的所有组件。 Android 服务是一个值得注意的例外,因为它们可以配置为在自己的进程中运行,并与其他应用程序(包括来自其他 Android 开发人员的应用程序)共享。 这些类型的服务称为 远程服务 进程外服务 。 这些服务的代码将包含在与 main 应用程序相同的 APK 中;但是,启动该服务时,Android 将为该服务创建一个新进程。 相比之下,在与应用程序的其余部分相同的进程中运行的服务有时称为 本地服务

通常,应用程序不需要实现远程服务。 在许多情况下,本地服务足以满足应用的 (和理想) 。 进程外具有自己的内存空间,必须由 Android 管理。 尽管这确实给整个应用程序带来了更多的开销,但在某些情况下,在其自身的进程中运行服务可能很有利:

  • 共享功能 - 某些应用程序开发人员可能具有多个应用和功能,这些应用和功能在所有应用程序之间共享。 在自己的进程中运行的 Android 服务中打包该功能可能会简化应用程序维护。 还可以将服务打包到其自己的独立 APK 中,并将其与应用程序的其余部分分开部署。

  • 改善用户体验 – 进程外服务可通过两种可能的方法改善应用程序的用户体验。 第一种方法处理内存管理。 当垃圾回收 (GC) 周期发生时,Android 将暂停过程中的所有活动,直到 GC 完成。 用户可能会将此暂停视为“断断续续”或“卡顿”。 当服务在自己的进程中运行时,暂停的是服务进程,而不是应用程序进程。 由于应用程序进程 (,因此用户界面) 不会暂停,因此,此暂停对用户不太明显。

    其次,如果进程的内存要求太大,Android 可能会终止该进程,以释放设备的资源。 如果服务占用大量内存,并且它与 UI 在同一进程中运行,则当 Android 强行回收这些资源时,UI 将关闭,强制用户启动应用。 但是,如果 Android 关闭在其自己的进程中运行的某个服务,则 UI 进程将不受影响。 UI 可以绑定 (并重启) 服务,对用户透明,并恢复正常运行。

  • 提高应用程序性能 - UI 进程可以独立于服务进程终止或关闭。 通过将冗长的启动任务移动到进程外服务,UI 的启动时间可能会有所改善, (假设服务进程在 ui 启动) 之间保持活动状态。

    在许多方面,绑定到在另一个进程中运行的服务与 绑定到本地服务 相同。 如有必要,客户端将调用 BindService 以绑定 (并启动) 服务。 将创建一个 Android.OS.IServiceConnection 对象来管理客户端与服务之间的连接。 如果客户端成功绑定到服务,则 Android 将通过 可用于在服务上调用方法的 返回对象 IServiceConnection 。 然后,客户端使用此对象与服务交互。 若要查看,下面是绑定到服务的步骤:

  • 创建意向 – 必须使用显式意向绑定到服务。
  • 实现和实例化 IServiceConnection 对象 – 对象 IServiceConnection 充当客户端和服务之间的中介。 它负责监视客户端和服务器之间的连接。
  • BindService 调用 方法 - 调用 BindService 会将在前面步骤中创建的意向和服务连接调度到 Android,这将负责启动服务并在客户端和服务之间建立通信。
  • 跨进程边界的需求确实带来了额外的复杂性: (客户端到服务器) 通信是单向的,客户端无法直接调用服务类上的方法。 回想一下,当服务运行与客户端相同的进程时,Android 会提供一个 IBinder 对象,该对象可能允许双向通信。 服务在其自己的进程中运行的情况并非如此。 客户端在 类的帮助下 Android.OS.Messenger 与远程服务通信。

    当客户端请求与远程服务绑定时,Android 将调用 Service.OnBind 生命周期方法,该方法将返回由 Messenger 封装的内部 IBinder 对象。 Messenger 是 Android SDK 提供的特殊 IBinder 实现的精简包装器。 负责 Messenger 两个不同进程之间的通信。 开发人员不关心序列化消息、跨进程边界封送消息,然后在客户端上反序列化它的详细信息。 此工作由 Messenger 对象处理。 此图显示了客户端启动绑定到进程外服务时涉及的客户端 Android 组件:

    Service 远程进程中的 类将经历与本地进程中绑定服务将经历的相同生命周期回调,并且所涉及的许多 API 都是相同的。 Service.OnCreate 用于初始化 并将其 Handler 注入 Messenger 对象。 同样, OnBind 被重写,但服务不会返回 IBinder 对象,而是返回 Messenger 。 此图演示了客户端绑定到服务时服务中发生的情况:

    Message 当服务收到 时,它由 在 实例 Android.OS.Handler 中进行处理。 服务将实现其自己的 Handler 子类,这些子类必须重写 HandleMessage 方法。 此方法由 Messenger 调用,并接收 Message 作为参数。 将 Handler 检查 Message 元数据,并使用该信息调用服务上的方法。

    当客户端创建 Message 对象并使用 方法将其调度到服务 Messenger.Send 时,会发生单向通信。 Messenger.Send 将序列化 Message 并将序列化的数据交给 Android,Android 将消息跨进程边界路由到服务。 Messenger 由服务托管的 将从传入数据创建 Message 对象。 这会 Message 放入队列中,其中消息一次提交一个到 Handler 。 将 Handler 检查 中包含的 Message 元数据,并在 上 Service 调用相应的方法。 下图演示了这些不同概念的实践:

    本指南将讨论实现进程外服务的详细信息。 本文将讨论如何实现旨在在其自己的进程中运行的服务,以及客户端如何使用框架与该服务 Messenger 通信。 它还将简要讨论双向通信:客户端向服务发送消息,以及将消息发送回客户端的服务。 由于服务可以在不同的应用程序之间共享,因此本指南还将讨论一种使用 Android 权限限制客户端访问服务的方法。

    Bugzilla 51940/GitHub 1950 - 具有独立进程和自定义应用程序类的服务无法正确解决重载 ,报告 Xamarin.Android 服务在 设置为 true IsolatedProcess 无法正常启动。 本指南仅供参考。 Xamarin.Android 应用程序仍应能够与用 Java 编写的进程外服务进行通信。

    本指南假定你熟悉创建服务。

    尽管可以将隐式意向用于面向较旧 Android API 的应用,但本指南将专门介绍显式意向的使用。 面向 Android 5.0 (API 级别 21) 或更高版本的应用必须使用显式意向来与服务绑定;这是本指南中将演示的技术。

    创建在单独的进程中运行的服务

    如上所述,服务在其自己的进程中运行意味着涉及一些不同的 API。 作为快速概述,下面是使用远程服务绑定和使用的步骤:

  • Service 创建子类 – 对类型进行子类化 Service ,并实现绑定服务的生命周期方法。 还需要设置元数据,以通知 Android 服务将在自己的进程中运行。
  • Handler 实现 Handler – 负责分析客户端请求,提取从客户端传递的任何参数,并在服务上调用适当的方法。
  • 实例化 Messenger – 如上所述,每个 Service 都必须维护 类的实例, Messenger 该实例会将客户端请求路由到 Handler 上一步中创建的 。
  • 基本上,打算在其自己的进程中运行的服务仍然是一个绑定服务。 服务类将扩展基 Service 类,并使用 ServiceAttribute 包含 Android 需要捆绑在 Android 清单中的元数据的 修饰。 首先,以下属性 ServiceAttribute 对进程外服务很重要:

  • Exported – 此属性必须设置为 true 以允许其他应用程序与服务交互。 此属性的默认值为 false
  • Process – 必须设置此属性。 它用于指定运行服务的进程的名称。
  • IsolatedProcess – 此属性将启用额外的安全性,告知 Android 在独立沙盒中运行服务,并且具有与系统其余部分交互的最小权限。 请参阅 Bugzilla 51940 - 具有独立进程和自定义应用程序类的服务无法正确解决重载
  • Permission – 可以通过指定客户端必须请求 (并授予) 的权限来控制客户端对服务的访问权限。
  • 若要运行服务自己的进程, Process 必须将 上的 ServiceAttribute 属性设置为服务的名称。 若要与外部应用程序交互,应将 Exported 属性设置为 true 。 如果 Exported false ,则只有同一 APK (即同一应用程序) 且在同一进程中运行的客户端才能与服务交互。

    服务将在哪种进程中运行取决于 属性的值 Process 。 Android 标识三种不同类型的进程:

  • 专用进程 – 专用进程是仅对启动它的应用程序可用的进程。 若要将进程标识为私有进程,其名称必须以 (分号) 开头。 前面的代码片段和屏幕截图中描述的服务是一个专用进程。 以下代码片段是 的 ServiceAttribute 一个示例:

    [Service(Name = "com.xamarin.TimestampService",
             Process=":timestampservice_process",
             Exported=true)]
    
  • 全局进程 - 在全局进程中运行的服务可供设备上运行的所有应用程序访问。 全局进程必须是以小写字符开头的完全限定类名。 (除非采取步骤来保护服务,否则其他应用程序可能会绑定该服务并与之交互。本指南稍后将讨论保护服务免受未经授权的使用。)

    [Service(Name = "com.xamarin.TimestampService",
             Process="com.xamarin.xample.messengerservice.timestampservice_process",
             Exported=true)]
    
  • 独立进程 – 独立进程是在其自己的沙盒中运行的进程,与系统的其余部分隔离,并且没有自身的特殊权限。 若要在隔离进程中运行服务, IsolatedProcess 请将 ServiceAttribute 的 属性设置为 true ,如以下代码片段所示:

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

    请参阅 Bugzilla 51940 - 具有独立进程和自定义应用程序类的服务无法正确解决重载

    隔离服务是一种保护应用程序和设备免受不受信任的代码的简单方法。 例如,应用可以从网站下载并执行脚本。 在这种情况下,在独立进程中执行此操作可提供额外的安全层,防止不受信任的代码破坏 Android 设备。

    导出服务后,不应更改服务的名称。 更改服务的名称可能会中断使用该服务的其他应用程序。

    若要查看 属性的效果 Process ,以下屏幕截图显示了在其自己的专用进程中运行的服务:

    下一个屏幕截图显示了 Process="com.xamarin.xample.messengerservice.timestampservice_process" 在全局进程中运行的服务:

    ServiceAttribute设置 后,服务需要实现 Handler

    实现处理程序

    若要处理客户端请求,服务必须实现 Handler 并重写 HandleMessage 方法。 此方法采用一个 Message 实例,该实例封装来自客户端的方法调用,并将该调用转换为服务将执行的某个操作或任务。 对象 Message 公开一个名为 What 的属性,该属性是一个整数值,其含义在客户端和服务之间共享,并且与服务要为客户端执行的某个任务相关。

    示例应用程序中的以下代码片段显示了 的 HandleMessage一个示例。 在此示例中,客户端可以请求服务的两个操作:

  • 第一个操作是 Hello, World 消息,客户端已向服务发送了一条简单消息。
  • 第二个操作将对服务调用方法并检索字符串,在本例中,字符串是一条消息,返回服务的启动时间和运行时间:
  • 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;
    

    还可以在 中 Message打包服务的参数。 本指南稍后将对此进行讨论。 下一个要考虑的主题是创建 Messenger 对象来处理传入 Message的 。

    实例化 Messenger

    如前所述,反序列化 Message 对象和调用 Handler.HandleMessage 是 对象的责任 Messenger 。 类 Messenger 还提供了一个 IBinder 对象,客户端将使用该对象向服务发送消息。

    服务启动时,它将实例化 Messenger 并注入 Handler。 执行此初始化的一个好位置是在服务的 方法上 OnCreate 。 此代码片段是初始化其自己的 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()}.");
    

    此时,最后一步是让 Service 替代 OnBind

    实现 Service.OnBind

    所有绑定服务(无论它们是否在自己的进程中运行)都必须实现 OnBind 方法。 此方法的返回值是客户端可用于与服务交互的某个对象。 该对象的确切内容取决于服务是本地服务还是远程服务。 虽然本地服务将返回自定义 IBinder 实现,但远程服务将返回 IBinder 封装但 Messenger 已在上一部分中创建的 :

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

    完成这三个步骤后,可将远程服务视为已完成。

    所有客户端都必须实现某些代码才能绑定和使用远程服务。 从概念上讲,从客户端的角度来看,绑定到本地服务或远程服务之间的差异很小。 客户端调用 BindService 方法,传递用于标识服务的显式意向,以及 IServiceConnection 帮助管理客户端与服务之间的连接的 。

    此代码片段是有关如何创建显式 意向 以绑定到远程服务的示例。 意向必须标识包含服务和服务名称的包。 设置此信息的一种方法是使用 Android.Content.ComponentName 对象,并将该对象提供给意向。 此代码片段是一个示例:

    // 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);
    

    绑定服务时, IServiceConnection.OnServiceConnected 将调用 方法,并为客户端提供 IBinder 。 但是,客户端不会直接使用 IBinder。 相反,它将从该 IBinder实例化 Messenger 对象。 这是 Messenger 客户端将用于与远程服务交互的 。

    下面是一个非常基本的 IServiceConnection 实现示例,该实现演示了客户端如何处理连接到服务以及从服务断开连接。 请注意, OnServiceConnected 方法接收 和 IBinder,客户端 Messenger 从中创建 IBinder

    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?
                // 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?
    

    创建服务连接和意向后,客户端可以调用 BindService 并启动绑定过程:

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

    在客户端成功绑定到服务且 Messenger 可用后,客户端可以发送到 Messages 服务。

    向服务发送消息

    客户端连接并具有 Messenger 对象后,可以通过 调度 Message 对象 Messenger来与服务通信。 此通信是单向的,客户端发送消息,但没有从服务返回到客户端的消息。 在这方面, Message 是一种火而忘记的机制。

    创建 Message 对象的首选方法是使用 Message.Obtain 工厂方法。 此方法将从 Android 维护的全局池中拉取 Message 对象。 Message.Obtain 还具有一些重载的方法,这些方法允许 Message 使用服务所需的值和参数初始化对象。 Message实例化 后,它通过调用 Messenger.Send调度到服务。 此代码片段是创建 并将 调度到服务进程的一个 Message 示例:

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

    方法有几种不同形式 Message.Obtain 。 前面的示例使用 Message.Obtain(Handler h, Int32 what)。 因为这是对进程外服务的异步请求;服务不会有任何响应,因此 将 Handler 设置为 null。 第二个Int32 what参数 将存储在 对象的 属性中.WhatMessage。 服务 .What 进程中的代码使用 属性来调用服务上的方法。

    Message 还公开了两个可能对收件人使用的附加属性: Arg1Arg2。 这两个属性是整数值,它们可能具有一些在客户端和服务之间具有含义的特殊约定值。 例如, Arg1 可以保存客户 ID,也可以 Arg2 保存该客户的采购订单编号。 Method.Obtain(Handler h, Int32 what, Int32 arg1, Int32 arg2)创建 时Message,可以使用 设置这两个属性。 填充这两个值的另一种方法是在创建对象后直接在对象上Message设置 .Arg.Arg2 属性。

    将其他值传递给服务

    可以使用 将更复杂的数据传递给服务 Bundle。 在这种情况下,可以在发送之前设置.Data属性属性,将额外的值放置在 中Bundle,并与 一起Message发送。

    Bundle serviceParameters = new Bundle();
    serviceParameters.
    var msg = Message.Obtain(null, Constants.SERVICE_TASK_TO_PERFORM);
    msg.Data = serviceParameters;
    messenger.Send(msg);
    

    通常, Message 的有效负载不应大于 1MB。 大小限制可能因 Android 版本以及供应商对 Android 开源项目的实现所做的任何专有更改而有所不同, (与设备捆绑的 AOSP) 。

    从服务返回值

    此时讨论的消息体系结构是单向的,客户端会向服务发送消息。 如果服务需要将值返回给客户端,则此时讨论的所有内容将反转。 服务必须创建 ,Message打包任何返回值,并通过 Messenger 将 调度Message到客户端。 但是,服务不会创建自己的 Messenger;而是依赖于客户端实例化并将 打包为初始请求的一 Messenger 部分。 服务将使用 Send 客户端提供的 Messenger此 消息。

    双向通信的事件序列如下所示:

  • 客户端绑定到服务。 当服务和客户端连接时, IServiceConnection 由客户端维护的 将具有对 Messenger 对象(用于将 传输到 Message服务)的 引用。 为避免混淆,这称为 Service Messenger
  • 客户端实例化Handler称为客户端处理程序) 的 (,并使用该 (客户端 Messenger) 初始化自己的Messenger。 请注意,Service Messenger 和 Client Messenger 是两个不同的对象,它们处理两个不同方向的流量。 Service Messenger 处理从客户端到服务的消息,而客户端 Messenger 将处理从服务到客户端的消息。
  • 客户端创建 一个 Message 对象,并使用 Client Messenger 设置 ReplyTo 属性。 然后使用 Service Messenger 将消息发送到服务。
  • 服务从客户端接收消息,并执行请求的工作。
  • 当服务将响应发送到客户端时,它将使用 Message.Obtain 创建新的 Message 对象。
  • 若要将此消息发送到客户端,服务将从客户端消息的 属性中提取 Client Messenger.ReplyTo,并将该消息用回.SendMessage客户端。
  • 当客户端收到响应时,它有自己的Handler响应,它将通过检查.What属性 (处理 Message ,并在必要时提取) 包含Message的任何参数。
  • 此示例代码演示客户端将如何实例化 Message 并打包 Messenger 服务应用于其响应的 :

    Handler clientHandler = new ActivityHandler();
    Messenger clientMessenger = new Messenger(activityHandler);
    Message msg = Message.Obtain(null, Constants.GET_UTC_TIMESTAMP);
    msg.ReplyTo = clientMessenger;
        serviceConnection.Messenger.Send(msg);
    catch (RemoteException ex)
        Log.Error(TAG, ex, "There was a problem sending the message.");
    

    服务必须对其自己的 Handler 进行一些更改才能提取 Messenger ,并使用它来向客户端发送答复。 此代码片段是服务的 Handler 如何创建 Message 并将其发送回客户端的示例:

    // 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)
            clientMessenger.Send(responseMessage);
        catch (Exception ex)
            Log.Error(TAG, ex, "There was a problem sending the message.");
    

    请注意,在上面的代码示例中 Messenger ,客户端创建的实例与服务接收的对象 不同 。 这是在表示信道的两个单独进程中运行的两个不同的 Messenger 对象。

    使用 Android 权限保护服务

    在该 Android 设备上运行的所有应用程序都可以访问在全局进程中运行的服务。 在某些情况下,这种开放性和可用性是不可取的,因此有必要保护服务免受未经授权的客户端的访问。 限制对远程服务的访问的一种方法是使用 Android 权限。

    权限可以通过修饰Service子类的 的 ServiceAttribute 属性来标识Permission。 这将命名在绑定到服务时必须授予客户端的权限。 如果客户端没有适当的权限,则当客户端尝试绑定到服务时,Android 将引发 Java.Lang.SecurityException

    Android 提供四种不同的权限级别:

  • normal - 这是默认权限级别。 它用于标识 Android 可以自动授予请求它的客户端的低风险权限。 用户不必显式授予这些权限,但可以在应用设置中查看这些权限。
  • 签名 – 这是一个特殊类别的权限,Android 将自动授予所有使用同一证书签名的应用程序。 此权限旨在使应用程序开发人员能够轻松地在其应用之间共享组件或数据,而无需用户进行持续审批。
  • signatureOrSystem – 这与上述 签名 权限非常相似。 除了自动授予由同一证书签名的应用之外,此权限还将授予对与 Android 系统映像一起安装的应用进行签名的相同证书的应用。 此权限通常仅由 Android ROM 开发人员用来允许其应用程序与第三方应用一起使用。 它通常不由面向广大公众的应用使用。
  • 危险 – 危险权限是可能导致用户出现问题的权限。 因此,用户必须显式批准 危险 权限。
  • 由于 signaturenormal 权限在安装时由 Android 自动授予,因此在包含客户端的 APK 之前 安装承载服务的 APK 至关重要。 如果首先安装客户端,则 Android 不会授予权限。 在这种情况下,需要卸载客户端 APK,安装服务 APK,然后重新安装客户端 APK。

    可通过两种常见方法保护具有 Android 权限的服务:

  • 实现签名级别安全性 – 签名级别安全性意味着会自动向那些使用用于对持有服务的 APK 进行签名的密钥签名的应用程序授予权限。 对于开发人员来说,这是一种保护其服务并使其可从自己的应用程序访问的简单方法。 通过将 的 ServiceAttribute 属性设置为 Permissionsignature来声明签名级别权限:

    [Service(Name = "com.xamarin.TimestampService",
             Process="com.xamarin.TimestampService.timestampservice_process",
             Permission="signature")]
    public class TimestampService : Service
    
  • 创建自定义权限 – 服务的开发人员可以创建服务的自定义权限。 这最适合开发人员想要与其他开发人员的应用程序共享其服务时。 自定义权限需要付出更多努力才能实现,下面将介绍。

    下一部分将介绍创建自定义 normal 权限的简化示例。 有关 Android 权限的详细信息,请参阅 Google 的最佳做法 & 安全文档。 有关 Android 权限的详细信息,请参阅应用程序清单的 Android 文档的权限 部分 ,了解有关 Android 权限的详细信息。

    一般情况下, Google 不建议使用自定义权限 ,因为它们可能会让用户感到困惑。

    创建自定义权限

    若要使用自定义权限,服务在客户端显式请求该权限时声明该权限。

    若要在服务 APK 中创建权限,请将 元素 permission 添加到 manifestAndroidManifest.xml中的 元素。 此权限必须设置 nameprotectionLevellabel 属性。 属性 name 必须设置为唯一标识权限的字符串。 该名称将显示在 Android 设置 (的“应用信息”视图中,如) 的下一部分所示。

    属性 protectionLevel 必须设置为上述四个字符串值之一。 labeldescription 必须引用字符串资源,并用于向用户提供用户友好名称和说明。

    此代码片段是一个在包含服务的 APK AndroidManifest.xml中声明自定义permission属性的示例:

    <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>
    

    然后,客户端 APK 的AndroidManifest.xml 必须显式请求此新权限。 这是通过将 属性添加到 users-permissionAndroidManifest.xml来完成的

    <?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>
    

    查看授予应用的权限

    若要查看应用程序已授予的权限,请打开“Android 设置”应用,然后选择“ 应用”。 在列表中查找并选择应用程序。 在 “应用信息 ”屏幕中,点击 “权限” ,显示授予应用的所有权限的视图:

    本指南是有关如何在远程进程中运行 Android 服务的高级讨论。 介绍了本地服务与远程服务之间的差异,以及远程服务有助于 Android 应用的稳定性和性能的一些原因。 在介绍了如何实现远程服务以及客户端如何与服务通信之后,本指南继续提供了一种方法来限制仅从授权客户端访问服务。

  • Messenger
  • ServiceAttribute
  • Exported 属性
  • 具有独立进程和自定义 Application 类的服务无法正确解决重载
  • 进程和线程
  • Android 清单 - 权限
  • MessengerServiceDemo (示例)
  •