在插件中使用服务的方法及装置
技术领域
本发明涉及插件技术领域,特别是一种在插件中使用服务的方法及装置。
背景技术
在软件开发中,为了方便对软件进行功能扩展,可以采用插件方式进行开发,将软件所要实现的一个一个功能封装在插件中,最后将各个插件集成在软件主程序包中一同发布。在实际开发中,经常会遇到使用插件服务的情况,例如,在手机卫士上需要使用Backup插件的BackupService服务。
现有技术一中,是通过在主程序(宿主)中编写一个“Service中介”来实现。这个Service中介可以直接通过反射调用插件来做。具体做法为:首先在宿主的AndroidManifest.xml中预埋此Service(如编写<service android:name=“BackupService”>),其次在宿主中编写BackupService,但所有的方法,如onCreate、onStartCommand等都通过Java反射来调用插件内部的实现(如在Backup插件中有个BackupServiceImpl类,也有onCreate方法,这时会在BackupService.onCreate中反射调用BackupServiceImpl.onCreate)。然而,现有技术一虽然实现起来很快,但最大的缺点就是,要增加Service就必须先升级宿主才可以。例如,若将来要添加BackupService2,则同样的,需要在宿主的AndroidManifest.xml中写上BackupService2,且只能在升级宿主后才能使用,显然这种做法非常不完美。
现有技术二中,是通过在宿主中预埋一些Service坑位来实现。现有技术二有效解决了现有技术一的问题,使其可以在不升级宿主的情况下,任意删改插件服务。但现有技术二也有一个比较明显的缺陷,就是坑位有限。Activity的生命周期远短于Service,换言之,即便只给每个不同的模式的Activity留3个坑位,只要页面安排得当,也是完全可以使用的。但Service是要常驻于后台的,生命周期会非常长,若只留少量的坑位,则很快坑位就会满,但坑位过多(如预留20个)又会导致启动速度受到影响,且第三方或自己通过Binder获取宿主应用的信息时,有可能因信息过多而出现TransactionTooLarge的异常,因此,该方案也不够完美。
综上所述,发明人发现亟待提供一种有效地在插件中使用服务的方案。
发明内容
鉴于上述问题,提出了本发明以便提供一种克服上述问题或者至少部分地解决上述问题的在插件中使用服务的方法及相应的装置。
依据本发明的一方面,提供了一种在插件中使用服务的方法,包括:
定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类;
当使用方需要使用目标插件服务时,调用所述客户端类中的第一指定类成员,由所述第一指定类成员获取所述目标插件服务的管理器的接口;
利用所述目标插件服务的管理器的接口实现所述服务端类,进而由所述服务端类向使用方提供所述目标插件服务。
可选地,对插件服务进行的操作包括下列至少之一:
启动插件服务、停止插件服务、绑定插件服务、解除绑定插件服务。
可选地,在由所述第一指定类成员获取所述目标插件服务的管理器的接口之前,所述方法还包括:
调用所述客户端类中的第二指定类成员,由所述第二指定类成员获取所述目标插件服务所在的目标进程。
可选地,由所述第二指定类成员获取所述目标插件服务所在的目标进程,包括:
由所述第二指定类成员获取所述目标插件服务的ServiceInfo对象;
利用所述目标插件服务的ServiceInfo对象,获取所述目标插件服务的ProcessName属性;
根据所述目标插件服务的ProcessName属性,获取所述目标插件服务所在的目标进程。
可选地,所述目标插件服务的ProcessName属性包括目标进程的索引值,根据所述目标插件服务的ProcessName属性,获取所述目标插件服务所在的目标进程,包括:
获取预先定义的进程索引,其中,所述进程索引包括进程的索引值及对应的进程;
在所述进程索引中查找所述目标进程的索引值对应的目标进程。
可选地,由所述第一指定类成员获取所述目标插件服务的管理器的接口,包括:
由所述第一指定类成员基于所述目标插件服务所在的目标进程,获取所述目标插件服务的管理器的接口。
可选地,所述目标插件服务所在的目标进程在启动后,构建所述目标插件服务的管理器的接口,并缓存至指定对象;
由所述第一指定类成员基于所述目标插件服务所在的目标进程,获取所述目标插件服务的管理器的接口,包括:
由所述第一指定类成员,调用缓存的所述指定对象来获取所述目标插件服务所在的目标进程;
从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口。
可选地,由所述第一指定类成员基于所述目标插件服务所在的目标进程,获取所述目标插件服务的管理器的接口,包括:
判断所述目标插件服务所在的目标进程是否存在;
若是,则直接从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口;
若否,则启动所述目标插件服务所在的目标进程,并构建所述目标插件服务的管理器的接口,进而获取构建的所述目标插件服务的管理器的接口。
可选地,若所述目标插件服务所在的目标进程为常驻进程,从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口,包括:
调用PluginProcessMain.getPluginHost()方法来获取IPluginHost对象;
调用IPluginHost.fetchServiceServer()方法来获取所述目标插件服务的管理器的接口。
可选地,由所述服务端类向使用方提供所述目标插件服务,包括:
所述客户端类所在客户端进程与所述服务端类所在目标进程进行通信,向使用方提供所述目标插件服务。
可选地,所述客户端类所在客户端进程与所述服务端类所在目标进程进行通信,向使用方提供所述目标插件服务,包括:
所述客户端类所在客户端进程利用BinderProxy对象,通过所述第一指定类成员与所述服务端类所在目标进程的BinderStub进行通信;
BinderStub在收到消息后,将所述消息转发给所述服务端类的Stub中,进而所述服务端类的Stub将所述消息转发给所述服务端类;
所述服务端类基于所述消息向使用方提供所述目标插件服务。
可选地,所述客户端类为PluginServiceClient类,所述服务端类为PluginServiceServer类,所述目标插件服务的管理器的接口为IPluginServiceServer接口。
可选地,所述IPluginServiceServer接口为安卓接口定义语言AIDL接口。
可选地,所述目标插件服务所在的目标进程在启动后,构建所述目标插件服务的管理器的接口,并缓存至指定对象,所述方法还包括:
检测所述目标插件服务所在的目标进程是否被终止;
若检测到所述目标插件服务所在的目标进程被终止,则调用缓存的所述指定对象来清除所述目标插件服务所在的目标进程。
依据本发明的另一方面,还提供了一种在插件中使用服务的装置,包括:
定义模块,适于定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类;
获取模块,适于当使用方需要使用目标插件服务时,调用所述客户端类中的第一指定类成员,由所述第一指定类成员获取所述目标插件服务的管理器的接口;
提供模块,适于利用所述目标插件服务的管理器的接口实现所述服务端类,进而由所述服务端类向使用方提供所述目标插件服务。
可选地,对插件服务进行的操作包括下列至少之一:
启动插件服务、停止插件服务、绑定插件服务、解除绑定插件服务。
可选地,所述获取模块还适于:
在由所述第一指定类成员获取所述目标插件服务的管理器的接口之前,调用所述客户端类中的第二指定类成员,由所述第二指定类成员获取所述目标插件服务所在的目标进程。
可选地,所述获取模块还适于:
由所述第二指定类成员获取所述目标插件服务的ServiceInfo对象;
利用所述目标插件服务的ServiceInfo对象,获取所述目标插件服务的ProcessName属性;
根据所述目标插件服务的ProcessName属性,获取所述目标插件服务所在的目标进程。
可选地,所述目标插件服务的ProcessName属性包括目标进程的索引值,所述获取模块还适于:
获取预先定义的进程索引,其中,所述进程索引包括进程的索引值及对应的进程;
在所述进程索引中查找所述目标进程的索引值对应的目标进程。
可选地,所述获取模块还适于:
由所述第一指定类成员基于所述目标插件服务所在的目标进程,获取所述目标插件服务的管理器的接口。
可选地,所述目标插件服务所在的目标进程在启动后,构建所述目标插件服务的管理器的接口,并缓存至指定对象;
所述获取模块还适于:
由所述第一指定类成员,调用缓存的所述指定对象来获取所述目标插件服务所在的目标进程;
从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口。
可选地,所述获取模块还适于:
判断所述目标插件服务所在的目标进程是否存在;
若是,则直接从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口;
若否,则启动所述目标插件服务所在的目标进程,并构建所述目标插件服务的管理器的接口,进而获取构建的所述目标插件服务的管理器的接口。
可选地,若所述目标插件服务所在的目标进程为常驻进程,所述获取模块还适于:
调用PluginProcessMain.getPluginHost()方法来获取IPluginHost对象;
调用IPluginHost.fetchServiceServer()方法来获取所述目标插件服务的管理器的接口。
可选地,所述提供模块还适于:
所述客户端类所在客户端进程与所述服务端类所在目标进程进行通信,向使用方提供所述目标插件服务。
可选地,所述提供模块还适于:
所述客户端类所在客户端进程利用BinderProxy对象,通过所述第一指定类成员与所述服务端类所在目标进程的BinderStub进行通信;
BinderStub在收到消息后,将所述消息转发给所述服务端类的Stub中,进而所述服务端类的Stub将所述消息转发给所述服务端类;
所述服务端类基于所述消息向使用方提供所述目标插件服务。
可选地,所述客户端类为PluginServiceClient类,所述服务端类为PluginServiceServer类,所述目标插件服务的管理器的接口为IPluginServiceServer接口。
可选地,所述IPluginServiceServer接口为安卓接口定义语言AIDL接口。
可选地,所述目标插件服务所在的目标进程在启动后,构建所述目标插件服务的管理器的接口,并缓存至指定对象,所述装置还包括:
检测模块,适于检测所述目标插件服务所在的目标进程是否被终止;
清除模块,适于若所述检测模块检测到所述目标插件服务所在的目标进程被终止,则调用缓存的所述指定对象来清除所述目标插件服务所在的目标进程。
本发明实施例设计了一种类似C/S架构,插件服务的使用方是客户端,插件服务的提供方是服务端,每个服务端所处的进程中都有一个对插件服务进行管理的管理器,利用该管理器对应的服务端类对插件服务进行管理。即,定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类,当使用方需要使用目标插件服务时,调用客户端类中的第一指定类成员,由第一指定类成员获取目标插件服务的管理器的接口,利用目标插件服务的管理器的接口来实现服务端类,进而由服务端类向使用方提供目标插件服务。可见,本发明实施例既可以不升级宿主程序,又无需在宿主的配置文件AndroidManifest.xml中增加服务的坑位,即可有效地实现插件服务的管理。
上述说明仅是本发明技术方案的概述,为了能够更清楚了解本发明的技术手段,而可依照说明书的内容予以实施,并且为了让本发明的上述和其它目的、特征和优点能够更明显易懂,以下特举本发明的具体实施方式。
根据下文结合附图对本发明具体实施例的详细描述,本领域技术人员将会更加明了本发明的上述以及其他目的、优点和特征。
附图说明
通过阅读下文优选实施方式的详细描述,各种其他的优点和益处对于本领域普通技术人员将变得清楚明了。附图仅用于示出优选实施方式的目的,而并不认为是对本发明的限制。而且在整个附图中,用相同的参考符号表示相同的部件。在附图中:
图1示出了根据本发明一实施例的在插件中使用服务的方法的流程图;
图2示出了根据本发明一实施例的类和进程的关系图;
图3示出了根据本发明一个实施例的在插件中使用服务的装置的结构示意图;以及
图4示出了根据本发明另一个实施例的在插件中使用服务的装置的结构示意图。
具体实施方式
下面将参照附图更详细地描述本公开的示例性实施例。虽然附图中显示了本公开的示例性实施例,然而应当理解,可以以各种形式实现本公开而不应被这里阐述的实施例所限制。相反,提供这些实施例是为了能够更透彻地理解本公开,并且能够将本公开的范围完整的传达给本领域的技术人员。
为解决上述技术问题,本发明实施例提供了一种在插件中使用服务的方法。图1示出了根据本发明一实施例的在插件中使用服务的方法的流程图。如图1所示,该方法至少可以包括以下步骤S102至步骤S106。
步骤S102,定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类。
步骤S104,当使用方需要使用目标插件服务时,调用客户端类中的第一指定类成员,由第一指定类成员获取目标插件服务的管理器的接口。
步骤S106,利用目标插件服务的管理器的接口实现服务端类,进而由服务端类向使用方提供目标插件服务。
本发明实施例设计了一种类似C/S架构,插件服务的使用方是客户端,插件服务的提供方是服务端,每个服务端所处的进程中都有一个对插件服务进行管理的管理器,利用该管理器对应的服务端类对插件服务进行管理。即,定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类,当使用方需要使用目标插件服务时,调用客户端类中的第一指定类成员,由第一指定类成员获取目标插件服务的管理器的接口,利用目标插件服务的管理器的接口来实现服务端类,进而由服务端类向使用方提供目标插件服务。可见,本发明实施例既可以不升级宿主程序,又无需在宿主的配置文件AndroidManifest.xml中增加服务的坑位,即可有效地实现插件服务的管理。
在本发明实施例中,插件服务的使用方可以是任意插件,插件服务的提供方提供的插件服务可以是备份数据、骚扰拦截、查杀修复、电脑清理、优化加速、宽带测速器、断网急救箱、免费wifi、DNS(Domain Name System,域名***)优选、流量防火墙、网游加速器、游戏保险箱、文件粉碎机、***急救箱,等等,本发明不限于此。
上文步骤S102中,对插件服务进行的操作可以包括启动插件服务、停止插件服务、绑定插件服务、解除绑定插件服务等等,本发明不限于此。例如,定义的客户端类为PluginServiceClient类,进一步定义该类中的方法可以有startService(开启插件服务)、stopService(停止插件服务)、bindService(绑定插件服务)、unbindService(解除绑定插件服务)、stopSelf(在插件服务中停止服务,该方法应在插件服务中被调用)、getProcessByComponentName(内部方法,通过ComponentName来获取插件信息,进而最终获取要加载服务的进程信息),等等。在本发明的可选实施例中,上述方法可以是静态方法,调用语法为“类名.静态成员名”,即直接通过类名加“.”号来访问。
此外,步骤S102中定义的服务端类可以为PluginServiceServer类,进一步定义该类中的方法可以有startService(开启插件服务)、stopService(停止插件服务)、bindService(绑定插件服务)、unbindService(解除绑定插件服务),等等。
上文步骤S104中,当使用方需要使用目标插件服务时,调用客户端类中的第一指定类成员,该第一指定类成员是客户端类中的静态变量,可以直接通过类名加“.”号来访问。静态变量在客户端类中进行了声明,在客户端类外进行定义分配内存。例如,在客户端类PluginServiceClient类中,第一指定类成员为PluginServiceServerFetcher,通过PluginServiceServerFetcher来获取目标插件服务的管理器的接口,即IPluginServiceServer接口。在本发明的可选实施例中,该IPluginServiceServer接口可以为AIDL(Android Interface Definition Language,安卓接口定义语言)接口。
在本发明的可选实施例中,在步骤S104由第一指定类成员获取目标插件服务的管理器的接口之前,调用客户端类中的第二指定类成员,由第二指定类成员获取目标插件服务所在的目标进程。这里的第二指定类成员可以如前文提及的getProcessByComponentName方法。
在由第二指定类成员获取目标插件服务所在的目标进程时,本发明提供了一种可选的方案,即,首先由第二指定类成员获取目标插件服务的ServiceInfo对象;随后利用目标插件服务的ServiceInfo对象,获取目标插件服务的ProcessName属性;进而根据目标插件服务的ProcessName属性,获取目标插件服务所在的目标进程。这里需要获取的目标插件服务的ServiceInfo对象是Android(安卓)***自带的,其是用来描述Service在Android***的配置文件AndroidManifest.xml中的信息。ServiceInfo对象的获取方式可以有多种,例如,在ComponentList中包含有插件的所有组件的列表,则可以通过调用Factory.queryPluginComponentList方法来获取该插件的所有组件的列表,再通过getService方法来获取这个插件内指定服务的ServiceInfo对象。又例如,可以自行解析插件里的PackageInfo,并找到其中的ServiceInfo对象。
在本发明的可选实施例中,目标插件服务的ProcessName属性包括目标进程的索引值,则根据目标插件服务的ProcessName属性,获取目标插件服务所在的目标进程,本发明实施例提供了一种可选的方案,在该方案中,可以获取预先定义的进程索引,其中,该进程索引包括进程的索引值及对应的进程;进而在进程索引中查找目标进程的索引值对应的目标进程。例如,为了便于判断,定义进程索引,可以有1、2、3之类的数字,如1表示UI进程,2表示常驻进程,以此类推。
在获取了目标插件服务所在的目标进程之后,本发明实施例可以由第一指定类成员基于目标插件服务所在的目标进程,获取目标插件服务的管理器的接口。即,上文例举的在客户端类PluginServiceClient类中,第一指定类成员为PluginServiceServerFetcher,通过PluginServiceServerFetcher来获取目标插件服务的管理器的接口,即IPluginServiceServer接口。这里可以通过多种方式来实现,例如从缓存中获取,创建或获取IPluginServiceServer对象并缓存,等等,下面将分别进行详细介绍。
方式一,从缓存中获取。
首先,目标插件服务所在的目标进程在启动后,会构建目标插件服务的管理器的接口,并缓存至指定对象,如mServiceManagerByProcessMap对象。因此,本发明实施例会从缓存中先尝试获取IPluginServiceServer对象,如前所述,每个服务进程都会有一个对应的PluginServiceServer对象,自然要通过IPC(Inter-Process Communication,进程间通信)来获取此对象,就必须要获取它的AIDL的接口,也即IPluginServiceServer接口对象。具体地,由第一指定类成员,调用缓存的指定对象来获取目标插件服务所在的目标进程,进而从目标插件服务所在的目标进程获取目标插件服务的管理器的接口。
方式二,创建或获取IPluginServiceServer对象并缓存。
若缓存中无此对象,则意味着需要创建或获取它。这里说的创建是指当目标进程不存在时,需要启动目标进程,并要求目标进程来构建出IPluginServiceServer的实现对象。而获取是指若目标进程存在,则直接从目标进程获取该对象即可,并将其缓存到mServiceManagerByProcessMap对象中,以备将来使用。具体地,首先判断目标插件服务所在的目标进程是否存在,若存在,则直接从目标插件服务所在的目标进程获取目标插件服务的管理器的接口;若不存在,则启动目标插件服务所在的目标进程,并构建目标插件服务的管理器的接口,进而获取构建的目标插件服务的管理器的接口。
在本发明的可选实施例中,若目标插件服务所在的目标进程为常驻进程,则可以通过调用PluginProcessMain.getPluginHost()方法来获取IPluginHost对象(它是常驻进程与其它进程通信中,最为核心的接口对象);进而通过调用IPluginHost.fetchServiceServer()方法来获取目标插件服务的管理器的接口。
本发明实施例中,无论采用上述何种方式来获取,都必须先唤醒目标进程,再通过ContentProvider和MatrixCursor来获取接口对象。
一旦调用过程建立起来了,客户端可以将所需要的信息传到服务端了,服务端里面就会做各种各样的处理。即,上文步骤S106中由服务端类向使用方提供目标插件服务,可以由客户端类所在客户端进程与服务端类所在目标进程进行通信,向使用方提供目标插件服务。
客户端类所在客户端进程与服务端类所在目标进程进行通信,向使用方提供目标插件服务,本发明实施例提供了一种可选的方案,在该方案中,客户端类所在客户端进程利用BinderProxy对象,通过第一指定类成员与服务端类所在目标进程的BinderStub进行通信;BinderStub在收到消息后,将该消息转发给服务端类的Stub中,进而服务端类的Stub将该消息转发给服务端类;从而服务端类基于该消息向使用方提供目标插件服务。
在本发明的可选实施例中,假若插件服务所在的进程被意外杀死,则若不及时从缓存中移除,那么下回再从缓存中获取到的IPluginServiceServer对象是不能使用的,调用其中的任何方法都会出现DeadObjectException异常。因此,本发明实施例提供了“插件生命周期”的处理。具体地,检测目标插件服务所在的目标进程是否被终止;若检测到目标插件服务所在的目标进程被终止,则调用缓存的指定对象来清除目标插件服务所在的目标进程;若未检测到目标插件服务所在的目标进程被终止,则继续进行检测。例如,可以定义binderDied是插件生命周期的核心方法,一旦目标进程被终止,则会立即回调给使用方,这里的做法很简单,只需将缓存清除即可。
以上介绍了图1所示实施例的各种环节的多种实施方式,下面通过一具体实施例来详细介绍本发明的在插件中使用服务的方法的实现过程。
本发明实施例设计了一种类似C/S架构,插件服务的使用方是客户端,插件服务的提供方是服务端,每个服务端所处的进程中都有一个对插件服务进行管理的管理器,利用该管理器对应的服务端类对插件服务进行管理,能够实现有效地使用插件服务,无需占坑,无需额外处理,像使用Android***服务那样去做。
首先来介绍本发明实施例与Android服务的不同之处,具体可以分为以下三个方面。
1)总统领与分管领导
Android服务的“总统领”是一个名叫“ActivityManageService”来实施的。所有服务,无论创建、管理、释放、注册、解除,还是传递AIDL等,都必须由这个服务来处理。
而本发明实施例出于对性能、设计上的考虑,不设计类似Android服务的“ActivityManageService”这样的“总统领”,而是设计一种类似“C/S”架构,服务的使用方(即调用方)是客户端,服务的提供方(即实施方)是服务端。每个服务端所处的进程都有一个“分管领导”服务“PluginServiceServer”,用来管理该进程的服务。
2)进程和加载时机的区别
Android服务的“总统领”存在于***中的唯一进程“system_server”,且随***所在终端(如手机等)启动时而被加载。
本发明实施例的“分管领导”们的加载时机,是在当前服务所在进程被加载时才会加载,如果当前服务没有被加载,则该进程的“分管领导”服务就不会再出现,可节省一定的性能和内存消耗。
3)ActivityManagerService和PluginServiceServer的职责和影响范围
Android的ActivityManagerService,其逻辑极为复杂,因为它需要管理“整个Android***”内的Activity、Service、ContentProvider、BroadcastReceiver、Application、Instrumentation、Process等等的信息。一旦该服务出现问题,则整个手机将会被重启(这也是很多手机莫名其妙自动重启的原因之一)。
PluginServiceServer的职责相对简单,只管理“当前服务进程”的Service和Process信息,逻辑清晰明了。若该服务出现问题,则受影响的仅仅是“当前服务进程”和使用它的进程,其它服务不受影响,最大限度的减少损失。
其次,为了方便说明,可以规定以下几种术语。
(1)插件服务
表示一个插件的Service,在Android平台上,用来表示一个“后台服务”,其对应的Java类为Service。
(2)使用插件服务
表示将要使用此Service。目前Android常见的使用方法有:启动服务(StartService)、停止服务(Stop Service)、绑定服务(Bind Service)和解除绑定服务(UnbindService)。
(3)插件服务所在进程
表示要使用的插件服务,应该存在于哪个进程。通常会在Android***配置文件AndroidManifest.xml中的<Service>标签中的android:process中表示。
(4)进程索引(可选项)
为了便于判断,可以定义“进程索引”,可以有1、2、3之类的数字。如在手机卫士而言,1表示UI进程,2表示常驻进程,以此类推。在实际应用中,本发明实施例也可以不用“进程索引”,依然可以实现。若不需要“进程索引”的地方可直接用“进程名”或者“进程PID(标识符)”代替。
(5)插件服务的客户端
使用插件的一方可以称之为处于插件服务的“客户端”。例如,清理插件要调用备份插件中的备份服务来做“清理前备份”功能,则这里“清理插件”这一方称为“客户端”。
(6)插件服务的服务端
被调度的一方可以称为处于插件服务的“服务端”。例如,清理插件要调用备份插件中的备份服务来做“清理前备份”功能,则这里“备份服务”一方则称为“服务端”。
(7)插件服务的管理器
每一个被调度一方的进程,都会存在一个“插件服务管理器”,也就是前文所介绍的“分管领导”,在本发明实施例中,可以将其自定义为“PluginServiceServer”类,进一步定义该类中的方法可以有startService(开启插件服务)、stopService(停止插件服务)、bindService(绑定插件服务)、unbindService(解除绑定插件服务),等等。
接下来,可以定义用于插件服务的使用方对插件服务进行操作的客户端类为PluginServiceClient类。此类是“插件服务的客户端”要用到的核心类,它是一种能够对插件的服务进行启动、停止、绑定、解绑等功能的类。所有针对插件命令的操作,均从此类开始。
startService,开启指定插件的服务,用法上近似于Context.startService方法;
stopService,停止指定插件的服务,用法上近似于Context.stopService;
bindService,绑定插件服务,获取其AIDL,用法上近似于Context.bindService;
unbindService,解除对插件服务的绑定,用法上近似于Context.unbindService;
stopSelf,在插件服务中停止服务,用法上近似于Service.stopSelf,此方法应在插件服务中被调用;
getProcessByComponentName,内部方法,通过ComponentName来获取插件信息,进而最终获取要加载服务的进程索引。
在客户端类PluginServiceClient类中,还定义了第一指定类成员为PluginServiceServerFetcher,该第一指定类成员是客户端类中的静态变量,可以直接通过类名加“.”号来访问。静态变量在客户端类中进行了声明,在客户端类外进行定义,并分配内存。
PluginServiceServerFetcher类,用来获取IPluginServiceServer接口的对应实现PluginServiceServer类。在该类中,fetchByProcess方法,通过“进程索引”(也可以换为进程PID等,参见前文)来获取指定的IPluginServiceServer对象。
IPluginServiceServer是“插件服务管理器”的核心接口。它和对应的实现PluginServiceServer共同负责Server端的服务调度、提供等工作,是服务的提供方。每个进程拥有各自的IPluginServiceServer对象,这样,这些进程都将拥有使用插件服务的能力。IPluginServiceServer是一个标准的AIDL接口,这意味着该接口可以被跨进程使用。IPluginServiceServer定义的代码如下:
下面介绍类和进程来说明IPluginServiceServer和PluginServiceClient的关系。如图2所示,PluginServiceClient运行在Process1进程,PluginServiceServer运行在Process2进程(允许Process1=Process2)。那么,PluginServiceClient可以利用BinderProxy,通过自定义的PluginServiceServerFetcher来和Process2的BinderStub进行通信。
BinderStub在收到消息后,会转发给PluginServiceServer的Stub中(这一步可以由***完成),该Stub会转发给PluginServiceServer,进而实现了PluginServiceClient和PluginServiceServer的通信。
此外,在图2中,将Android***标在右上角位置,是因为整个服务的管理过程,本发明实施例都没有接触到ActivityManagerService,也没有对它做Hook,等于是完全独立的一套方案。
在查找服务所在进程时,可以包括步骤301和302,且具体代码如下:
301:获取插件服务的ServiceInfo对象;
在该步骤中,ServiceInfo对象是Android***自带的,其是用来描述Service在Android***的配置文件AndroidManifest.xml中的信息。ServiceInfo对象的获取方式可以有多种,例如,在ComponentList中包含有插件的所有组件的列表,则可以通过调用Factory.queryPluginComponentList方法来获取该插件的所有组件的列表,再通过getService方法来获取这个插件内指定服务的ServiceInfo对象。又例如,可以自行解析插件里的PackageInfo,并找到其中的ServiceInfo对象。
302:拿到ProcessName,最终决定要使用的“进程索引”。
在拿到插件服务的信息后,需要获取ProcessName属性,拿到插件服务应该被执行到的进程名。在手机卫士里,采用了“进程索引”的方式,因此在这里会做判断,如果名字符合“guardservice”,则该服务在“常驻进程”中被执行,否则会在UI进程中被执行。
在获取插件服务的管理器的接口IPluginServiceServer时,可以包括步骤401、402和403,且具体代码如下:
401:从缓存中获取;
在该步骤中,会从缓存中先尝试获取IPluginServiceServer对象,如前所述,每个服务进程都会有一个对应的PluginServiceServer对象,自然要通过IPC来获取此对象,就必须要获取它的AIDL的接口,也即IPluginServiceServer接口对象。
402:创建或获取IPluginServiceServer对象并缓存;
若缓存中无此对象,则意味着需要创建或获取它。这里说的创建是指当目标进程不存在时,需要启动目标进程,并要求目标进程来构建出IPluginServiceServer的实现对象。而获取是指若目标进程存在,则直接从目标进程获取该对象即可,并将其缓存到mServiceManagerByProcessMap对象中,以备将来使用。在上面的代码中,先做判断,若目标进程为常驻进程,则可以通过调用PluginProcessMain.getPluginHost()方法来获取IPluginHost对象(它是常驻进程与其它进程通信中,最为核心的接口对象);进而通过调用IPluginHost.fetchServiceServer()方法来获取目标插件服务的管理器的接口。
当然,这个是手机卫士的做法,其它应用若要实施此专利,也可以直接获取目标进程的IPluginServiceServer对象即可。但有一点务必注意,无论采用何种方式获取,都必须先唤醒目标进程,再通过ContentProvider和MatrixCursor来获取接口对象。
403:挂“插件生命周期检测”。
该步骤也很关键。假若插件服务所在的进程被意外杀死,则若不及时从缓存中移除,那么下回再从缓存中获取到的IPluginServiceServer对象是不能使用的,调用其中的任何方法都会出现DeadObjectException异常。因此,本发明实施例提供了“插件生命周期”的处理。有关插件生命周期的信息,这里主要展示下其实现:
其中,binderDied是插件生命周期的核心方法,一旦目标进程被终止,则会立即回调给使用方,这里的做法很简单,只需将缓存清除即可。
基于上文各个实施例的在插件中使用服务的方法,基于同一发明构思,本发明实施例还提供了一种在插件中使用服务的装置。
图3示出了根据本发明一个实施例的在插件中使用服务的装置的结构示意图。如图3所示,该装置至少可以包括定义模块310、获取模块320以及提供模块330。
现介绍本发明实施例的在插件中使用服务的装置的各组成或器件的功能以及各部分间的连接关系:
定义模块310,适于定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类;
获取模块320,与定义模块310相耦合,适于当使用方需要使用目标插件服务时,调用所述客户端类中的第一指定类成员,由所述第一指定类成员获取所述目标插件服务的管理器的接口;
提供模块330,与获取模块320相耦合,适于利用所述目标插件服务的管理器的接口实现所述服务端类,进而由所述服务端类向使用方提供所述目标插件服务。
在本发明一实施例中,对插件服务进行的操作包括下列至少之一:
启动插件服务、停止插件服务、绑定插件服务、解除绑定插件服务。
在本发明一实施例中,所述获取模块320还适于:
在由所述第一指定类成员获取所述目标插件服务的管理器的接口之前,调用所述客户端类中的第二指定类成员,由所述第二指定类成员获取所述目标插件服务所在的目标进程。
在本发明一实施例中,所述获取模块320还适于:
由所述第二指定类成员获取所述目标插件服务的ServiceInfo对象;
利用所述目标插件服务的ServiceInfo对象,获取所述目标插件服务的ProcessName属性;
根据所述目标插件服务的ProcessName属性,获取所述目标插件服务所在的目标进程。
在本发明一实施例中,所述目标插件服务的ProcessName属性包括目标进程的索引值,所述获取模块320还适于:
获取预先定义的进程索引,其中,所述进程索引包括进程的索引值及对应的进程;
在所述进程索引中查找所述目标进程的索引值对应的目标进程。
在本发明一实施例中,所述获取模块320还适于:
由所述第一指定类成员基于所述目标插件服务所在的目标进程,获取所述目标插件服务的管理器的接口。
在本发明一实施例中,所述目标插件服务所在的目标进程在启动后,构建所述目标插件服务的管理器的接口,并缓存至指定对象;
所述获取模块320还适于:
由所述第一指定类成员,调用缓存的所述指定对象来获取所述目标插件服务所在的目标进程;
从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口。
在本发明一实施例中,所述获取模块320还适于:
判断所述目标插件服务所在的目标进程是否存在;
若是,则直接从所述目标插件服务所在的目标进程获取所述目标插件服务的管理器的接口;
若否,则启动所述目标插件服务所在的目标进程,并构建所述目标插件服务的管理器的接口,进而获取构建的所述目标插件服务的管理器的接口。
在本发明一实施例中,若所述目标插件服务所在的目标进程为常驻进程,所述获取模块320还适于:
调用PluginProcessMain.getPluginHost()方法来获取IPluginHost对象;
调用IPluginHost.fetchServiceServer()方法来获取所述目标插件服务的管理器的接口。
在本发明一实施例中,所述提供模块330还适于:
所述客户端类所在客户端进程与所述服务端类所在目标进程进行通信,向使用方提供所述目标插件服务。
在本发明一实施例中,所述提供模块330还适于:
所述客户端类所在客户端进程利用BinderProxy对象,通过所述第一指定类成员与所述服务端类所在目标进程的BinderStub进行通信;
BinderStub在收到消息后,将所述消息转发给所述服务端类的Stub中,进而所述服务端类的Stub将所述消息转发给所述服务端类;
所述服务端类基于所述消息向使用方提供所述目标插件服务。
在本发明一实施例中,所述客户端类为PluginServiceClient类,所述服务端类为PluginServiceServer类,所述目标插件服务的管理器的接口为IPluginServiceServer接口。
在本发明一实施例中,所述IPluginServiceServer接口为安卓接口定义语言AIDL接口。
在本发明一实施例中,如图4所示,所述目标插件服务所在的目标进程在启动后,构建所述目标插件服务的管理器的接口,并缓存至指定对象,所述装置还可以包括:
检测模块340,与提供模块330相耦合,适于检测所述目标插件服务所在的目标进程是否被终止;
清除模块350,与检测模块340相耦合,适于若所述检测模块340检测到所述目标插件服务所在的目标进程被终止,则调用缓存的所述指定对象来清除所述目标插件服务所在的目标进程。
根据上述任意一个优选实施例或多个优选实施例的组合,本发明实施例能够达到如下有益效果:
本发明实施例设计了一种类似C/S架构,插件服务的使用方是客户端,插件服务的提供方是服务端,每个服务端所处的进程中都有一个对插件服务进行管理的管理器,利用该管理器对应的服务端类对插件服务进行管理。即,定义用于插件服务的使用方对插件服务进行操作的客户端类,以及定义对插件服务进行管理的管理器对应的服务端类,当使用方需要使用目标插件服务时,调用客户端类中的第一指定类成员,由第一指定类成员获取目标插件服务的管理器的接口,利用目标插件服务的管理器的接口来实现服务端类,进而由服务端类向使用方提供目标插件服务。
采用本发明的技术方案解决了前文提及的现有技术一和现有技术二的问题,在手机卫士上采用现有技术一的方案,虽然实现起来很快,但最大的缺点就是,要增加Service就必须先升级宿主(即手机卫士)才可以。例如,若将来要添加BackupService2,则同样的,需要在宿主的AndroidManifest.xml中写上BackupService2,且只能在升级宿主后才能使用,显然这种做法非常不完美。在手机卫士上采用现有技术二的方案,通过在宿主(即手机卫士)中预埋一些Service坑位来实现,然而坑位有限,坑位过多又会导致启动速度受到影响。本发明实施例提供的技术方案,利用每个服务端所处的进程中都有一个对插件服务进行管理的管理器的处理逻辑,既可以不升级宿主程序,又无需在宿主的配置文件AndroidManifest.xml中增加服务的坑位,即可有效地实现插件服务的管理,增加了插件服务使用的稳定性,实用性高。
进一步地,本发明实施例中需要实现的插件服务Service中的onCreate、onStartCommand、onBind等方法,其做法和***Service完全一致,无需进行特殊修改。并且,本发明实施例可以直接通过PluginServiceClinet.xxxService(xxx可以为Service的四大基本操作:start/stop/bind/unbind)来使用插件服务。另外,本发明实施例是在服务端一侧来确定插件服务应执行在哪个进程上,不能在客户端决定执行进程,可以在定义插件内部AndroidManifest.xml中的<service>标签时指定。
在此处所提供的说明书中,说明了大量具体细节。然而,能够理解,本发明的实施例可以在没有这些具体细节的情况下实践。在一些实例中,并未详细示出公知的方法、结构和技术,以便不模糊对本说明书的理解。
类似地,应当理解,为了精简本公开并帮助理解各个发明方面中的一个或多个,在上面对本发明的示例性实施例的描述中,本发明的各个特征有时被一起分组到单个实施例、图、或者对其的描述中。然而,并不应将该公开的方法解释成反映如下意图:即所要求保护的本发明要求比在每个权利要求中所明确记载的特征更多的特征。更确切地说,如下面的权利要求书所反映的那样,发明方面在于少于前面公开的单个实施例的所有特征。因此,遵循具体实施方式的权利要求书由此明确地并入该具体实施方式,其中每个权利要求本身都作为本发明的单独实施例。
本领域那些技术人员可以理解,可以对实施例中的设备中的模块进行自适应性地改变并且把它们设置在与该实施例不同的一个或多个设备中。可以把实施例中的模块或单元或组件组合成一个模块或单元或组件,以及此外可以把它们分成多个子模块或子单元或子组件。除了这样的特征和/或过程或者单元中的至少一些是相互排斥之外,可以采用任何组合对本说明书(包括伴随的权利要求、摘要和附图)中公开的所有特征以及如此公开的任何方法或者设备的所有过程或单元进行组合。除非另外明确陈述,本说明书(包括伴随的权利要求、摘要和附图)中公开的每个特征可以由提供相同、等同或相似目的的替代特征来代替。
此外,本领域的技术人员能够理解,尽管在此所述的一些实施例包括其它实施例中所包括的某些特征而不是其它特征,但是不同实施例的特征的组合意味着处于本发明的范围之内并且形成不同的实施例。例如,在权利要求书中,所要求保护的实施例的任意之一都可以以任意的组合方式来使用。
本发明的各个部件实施例可以以硬件实现,或者以在一个或者多个处理器上运行的软件模块实现,或者以它们的组合实现。本领域的技术人员应当理解,可以在实践中使用微处理器或者数字信号处理器(DSP)来实现根据本发明实施例的在插件中使用服务的装置中的一些或者全部部件的一些或者全部功能。本发明还可以实现为用于执行这里所描述的方法的一部分或者全部的设备或者装置程序(例如,计算机程序和计算机程序产品)。这样的实现本发明的程序可以存储在计算机可读介质上,或者可以具有一个或者多个信号的形式。这样的信号可以从因特网网站上下载得到,或者在载体信号上提供,或者以任何其他形式提供。
应该注意的是上述实施例对本发明进行说明而不是对本发明进行限制,并且本领域技术人员在不脱离所附权利要求的范围的情况下可设计出替换实施例。在权利要求中,不应将位于括号之间的任何参考符号构造成对权利要求的限制。单词“包含”不排除存在未列在权利要求中的元件或步骤。位于元件之前的单词“一”或“一个”不排除存在多个这样的元件。本发明可以借助于包括有若干不同元件的硬件以及借助于适当编程的计算机来实现。在列举了若干装置的单元权利要求中,这些装置中的若干个可以是通过同一个硬件项来具体体现。单词第一、第二、以及第三等的使用不表示任何顺序。可将这些单词解释为名称。
至此,本领域技术人员应认识到,虽然本文已详尽示出和描述了本发明的多个示例性实施例,但是,在不脱离本发明精神和范围的情况下,仍可根据本发明公开的内容直接确定或推导出符合本发明原理的许多其他变型或修改。因此,本发明的范围应被理解和认定为覆盖了所有这些其他变型或修改。