应用程序免安装运行控制方法及其装置
技术领域
本发明涉及应用程序运行控制领域,具体而言,本发明涉及以Android系统原理为基础的一种应用程序免安装运行控制方法及其装置。
背景技术
随着移动互联网的发展,移动终端安装的应用程序越来越多,为了不断满足用户的新需求,就需要不断地增加新的功能模块,而现有技术中应用程序每次更新升级时都需要重新安装,会使得用户体验度下降,且升级后的应用程序的体积大,安装占用内存大,且浪费大量的用户流量。
为了克服应用程序版本更迭所导致的流量浪费的问题,现有技术常以提供增量包的形式来降低安装流量,这种方式则需要架设繁重的云端服务架构,并且对终端的计算量也提出了更高的要求。更关键的,这种方式并不能满足应用程序功能模块灵活扩展这一需求。
现有技术也尝试在Activity提供的界面中进一步提供网页访问接口,甚至结合HTML5等新型技术,以期实现应用程序的功能扩充,但是,网页提供的交互功能毕竟有限,未必能充分开发终端的最大性能,用户体验上也将有所下降。
同时,应用程序的免安装运行需求也变得日益迫切,免安装可以大大减少安装应用程序占用的内存空间,提高用户使用应用程序的体验度,故而提供一种应用程序的免安装运行方法变得极其重要。
发明内容
本发明的目的旨在解决上述至少一个问题,提供一种应用程序免安装运行控制方法及其相应装置。
为了实现上述目的,本发明采用以下技术方案:
本发明提供一种应用程序免安装运行控制方法,包括以下步骤:
响应于运行所述应用程序的指令,解析该应用程序的安装文件,获取其所需的运行信息,并据其中的配置文件预先注册应用程序运行所需程序资源和进程资源;
基于所述运行信息构造所述应用程序的运行环境,利用所述预注册的程序资源及进程资源运行该应用程序;
在所述应用程序运行过程中,调度应用程序运行所需的系统资源,以确保该应用程序正常运行。
具体的,所述运行信息包括应用程序的包名、版本号、组件。
进一步,所述组件包括Activity组件、Broadcast Receiver组件、Service组件、ContentProvider组件。
具体的,所述预先注册应用程序运行所需程序资源和进程资源的过程包括:
向系统预注册多个Activity坑位;
向系统预注册多个进程;
每个进程向系统预注册一个ContentProvider和一个Service。
进一步,所述构造所述应用程序的运行环境的过程包括在每个进程启动时动态注册广播。
进一步,所述构造所述应用程序的运行环境的过程还包括:
基于注册的组件坑位为待加载的应用程序组件分配相适配的目标组件坑位;
将待加载应用程序的组件加载到目标坑位所对应的进程空间中运行。
进一步,所述方法还包括步骤,将获取的运行信息缓存于系统内存,以供应用程序所处的进程在运行过程中查询相关信息。
进一步,所述方法还包括步骤,在应用程序运行过程中,其所处的进程通过binder接口查询缓存于内存中的运行信息。
进一步,所述方法还包括步骤,依据应用程序匹配的系统版本号为应用程序适配相应的系统接口函数。
具体的,所述调用相应的运行资源的步骤包括,拦截系统接口函数startActivity(),将系统进程的相应Activity组件参数替换为应用程序相应的Activity组件参数,以启动应用程序的Activity组件。
具体的,所述调用相应的运行资源的步骤包括,拦截系统接口函数getContetProvider(),将系统进程的相应ContentProvider组件参数替换为应用程序相应的ContentProvider组件参数,以启动应用程序的ContentProvider组件。
具体的,所述调用相应的运行资源的步骤包括,拦截系统接口函数startService(),将系统进程的相应Service组件参数替换为应用程序相应的Service组件参数,以启动应用程序的Service组件。
具体的,所述对系统接口函数的拦截采用Hook技术。
进一步,所述方法还包括步骤,当应用程序在运行过程中执行写操作时,对其写操作进行重定向,以使其写入其所属的文件存储目录。
进一步,还包括步骤,在对系统接口函数进行调用的过程中,将函数对象缓存入内存中,以供重复调用时直接获取相应的函数对象。
进一步,还包括步骤,在应用程序运行过程中,解析系统传输的Intent以获取要启动的组件,与应用程序的所有组件进行匹配,以确定要启动的应用程序组件并启动。
进一步,还包括步骤,在应用程序运行过程中,解析所述系统传输的Intent以获取相应的特征信息,基于特征信息匹配确定要启动的应用程序组件并启动。
一种应用程序免安装运行控制装置,包括:
响应单元:用于响应于运行所述应用程序的指令,解析该应用程序的安装文件,获取其所需的运行信息,并据其中的配置文件预先注册应用程序运行所需程序资源和进程资源;
构造单元:用于基于所述运行信息构造所述应用程序的运行环境,利用所述预注册的程序资源及进程资源运行该应用程序;
调度单元:用于在所述应用程序运行过程中,调度应用程序运行所需的系统资源,以确保该应用程序正常运行。
具体的,所述运行信息包括应用程序的包名、版本号、组件。
具体的,所述组件包括Activity组件、Broadcast Receiver组件、Service组件、ContentProvider组件。
具体的,所述响应单元预先注册应用程序所需程序资源和进程资源的过程包括:
向系统预注册多个Activity坑位;
向系统预注册多个进程;
每个进程向系统预注册一个ContentProvider和一个Service。
进一步,所述构造单元构造所述应用程序的运行环境的过程包括在每个进程启动时动态注册广播。
进一步,所述构造单元构造所述应用程序的运行环境的过程还包括:
基于注册的组件坑位为待加载的应用程序组件分配相适配的目标组件坑位;
将待加载应用程序的组件加载到目标坑位所对应的进程空间中运行。
进一步,还包括第一存储单元,用于将获取的运行信息缓存于系统内存,以供应用程序所处的进程在运行过程中查询相关信息。
进一步,还包括查询单元,用于在应用程序运行过程中,其所处的进程通过binder接口查询缓存于内存中的运行信息。
进一步,还包括适配单元,用于依据应用程序匹配的系统版本号为应用程序适配相应的系统接口函数。
具体的,所述调度单元执行的步骤包括,拦截系统接口函数startActivity(),将系统进程的相应Activity组件参数替换为应用程序相应的Activity组件参数,以启动应用程序的Activity组件。
具体的,所述调度单元执行的步骤包括,拦截系统接口函数getContetProvider(),将系统进程的相应ContentProvider组件参数替换为应用程序相应的ContentProvider组件参数,以启动应用程序的ContentProvider组件。
具体的,所述调度单元执行的步骤包括,拦截系统接口函数startService(),将系统进程的相应Service组件参数替换为应用程序相应的Service组件参数,以启动应用程序的Service组件。
具体的,所述对系统接口函数的拦截采用Hook技术。
进一步,还包括重定向单元,用于当应用程序在运行过程中执行写操作时,对其写操作进行重定向,以使其写入其所属的文件存储目录。
进一步,还包括第二存储单元,用于在对系统接口函数进行调用的过程中,将函数对象缓存入内存中,以供重复调用时直接获取相应的函数对象。
进一步,还包括Intent解析单元,用于在应用程序运行过程中,解析系统传输的Intent以获取要启动的组件,与应用程序的所有组件进行匹配,以确定要启动的应用程序组件并启动。
进一步,还包括Intent解析单元,用于在应用程序运行过程中,解析所述系统传输的Intent以获取相应的特征信息,基于特征信息匹配确定要启动的应用程序组件并启动。
相比现有技术,本发明的方案具有以下优点:
1、本发明通过对Android系统的包管理服务相关函数进行拦截,绕过系统查验的同时,解析获取免安装应用程序的各组件信息,由宿主程序预先向系统注册多个进程及组件坑位。运行应用程序过程中,为应用程序的组件分配相匹配的组件坑位,并加载相应的应用程序组件,在应用程序运行过程中,对系统的相应接口函数进行拦截,将相应组件参数替换为应用程序的组件参数,从而使应用程序通过宿主程序的进程及预注册组件坑位实现免安装运行。同时,在宿主程序运行免安装应用程序的过程中,对应用程序的写操作进行重定位,以使其写入所属的路径下。由此,实现应用程序的免安装运行,对于多功能模块的应用程序来说,将各功能模块独立出来,可以减小主程序的体积,对于远程下载进行更新升级的情况而言,每次只需下载相应的功能模块即可,即节省流量,又提高效率,提高用户体验度。
2、基于本发明所述方法可以实现应用程序的动态加载,对于包括多种功能模块的应用程序而言,使其各模块实现解耦合,当单个模块升级修改时,其他模块不受影响,同时也使该应用程序增加或修改某个功能模块时,无需修改主程序,不需要发布新版本,有利于应用程序的功能模块的灵活扩展,从而大大增加了应用程序开发的灵活度。
3、基于本发明所述方法,应用程序的功能模块可以实现标准化,由此便可以简化应用分发系统的云端实现,有利于不同应用程序之间的资源共享。不同应用程序之间只要构建了实施本发明的架构,即可通过下载一些通用的功能模块来实现同一功能,显然大大减轻了开发者的劳动,也简化了服务器的程序资源的复杂度。
本发明附加的方面和优点将在下面的描述中部分给出,这些将从下面的描述中变得明显,或通过本发明的实践了解到。
附图说明
本发明上述的和/或附加的方面和优点从下面结合附图对实施例的描述中将变得明显和容易理解,其中:
图1为本发明的应用程序免安装运行控制方法的流程示意图;
图2为本发明的活动组件进行空间分配方法的流程示意图;
图3为本发明的应用程序免安装运行控制装置原理框图。
具体实施方式
下面详细描述本发明的实施例,所述实施例的示例在附图中示出,其中自始至终相同或类似的标号表示相同或类似的元件或具有相同或类似功能的元件。下面通过参考附图描述的实施例是示例性的,仅用于解释本发明,而不能解释为对本发明的限制。
本技术领域技术人员可以理解,除非特意声明,这里使用的单数形式“一”、“一个”、“所述”和“该”也可包括复数形式。应该进一步理解的是,本发明的说明书中使用的措辞“包括”是指存在所述特征、整数、步骤、操作、元件和/或组件,但是并不排除存在或添加一个或多个其他特征、整数、步骤、操作、元件、组件和/或它们的组。应该理解,当我们称元件被“连接”或“耦接”到另一元件时,它可以直接连接或耦接到其他元件,或者也可以存在中间元件。此外,这里使用的“连接”或“耦接”可以包括无线连接或无线耦接。这里使用的措辞“和/或”包括一个或更多个相关联的列出项的全部或任一单元和全部组合。
本技术领域技术人员可以理解,除非另外定义,这里使用的所有术语(包括技术术语和科学术语),具有与本发明所属领域中的普通技术人员的一般理解相同的意义。还应该理解的是,诸如通用字典中定义的那些术语,应该被理解为具有与现有技术的上下文中的意义一致的意义,并且除非像这里一样被特定定义,否则不会用理想化或过于正式的含义来解释。
本技术领域技术人员可以理解,这里所使用的“终端”、“终端设备”既包括无线信号接收器的设备,其仅具备无发射能力的无线信号接收器的设备,又包括接收和发射硬件的设备,其具有能够在双向通信链路上,执行双向通信的接收和发射硬件的设备。这种设备可以包括:蜂窝或其他通信设备,其具有单线路显示器或多线路显示器或没有多线路显示器的蜂窝或其他通信设备;PCS(Personal Communications Service,个人通信系统),其可以组合语音、数据处理、传真和/或数据通信能力;PDA(Personal Digital Assistant,个人数字助理),其可以包括射频接收器、寻呼机、互联网/内联网访问、网络浏览器、记事本、日历和/或GPS(Global Positioning System,全球定位系统)接收器;常规膝上型和/或掌上型计算机或其他设备,其具有和/或包括射频接收器的常规膝上型和/或掌上型计算机或其他设备。这里所使用的“终端”、“终端设备”可以是便携式、可运输、安装在交通工具(航空、海运和/或陆地)中的,或者适合于和/或配置为在本地运行,和/或以分布形式,运行在地球和/或空间的任何其他位置运行。这里所使用的“终端”、“终端设备”还可以是通信终端、上网终端、音乐/视频播放终端,例如可以是PDA、MID(Mobile Internet Device,移动互联网设备)和/或具有音乐/视频播放功能的移动电话,也可以是智能电视、机顶盒等设备。
本技术领域技术人员可以理解,这里所使用的远端网络设备,其包括但不限于计算机、网络主机、单个网络服务器、多个网络服务器集或多个服务器构成的云。在此,云由基于云计算(Cloud Computing)的大量计算机或网络服务器构成,其中,云计算是分布式计算的一种,由一群松散耦合的计算机集组成的一个超级虚拟计算机。本发明的实施例中,远端网络设备、终端设备与WNS服务器之间可通过任何通信方式实现通信,包括但不限于,基于3GPP、LTE、WIMAX的移动通信、基于TCP/IP、UDP协议的计算机网络通信以及基于蓝牙、红外传输标准的近距无线传输方式。
本发明主要是以Android的ART虚拟机为基础而提出的,当然,如果其它操作系统中架构了类似于ART的机制,理论上也可以将本发明推广之。
如图1所揭示的本发明的应用程序免安装运行控制方法,可以实现于作为主程序使用的宿主功能模块中,其具体包括如下步骤:
S11、响应于运行所述应用程序的指令,解析该应用程序的安装文件,获取其所需的运行信息,并据其中的配置文件预先注册应用程序运行所需程序资源和进程资源;
此处所述应用程序即为免安装应用程序或免安装插件模块,对其安装文件进行解析,以获取其包名、版本号、资源存储路径、组件等在其运行过程中进程需要用到的信息。所述程序资源包括宿主程序预先向系统注册的组件坑位资源,所述进程资源包括宿主程序向系统预先注册的多个进程资源。
Android操作系统有其不同于其他操作系统的原理,Android为开发者提供四大组件,具体指Activity、Service、Receiver以及Content Provider等组件。Android应用程序以APK安装包的形式提供给用户进行安装,APK安装包中,具有用于封装实现各个组件的程序代码的classes.dex代码文件以及用于表达应用程序所用的各个组件的注册信息以及权限申请信息等内容的Androidmanifest.xml配置文件。由于应用程序的安装过程就是PackageManagerService(PMS)解析Androidmanifest.xml文件的过程,而应用程序安装包没有安装在本地,因此无法得到应用程序的相关属性信息,但只有获取如Activity组件、Service组件、Broadcast Receiver组件、ContentProvider组件等信息时,才能通过ActivityManagerService(AMS)正常运行应用程序。因此本发明采用如下方法解析应用程序的安装包:
1、解压缩安装包获取Androidmanifest.xml文件
Android系统中,每一个应用程序安装包都为APK文件,即Android ApplicationPackage File的缩写,其实质是zip格式的文件,是利用zip压缩技术结合签名技术实现的压缩包,因此,一方面可以通过解压缩技术释放其内部文件,另一方面可以通过Apktool之类的工具软件获取其内部文件。本领域技术人员均能娴熟地利用这些公知技术在一个给定目录中对原安装包进行处理,从而通过内存操作(非文件操作)的方式来获得其中的内部文件。
所述Android应用程序安装文件的内部结构如表1所示:
表1 APK文件结构
故而通过解析应用程序的安装包获得表1中所示的目录和文件,从而获取Androidmanifest.xml文件。
2、解析Androidmanifest.xml文件获取相应的属性信息
每个apk文件中都包含有一个AndroidManifest.xml文件,它记录着安装包的全局配置信息,是典型的配置文件。该配置文件中,除了包名package name、版本编号versionCode、版本号versionName等信息,还包括应用程序注册的activity、service、broadcastReceiver、contentProvider等系统组件,各大组件的注册信息在该配置文件中予以声明,在安装时,提供给应用程序向系统注册,具体是由系统的PackageManagerService读取该配置文件以完成注册。故对AndroidManifest.xml文件进行解析,获取应用程序的包名、版本号、各活动组件等属性信息,该些属性信息将在应用程序运行过程中作为运行信息提供给系统进程。
3、将获取的属性信息缓存于系统内存中
将上述获取的属性信息以数据结构的形式存储于系统内存中,如以数据库、map列表等方式进行存储,当应用程序运行时,其他进程需要获取应用程序的相应信息时,则通过binder接口查询,以快速获取所需的运行信息。
其中,解析应用程序安装文件的过程由系统的PMS执行,由于应用程序未安装到系统中,当PMS向系统查验该应用程序程序是否安装时,该未安装应用程序会被系统查询到不存在。因此,本发明采用预设注入的hook函数,对PMS执行查验的相关函数进行挂钩拦截,在查验之前将查验参数修改为宿主程序的查验参数,查验结束之后再将查验参数修改为所述应用程序的查验参数,以此通过系统查验,并由PMS将应用程序的安装文件解析,获取应用程序的运行信息,以该些运行信息作为候选构造应用程序运行环境的基础。
S12、基于所述运行信息构造所述应用程序的运行环境,利用所述预注册的程序资源及进程资源运行该应用程序;
众所周知,宿主程序要调起未安装的应用程序,需要访问应用程序的资源,而未安装的应用程序没有把其资源拷贝到宿主程序中,故而本发明采用以下方法访问应用程序的资源:
Activity的工作主要由Contextlmpl完成,其具有一个成员变量mBase,系统通过Contextlmpl中的获取资源的方法获取context,即应用程序的上下文。本发明通过自定义实现所述Contextlmpl中的获取资源的方法,获取未安装应用程序的资源,具体代码如下:
通过反射机制,调用AssetManager中的addAssetPath方法,将应用程序安装文件中的资源加载到Resource中,通过Resource对象调用应用程序安装文件中的资源。在获取应用程序安装文件资源之前,首先要由宿主程序新建DexClassLoader加载应用程序安装包,然后新建AssetManager加载应用程序安装文件的资源,最后通过Resource对象读取所述应用程序安装文件的资源。
Android操作系统有其不同于其他操作系统的原理,Android为开发者提供四大组件,具体指Activity、Service、Broadcast Receiver以及Content Provider等组件。其中,Activity组件通常用于实现用户界面以便应用程序能与用户进行人机交互,其代码可以被封装在classes.dex文件中,且需要在配置文件中AndroidManifest.xml实现注册;Service通常用于以后台服务的形式执行应用程序,有其自己的计算逻辑,通过创建新的进程启动,其代码也被封装在classes.dex文件中,且需要在配置文件中AndroidManifest.xml实现注册;Content Provider组件用于在进程间传输应用程序的数据信息,需要在配置文件中AndroidManifest.xml预先注册;Broadcast Receiver组件的注册有两种方式,一种是静态注册,即在配置文件中AndroidManifest.xml实现注册,另一种是动态注册,即在程序使用过程中通过Context.registerReceiver注册。
应用程序包含多个Activity,其启动过程实际上是应用程序中的默认activity启动过程,然后由默认activity在内部调起其他activity,故本发明重点阐述未安装应用程序的activity组件加载调度过程。
应用程序的配置文件Androidmanafest.xml中,各个activity的注册信息可以视为独立注册信息模块,视为“坑位”。Android系统在对应用程序进行安装时,由PackageManageService(PMS)对APK安装包的各个组件进行校验,而ActivityManagerSevice(AMS)则可以根据注册信息为活动组件建立进程入口(ActivityThread),以确保活动组件的正常运行。
通常,Activity活动组件应当预先在Androidmanafest.xml配置文件中进行坑位注册,并且坑位与组件名称以及活动组件的其他属性之间应保持一一对应性和确定性,这样才能够确保这些活动组件的正常注册。这种情况下,整个应用程序应当在开发时便确定了这些活动组件,由这些活动组件有机组合而成的应用程序继而作为一个整体提供给终端进行安装。终端每一次安装均是基于一个完整的应用程序的基础上进行,其中的每一个活动组件都在配置文件中进行了一一对应的注册表达,不存在任何不确定性。换言之,任何未经在Androidmanifest.xml注册的Activity活动组件,常规情况下均不能够被调度为进程运行。由此可知,每一次为应用程序扩展功能而提供新的活动组件,均需要将应用程序重新进行打包,并以打包后的安装文件提供给客户端进行安装,每次安装均是全量的。对于远程下载的情况而言,既浪费流量,又降低了效率,更不利于应用程序的功能模块的灵活扩展。为了解决此问题,本发明采用在配置文件中预留活动组件坑位的方式加载应用程序的活动组件。所述活动组件坑位的具体调度步骤如下:
步骤1、宿主程序向系统预注册多个activity组件坑位;
本发明出于预留的目的而在宿主程序的Androidmanifest.xml配置文件中设置多个活动组件注册信息,但这些注册信息所指向的活动组件的真实内容并未确定,因此,本发明中,形象地将这些配置文件预留了注册信息的活动组件,并在该配置文件提供给系统进行安装之后形成的活动组件虚拟单位称之为活动组件坑位。具体而言,预先在主程序的Androidmanifest.xml文件中声明了多个活动组件的注册信息,以便在主程序安装时,由PMS依据这些注册信息进行注册,由AMS为其建立进程入口,从而形成所述的活动组件坑位。
依据活动组件的不同属性,诸如launchMode、taskAffinity、theme等属性对活动组件进行分类管理,因此,在所述的注册信息中所使用到的包名,可以在包名的后缀中使用不同的特征字符用于表征不同类型的坑位,也是指代不同的进程空间,以利于识别。
步骤2、构建包含所述多个活动组件坑位的特征信息的坑位列表;
构建坑位列表的过程,将涉及到对Application类的运用,因此先行介绍Application类。
Application和Activity、Service一样是Android框架的一个系统组件,当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(singleton)模式的一个类。通常使用时并不需要指定一个Application,系统会自动为之创建该类,如果需要创建自有的Application,只需创建一个类继承Application并在AndroidManifest.xml文件中的application标签中进行注册。
启动Application时,系统会创建一个PID,即进程ID,所有的Activity都会在此进程上运行。那么如果在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,如果在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变。Application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局的单例的,所以在不同的Activity、Service中获得的对象都是同一个对象。所以可以通过Application来进行一些,如:数据传递、数据共享和数据缓存等操作。通过继承Application类来实现应用程序级的全局变量,这种全局变量方法相对静态类更有保障,直到应用的所有Activity全部被destory掉之后才会被释放掉。
与此同时,Activity的launchMode属性在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task栈。其中,launchMode属性包括standard、singleTop、singleTask、singleInstance四种模式。基于Application和launchMode的以上特性,通过重构Application.onCreate方法,在其中写入用于添加每个活动组件坑位特征信息的代码指令,来实现所述坑位列表的构建。
所述坑位列表实质上是实现了对具体列表的索引,其包括总表、以launchMode属性进行分类后组成的四种列表,以及launchMode属性结合Theme属性、taskAffinity属性分类后组成的列表。如private final HashMap<String,ActivityState>mStates,其中,String字段是字符串,表示Activity坑位的坑位名,而ActivityState则为状态信息,mStates表示该列表为总表,即所有的Activity都要存储在这张表中,以作为汇总之用。
通过上述的方式,便建立起坑位列表所包括的各个子表的索引关系,由此形成一个数据架构。
步骤3、利用坑位列表为待加载活动组件分配相适配的目标活动组件坑位;
本步骤利用上述的坑位列表为应用程序中的待加载活动组件分配坑位。具体而言,从应用程序的PackageInfo中获取所有ActivityInfo对象,通过launchMode、taskAffinity、theme等属性判别与请求分配的待加载活动组件所属的类型表,开始对类型表进行检索,从相应的类型表中确定相适配的目标活动组件坑位。
步骤4、将该待加载活动组件加载到目标坑位所对应的进程空间中运行。
由上一步骤提供的分配方法确定唯一的一个目标坑位,返回该目标坑位的信息,所述信息主要指坑位名称,通过重构的类加载器ClassLoader加载待加载活动组件。然后由类对象传递目标坑位,利用该对象构造Intent,然后以context.startActivity方法调用该Intent,即可打开目标坑位,从而使待加载活动组件加载于既定进程空间中运行。
由此,便可实现免安装应用程序的活动组件Activity加载于系统进程中,构造出其运行环境。此外,
宿主程序还向系统预注册多个进程,以满足有多个未安装应用程序同时借助宿主程序运行时使用,每个应用程序对应一个进程,且仅对应一个ContentProvider和一个Service。故而,每个进程又向系统预注册一个ContentProvider坑位和一个Service坑位。其中,Service用于以后台服务的形式运行应用程序,ContentProvider用于为不同的应用程序访问相同的数据提供统一接口。本发明对免安装应用程序的Service和ContentProvider的调度与上述活动组件Activity相同,只是本发明所述这两种组件的调度方案更适用与多进程的情况,而Activity的调度方法适用于单进程和多进程。对于Service和ContentProvider的调度方案可参考上述Activity的调度方法,在此不再赘述。
Android系统四大组件还包括广播组件Broadcast Receiver组件,由于通常的静态注册方式需要在AndroidManifest.xml文件中声明,而免安装应用程序由于没有安装入系统,故其AndroidManifest.xml文件中声明的Broadcast Receiver组件不能通过系统认证,也不能在系统中注册。因此,本发明采用动态注册的方式在系统中注册BroadcastReceiver,以使应用程序在运行过程中实现广播的注册。
宿主程序预先向系统注册多个进程,以满足多个应用程序的同时运行,或应用程序的不同的组件加载于不同的进程中。其中,每个进程仅对应一个上述所述的组件坑位,当应用程序运行时,则去匹配未被占用的坑位,如果匹配成功则应用程序运行于该坑位对应的进程,如果匹配不成功则继续匹配未被占用的坑位,直到匹配成功。在此过程中,当坑位被应用程序使用完以后,则回收该坑位,无论该坑位对应的进程是否还在运行,都对该坑位进行回收,以便后续分配给其他应用程序,实现进程的共享。
当然,上述只是对应用程序部分运行环境的构造方法做说明,由于程序的运行需要涉及很多细节,故在此不作一一说明,本领域技术人员可以基于本发明所述方法,结合现有技术,构造出免安装应用程序的运行环境。
S13、在所述应用程序运行过程中,调度应用程序运行所需的系统资源,以确保该应用程序正常运行。
应用程序基于上述构造完成的运行环境运行过程中,还包括对一系列运行资源的调用。由于Android系统的碎片化,要适用于多种不同的系统版本,就需要开发不同版本的应用程序,而为了使作为功能模块的应用程序在免安装的情况下适用于不同版本的程序,需要在应用程序运行过程中,为其适配相匹配的系统接口函数。在对系统接口函数调用过程中,通常需要先利用Java的反射机制去获取函数接口的对象,然后再调用该函数方法,该过程比较耗时,因此,为了提高调用效率,本发明将第一次调用后获取的函数接口对象缓存入内存,以便后续调用相同函数时直接从内存中获取相应的对象,从而避免系统查找相应对象产生的耗时问题。
在应用程序运行过程中,由于其未安装到系统中,故其各组件就没有实际意义,而各组件的生命周期是由系统管理的。Activity的生命周期由ActivityManager管理的,由ActivityManager调用startActivity函数启动Activity。主进程调用startService函数,通过binder进程间通信机制通知ActivityManagerService创建新的进程,并启动指定的服务,其中,所述指定的服务即为上述步骤确定的要加载的应用程序的服务。ContentProvider只有在被用到的时候才会被系统加载到内存中,ActivityManager调用getContentProvider函数获取应用程序的数据信息。
基于上述原理,为了保证应用程序的正常运行,本发明提供一种方法,作为系统和应用程序之间的代理,将应用程序的运行过程中用户的资源加载到系统的进程中。具体调用应用程序的运行资源包括以下几方面:
1、在Android系统中,启动Activity的方式有两种,一种是用户点击应用程序的图标时,launcher会启动主Activity;另一种是应用程序的默认Activity启动之后,从内部调用startActivity函数接口启动新的Activity。由于应用程序未安装,所以无法通过第一种点击图标的显式方式启动Activity,故而通过启动应用程序的默认Activity启动应用程序的Activity。具体对宿主程序启动的默认Activity的函数接口startActivity进行拦截,将其参数intent中的活动组件修改为应用程序的默认Activity,从而使系统启动应用程序的主Activity,而后再由主Activity依次启动应用程序的其他子默认Activity,在该过程中,应用程序的所有活动组件以预先基于上一步骤所述的活动组件的调度方法加载入了系统进程空间,故而可以基于本步骤的方法启动相应的应用程序活动组件。所述对应用程序活动组件的启动过程中,还包括对启动过程中其他相关函数的拦截处理,如handleLaunchActivity函数,该函数为ActivityThread类的成员函数,用来创建以及首次激活Activity组件,由钩子函数对其进行拦截,将系统进程的Activity组件相应参数替换为应用程序相应的Activity组件参数,从而使得应用程序的活动组件正常启动,由此实现对应用程序Activity生命周期的管理。
2、Service是运行后台的服务,不可见,是不能直接交互的程序。其启动方式有两种,一种是通过调用startService()函数启动,另一种是通过调用函数bindService()启动。对于第一种方式而言,通过调用onCreate()、onStart()启动Service,调用stopService()结束Service。对于第二种方式而言,首先调用onCreate()创建一个service实例,然后调用bindService()函数返回基于binder机制通信的客户端一个IBind接口实例,以允许客户端回调service,将该service和调用service的客户端类绑定,结束service时调用unbindService()解除绑定,再调用onDestroyed()退出。基于该原理,宿主程序调用监控模块由相应的钩子函数对startService()、stopService()、bindService()、unbindService()等与service组件启动和结束相关的接口函数进行拦截处理,将系统进程的Service组件参数替换为应用程序相应的Service组件参数,以启动应用程序的Service组件,实现对service生命周期的管理。
3、ContentProvider是Android四大组件之一,Android系统提供了五种数据存储技术,除了ContentProvider以外还包括SharedPreference、IO、SQLite、网络,而这五种技术都能由ContentProvider提供的方法按照统一的代码格式存储,以实现跨应用数据共享。ContentProvider只有在使用的时才会被系统加载到内存中,为了提高调用效率,通常在调用一次后将ContentProvider接口保存在ActivityThread类的mProviderMap成员变量中,以ContentProvider对应的URI的authority为键值进行保存。第一次启动ContentProvider的时候,系统会调用ActivityManagerService服务的getContentProvider接口,获取一个ContentProviderHolder对象holder,该对象包含了所要获取的ArticlesProvider接口,将该接口返回给调用者之后,会调用installProvider函数将该接口保存在本地中。后续再调用ContentProvider接口时,可以直接通过getExistingProvider函数获取保存在本地的ContentProvider接口。基于上述原理可知,对宿主程序调用监控模块由相应的钩子函数对getContentProvider拦截处理,将系统进程的ContentProvider组件的相关参数替换为应用程序相应的ContentProvider组件的相关参数,以在第一次启动ContentProvider接口时将应用程序的ContentProvider接口存入本地,供后续调用使用。
以上只是对挂钩函数的部分列举,对于其他相关函数的拦截处理再此不再一一列举,本领域技术人员在按照本发明实现过程中可以根据具体情况加入相应的钩子函数。
Intent是不同组件之间相互通信的纽带,封装了不同组件之间通信的条件。在启动各组件的过程中,通常通过Intent传递组件参数,所述对拦截函数的处理,具体为将其参数替换为应用程序的相应参数,包括修改Intent的参数。故而需要解析系统传输的Intent,以获取其要传递的信息,在传递启动组件的信息时,通过解析Intent获取要启动的组件,如activity、seriver等,与应用程序的所有组件进行匹配,确定相匹配的组件以将应用程序的组件替换原Intent中的组件信息,从而启动应用程序的相应组件。
在其他实施例中,所述解析系统传输的Intent也可以获取其他特征信息,如Intent执行的动作action信息、传递的数据data信息、附加信息extras等,基于特征信息匹配应用程序的所以组件,以确定相匹配的组件,将该应用程序的匹配组件替换原Intent中的组件信息,从而启动应用程序的相应组件。
所述对系统接口函数的拦截采用Hook技术,利用钩子函数对相关函数接口进行监控,截获对相应函数的调用,转而执行相应的钩子函数,由钩子函数依据其自身实现的逻辑来应答调用指令,从而实现以上所述对运行资源的调用,以保证应用程序的正常运行。
其中,术语“钩子”涵盖了用于通过拦截在软件组件之间传递的函数调用、消息、或事件来改变或增加操作系统、应用程序、或其他软件组件的行为的技术。而处理这种被拦截的函数调用、事件或消息的代码就被称为钩子函数。钩子通常用于各种目标,包括对功能进行调试和对功能进行扩展。其示例可以包括在键盘或鼠标事件传递到应用程序之前拦截它们,或者拦截系统调用(system call)、或者系统函数行为、函数执行结果等,以监视或修改应用程序或其他组件的功能等等。本实施例即可采用钩子函数对所述系统接口函数挂钩以实现对应用程序的相应资源的加载。
在应用程序运行过程中,还包括对应用程序的写操作进行重定向,以使其写入应用程序所属的文件存储目录,以免造成错误地写入其他文件目录。
上述以程序化思维对本发明所述方法进行了说明,为了更进一步以模块化方式描述本发明所述方法,还提供一种应用程序免安装运行控制装置,包括响应单元11、构造单元12、运行资源调用单元13,以及在部分变换实施例中的第一存储单元14、查询单元15、适配单元16、重定向单元17、第二存储单元18、Intent解析单元19。其中,
响应单元11,用于响应于运行所述应用程序的指令,解析该应用程序的安装文件,获取其所需的运行信息,并据其中的配置文件预先注册应用程序运行所需程序资源和进程资源;
此处所述应用程序即为免安装应用程序或免安装插件模块,由解析单元11对其安装文件进行解析,以获取其包名、版本号、资源存储路径、组件等在其运行过程中进程需要用到的信息。所述程序资源包括宿主程序预先向系统注册的组件坑位资源,所述进程资源包括宿主程序向系统预先注册的多个进程资源。
Android操作系统有其不同于其他操作系统的原理,Android为开发者提供四大组件,具体指Activity、Service、Receiver以及Content Provider等组件。Android应用程序以APK安装包的形式提供给用户进行安装,APK安装包中,具有用于封装实现各个组件的程序代码的classes.dex代码文件以及用于表达应用程序所用的各个组件的注册信息以及权限申请信息等内容的Androidmanifest.xml配置文件。由于应用程序的安装过程就是PackageManagerService(PMS)解析Androidmanifest.xml文件的过程,而应用程序安装包没有安装在本地,因此无法得到应用程序的相关属性信息,但只有获取如Activity组件、Service组件、Broadcast Receiver组件、ContentProvider组件等信息时,才能通过ActivityManagerService(AMS)正常运行应用程序。因此本发明响应单元11采用如下步骤解析应用程序的安装包:
1、解压缩安装包获取Androidmanifest.xml文件
Android系统中,每一个应用程序安装包都为APK文件,即Android ApplicationPackage File的缩写,其实质是zip格式的文件,是利用zip压缩技术结合签名技术实现的压缩包,因此,一方面可以通过解压缩技术释放其内部文件,另一方面可以通过Apktool之类的工具软件获取其内部文件。本领域技术人员均能娴熟地利用这些公知技术在一个给定目录中对原安装包进行处理,从而通过内存操作(非文件操作)的方式来获得其中的内部文件。
所述Android应用程序安装文件的内部结构如表1所示:
表1 APK文件结构
故而通过解析应用程序的安装包获得表1中所示的目录和文件,从而获取Androidmanifest.xml文件。
2、解析Androidmanifest.xml文件获取相应的属性信息
每个apk文件中都包含有一个AndroidManifest.xml文件,它记录着安装包的全局配置信息,是典型的配置文件。该配置文件中,除了包名package name、版本编号versionCode、版本号versionName等信息,还包括应用程序注册的activity、service、broadcastReceiver、contentProvider等系统组件,各大组件的注册信息在该配置文件中予以声明,在安装时,提供给应用程序向系统注册,具体是由系统的PackageManagerService读取该配置文件以完成注册。故对AndroidManifest.xml文件进行解析,获取应用程序的包名、版本号、各活动组件等属性信息,该些属性信息将在应用程序运行过程中作为运行信息提供给系统进程。
3、将获取的属性信息缓存于系统内存中
第一存储单元14将上述获取的属性信息以数据结构的形式存储于系统内存中,如以数据库、map列表等方式进行存储,当应用程序运行时,其他进程需要获取应用程序的相应信息时,则由查询单元15通过binder接口查询,以快速获取所需的运行信息。
其中,解析应用程序安装文件的过程由系统的PMS执行,由于应用程序未安装到系统中,当PMS向系统查验该应用程序程序是否安装时,该未安装应用程序会被系统查询到不存在。因此,本发明采用预设注入的hook函数,对PMS执行查验的相关函数进行挂钩拦截,在查验之前将查验参数修改为宿主程序的查验参数,查验结束之后再将查验参数修改为所述应用程序的查验参数,以此通过系统查验,并由PMS将应用程序的安装文件解析,获取应用程序的运行信息,以该些运行信息作为候选构造应用程序运行环境的基础。
构造单元12,用于基于所述运行信息构造所述应用程序的运行环境,利用所述预注册的程序资源及进程资源运行该应用程序;
众所周知,宿主程序要调起未安装的应用程序,需要访问应用程序的资源,而未安装的应用程序没有把其资源拷贝到宿主程序中,故而本发明采用以下方法访问应用程序的资源:
Activity的工作主要由Contextlmpl完成,其具有一个成员变量mBase,系统通过Contextlmpl中的获取资源的方法获取context,即应用程序的上下文。本发明通过自定义实现所述Contextlmpl中的获取资源的方法,获取未安装应用程序的资源,具体代码如下:
通过反射机制,调用AssetManager中的addAssetPath方法,将应用程序安装文件中的资源加载到Resource中,通过Resource对象调用应用程序安装文件中的资源。在获取应用程序安装文件资源之前,首先要由宿主程序新建DexClassLoader加载应用程序安装包,然后新建AssetManager加载应用程序安装文件的资源,最后通过Resource对象读取所述应用程序安装文件的资源。
Android操作系统有其不同于其他操作系统的原理,Android为开发者提供四大组件,具体指Activity、Service、Broadcast Receiver以及Content Provider等组件。其中,Activity组件通常用于实现用户界面以便应用程序能与用户进行人机交互,其代码可以被封装在classes.dex文件中,且需要在配置文件中AndroidManifest.xml实现注册;Service通常用于以后台服务的形式执行应用程序,有其自己的计算逻辑,通过创建新的进程启动,其代码也被封装在classes.dex文件中,且需要在配置文件中AndroidManifest.xml实现注册;Content Provider组件用于在进程间传输应用程序的数据信息,需要在配置文件中AndroidManifest.xml预先注册;Broadcast Receiver组件的注册有两种方式,一种是静态注册,即在配置文件中AndroidManifest.xml实现注册,另一种是动态注册,即在程序使用过程中通过Context.registerReceiver注册。
应用程序的运行需要相匹配的运行环境,故本发明由构造单元12构造应用程序的运行环境。应用程序包含多个Activity,其启动过程实际上是应用程序中的默认activity启动过程,然后由默认activity在内部调起其他activity,故本发明重点阐述由构造单元12对未安装应用程序的activity组件加载调度过程。
应用程序的配置文件Androidmanafest.xml中,各个activity的注册信息可以视为独立注册信息模块,视为“坑位”。Android系统在对应用程序进行安装时,由PackageManageService(PMS)对APK安装包的各个组件进行校验,而ActivityManagerSevice(AMS)则可以根据注册信息为活动组件建立进程入口(ActivityThread),以确保活动组件的正常运行。
通常,Activity活动组件应当预先在Androidmanafest.xml配置文件中进行坑位注册,并且坑位与组件名称以及活动组件的其他属性之间应保持一一对应性和确定性,这样才能够确保这些活动组件的正常注册。这种情况下,整个应用程序应当在开发时便确定了这些活动组件,由这些活动组件有机组合而成的应用程序继而作为一个整体提供给终端进行安装。终端每一次安装均是基于一个完整的应用程序的基础上进行,其中的每一个活动组件都在配置文件中进行了一一对应的注册表达,不存在任何不确定性。换言之,任何未经在Androidmanifest.xml注册的Activity活动组件,常规情况下均不能够被调度为进程运行。由此可知,每一次为应用程序扩展功能而提供新的活动组件,均需要将应用程序重新进行打包,并以打包后的安装文件提供给客户端进行安装,每次安装均是全量的。对于远程下载的情况而言,既浪费流量,又降低了效率,更不利于应用程序的功能模块的灵活扩展。为了解决此问题,本发明采用在配置文件中预留活动组件坑位的方式加载应用程序的活动组件。所述活动组件坑位的具体调度步骤如下:
步骤1、宿主程序向系统预注册多个activity组件坑位;
本发明出于预留的目的而在宿主程序的Androidmanifest.xml配置文件中设置多个活动组件注册信息,但这些注册信息所指向的活动组件的真实内容并未确定,因此,本发明中,形象地将这些配置文件预留了注册信息的活动组件,并在该配置文件提供给系统进行安装之后形成的活动组件虚拟单位称之为活动组件坑位。具体而言,预先在主程序的Androidmanifest.xml文件中声明了多个活动组件的注册信息,以便在主程序安装时,由PMS依据这些注册信息进行注册,由AMS为其建立进程入口,从而形成所述的活动组件坑位。
依据活动组件的不同属性,诸如launchMode、taskAffinity、theme等属性对活动组件进行分类管理,因此,在所述的注册信息中所使用到的包名,可以在包名的后缀中使用不同的特征字符用于表征不同类型的坑位,也是指代不同的进程空间,以利于识别。
步骤2、构建包含所述多个活动组件坑位的特征信息的坑位列表;
构建坑位列表的过程,将涉及到对Application类的运用,因此先行介绍Application类。
Application和Activity、Service一样是Android框架的一个系统组件,当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(singleton)模式的一个类。通常使用时并不需要指定一个Application,系统会自动为之创建该类,如果需要创建自有的Application,只需创建一个类继承Application并在AndroidManifest.xml文件中的application标签中进行注册。
启动Application时,系统会创建一个PID,即进程ID,所有的Activity都会在此进程上运行。那么如果在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,如果在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变。Application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局的单例的,所以在不同的Activity、Service中获得的对象都是同一个对象。所以可以通过Application来进行一些,如:数据传递、数据共享和数据缓存等操作。通过继承Application类来实现应用程序级的全局变量,这种全局变量方法相对静态类更有保障,直到应用的所有Activity全部被destory掉之后才会被释放掉。
与此同时,Activity的launchMode属性在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task栈。其中,launchMode属性包括standard、singleTop、singleTask、singleInstance四种模式。基于Application和launchMode的以上特性,通过重构Application.onCreate方法,在其中写入用于添加每个活动组件坑位特征信息的代码指令,来实现所述坑位列表的构建。
所述坑位列表实质上是实现了对具体列表的索引,其包括总表、以launchMode属性进行分类后组成的四种列表,以及launchMode属性结合Theme属性、taskAffinity属性分类后组成的列表。如private final HashMap<String,ActivityState>mStates,其中,String字段是字符串,表示Activity坑位的坑位名,而ActivityState则为状态信息,mStates表示该列表为总表,即所有的Activity都要存储在这张表中,以作为汇总之用。
通过上述的方式,便建立起坑位列表所包括的各个子表的索引关系,由此形成一个数据架构。
步骤3、利用坑位列表为待加载活动组件分配相适配的目标活动组件坑位;
本步骤利用上述的坑位列表为应用程序中的待加载活动组件分配坑位。具体而言,从应用程序的PackageInfo中获取所有ActivityInfo对象,通过launchMode、taskAffinity、theme等属性判别与请求分配的待加载活动组件所属的类型表,开始对类型表进行检索,从相应的类型表中确定相适配的目标活动组件坑位。
步骤4、将该待加载活动组件加载到目标坑位所对应的进程空间中运行。
由上一步骤提供的分配方法确定唯一的一个目标坑位,返回该目标坑位的信息,所述信息主要指坑位名称,通过重构的类加载器ClassLoader加载待加载活动组件。然后由类对象传递目标坑位,利用该对象构造Intent,然后以context.startActivity方法调用该Intent,即可打开目标坑位,从而使待加载活动组件加载于既定进程空间中运行。
由此,便可实现免安装应用程序的活动组件Activity加载于系统进程中,构造出其运行环境。此外,
宿主程序还向系统预注册多个进程,以满足有多个未安装应用程序同时借助宿主程序运行时使用,每个应用程序对应一个进程,且仅对应一个ContentProvider和一个Service。故而,每个进程又向系统预注册一个ContentProvider坑位和一个Service坑位。其中,Service用于以后台服务的形式运行应用程序,ContentProvider用于为不同的应用程序访问相同的数据提供统一接口。本发明对免安装应用程序的Service和ContentProvider的调度与上述活动组件Activity相同,只是本发明所述这两种组件的调度方案更适用与多进程的情况,而Activity的调度方法适用于单进程和多进程。对于Service和ContentProvider的调度方案可参考上述Activity的调度方法,在此不再赘述。
Android系统四大组件还包括广播组件Broadcast Receiver组件,由于通常的静态注册方式需要在AndroidManifest.xml文件中声明,而免安装应用程序由于没有安装入系统,故其AndroidManifest.xml文件中声明的Broadcast Receiver组件不能通过系统认证,也不能在系统中注册。因此,本发明采用动态注册的方式在系统中注册BroadcastReceiver,以使应用程序在运行过程中实现广播的注册。
宿主程序预先向系统注册多个进程,以满足多个应用程序的同时运行,或应用程序的不同的组件加载于不同的进程中。其中,每个进程仅对应一个上述所述的组件坑位,当应用程序运行时,则去匹配未被占用的坑位,如果匹配成功则应用程序运行于该坑位对应的进程,如果匹配不成功则继续匹配未被占用的坑位,直到匹配成功。在此过程中,当坑位被应用程序使用完以后,则回收该坑位,无论该坑位对应的进程是否还在运行,都对该坑位进行回收,以便后续分配给其他应用程序,实现进程的共享。
当然,上述只是对本发明构造单元12执行的对应用程序部分运行环境的构造方法做说明,由于程序的运行需要涉及很多细节,故在此不一一说明,本领域技术人员可以基于本发明所述方法,结合现有技术,构造出免安装应用程序的运行环境。
运行资源调用单元13,用于在所述应用程序运行过程中,调度应用程序运行所需的系统资源,以确保该应用程序正常运行。
应用程序基于上述构造完成的运行环境运行过程中,还包括对一系列运行资源的调用,本发明配置运行资源调用单元13执行。首先,由于Android系统的碎片化,要适用于多种不同的系统版本,就需要开发不同版本的应用程序,而为了使作为功能模块的应用程序在免安装的情况下适用于不同版本的程序,需要在应用程序运行过程中,由适配单元16为其适配相匹配的系统接口函数。在对系统接口函数调用过程中,通常需要先利用Java的反射机制去获取函数接口的对象,然后再调用该函数方法,该过程比较耗时,因此,为了提高调用效率,本发明的第二存储单元18将第一次调用后获取的函数接口对象缓存入内存,以便后续调用相同函数时直接从内存中获取相应的对象,从而避免系统查找相应对象产生的耗时问题。
其次,在应用程序运行过程中,由于其未安装到系统中,故其各组件就没有实际意义,而各组件的生命周期是由系统管理的。Activity的生命周期由ActivityManager管理的,由ActivityManager调用startActivity函数启动Activity。主进程调用startService函数,通过binder进程间通信机制通知ActivityManagerService创建新的进程,并启动指定的服务,其中,所述指定的服务即为上述步骤确定的要加载的应用程序的服务。ContentProvider只有在被用到的时候才会被系统加载到内存中,ActivityManager调用getContentProvider函数获取应用程序的数据信息。
基于上述原理,为了保证应用程序的正常运行,本发明的运行资源调用单元13提供一种方法,作为系统和应用程序之间的代理,将应用程序的运行过程中用户的资源加载到系统的进程中。具体调用应用程序的运行资源包括以下几方面:
1、在Android系统中,启动Activity的方式有两种,一种是用户点击应用程序的图标时,launcher会启动主Activity;另一种是应用程序的默认Activity启动之后,从内部调用startActivity函数接口启动新的Activity。由于应用程序未安装,所以无法通过第一种点击图标的显式方式启动Activity,故而通过启动应用程序的默认Activity启动应用程序的Activity。具体对宿主程序启动的默认Activity的函数接口startActivity进行拦截,将其参数intent中的活动组件修改为应用程序的默认Activity,从而使系统启动应用程序的主Activity,而后再由主Activity依次启动应用程序的其他子默认Activity,在该过程中,应用程序的所有活动组件以预先基于上一步骤所述的活动组件的调度方法加载入了系统进程空间,故而可以基于本步骤的方法启动相应的应用程序活动组件。所述对应用程序活动组件的启动过程中,还包括对启动过程中其他相关函数的拦截处理,如handleLaunchActivity函数,该函数为ActivityThread类的成员函数,用来创建以及首次激活Activity组件,由钩子函数对其进行拦截,将系统进程的Activity组件相应参数替换为应用程序相应的Activity组件参数,从而使得应用程序的活动组件正常启动,由此实现对应用程序Activity生命周期的管理。
2、Service是运行后台的服务,不可见,是不能直接交互的程序。其启动方式有两种,一种是通过调用startService()函数启动,另一种是通过调用函数bindService()启动。对于第一种方式而言,通过调用onCreate()、onStart()启动Service,调用stopService()结束Service。对于第二种方式而言,首先调用onCreate()创建一个service实例,然后调用bindService()函数返回基于binder机制通信的客户端一个IBind接口实例,以允许客户端回调service,将该service和调用service的客户端类绑定,结束service时调用unbindService()解除绑定,再调用onDestroyed()退出。基于该原理,宿主程序调用监控模块由相应的钩子函数对startService()、stopService()、bindService()、unbindService()等与service组件启动和结束相关的接口函数进行拦截处理,将系统进程的Service组件参数替换为应用程序相应的Service组件参数,以启动应用程序的Service组件,实现对service生命周期的管理。
3、ContentProvider是Android四大组件之一,Android系统提供了五种数据存储技术,除了ContentProvider以外还包括SharedPreference、IO、SQLite、网络,而这五种技术都能由ContentProvider提供的方法按照统一的代码格式存储,以实现跨应用数据共享。ContentProvider只有在使用的时才会被系统加载到内存中,为了提高调用效率,通常在调用一次后将ContentProvider接口保存在ActivityThread类的mProviderMap成员变量中,以ContentProvider对应的URI的authority为键值进行保存。第一次启动ContentProvider的时候,系统会调用ActivityManagerService服务的getContentProvider接口,获取一个ContentProviderHolder对象holder,该对象包含了所要获取的ArticlesProvider接口,将该接口返回给调用者之后,会调用installProvider函数将该接口保存在本地中。后续再调用ContentProvider接口时,可以直接通过getExistingProvider函数获取保存在本地的ContentProvider接口。基于上述原理可知,对宿主程序调用监控模块由相应的钩子函数对getContentProvider拦截处理,将系统进程的ContentProvider组件的相关参数替换为应用程序相应的ContentProvider组件的相关参数,以在第一次启动ContentProvider接口时将应用程序的ContentProvider接口存入本地,供后续调用使用。
以上只是对挂钩函数的部分列举,对于其他相关函数的拦截处理再此不再一一列举,本领域技术人员在按照本发明实现过程中可以根据具体情况加入相应的钩子函数。
Intent是不同组件之间相互通信的纽带,封装了不同组件之间通信的条件。在启动各组件的过程中,通常通过Intent传递组件参数,所述对拦截函数的处理,具体为将其参数替换为应用程序的相应参数,包括修改Intent的参数。故而需要解析系统传输的Intent,以获取其要传递的信息,在传递启动组件的信息时,由Intent解析单元19通过解析Intent获取要启动的组件,如activity、seriver等,与应用程序的所有组件进行匹配,确定相匹配的组件以将应用程序的组件替换原Intent中的组件信息,从而启动应用程序的相应组件。
在其他实施例中,所述Intent解析单元19解析系统传输的Intent也可以获取其他特征信息,如Intent执行的动作action信息、传递的数据data信息、附加信息extras等,基于特征信息匹配应用程序的所以组件,以确定相匹配的组件,将该应用程序的匹配组件替换原Intent中的组件信息,从而启动应用程序的相应组件。
所述对系统接口函数的拦截采用Hook技术,利用钩子函数对相关函数接口进行监控,截获对相应函数的调用,转而执行相应的钩子函数,由钩子函数依据其自身实现的逻辑来应答调用指令,从而实现以上所述对运行资源的调用,以保证应用程序的正常运行。
在应用程序运行过程中,还包括由重定向单元17对应用程序的写操作进行重定向,以使其写入应用程序所属的文件存储目录,以免造成错误地写入其他文件目录。
为便于更直观地理解本发明,如下介绍一个以本发明的方法或装置实现的应用场景。
在实现了本发明的方法或装置的手机终端中,由实施了本发明的诸如手机助手之类的软件处于运行状态,用户通过手机助手下载其中的其他功能插件,比如清理插件、生活助手、面对面快传等。当用户点击打开按钮使上述一个或多个插件运行时,手机助手作为宿主程序根据其配置文件中的组件信息预先向系统注册相应组件的坑位,如activity组件、service组件、provider组件,宿主程序获取上述一个或多个插件的安装文件中的相关资源,调用PMS对其资源中的配置文件进行解析,从而获得插件的安装文件中的组件信息。宿主程序基于该组件信息将对应的预注册坑位分别分配给相应的组件,由此完成应用程序组件的系统注册。
当用户打开插件的一个页面时,则宿主程序启动该页面相应的activity组件,具体通过拦截系统启动activity组件的接口函数,获取intent信息,并将其中传递的activity组件参数替换为插件对应的activity组件参数。由此,实现插件的组件正常加载运行。在插件运行过程中,宿主程序还为其调度相应的系统资源以及进程资源,以确保其正常运行。对于多个插件而言,所述宿主程序预先向系统注册的多个组件坑位以及进程,由多个插件共享,坑位被占用时标记为已占用,否则标记为未被占用,当坑位未被占用时则回收该坑,分配给其他插件的相应组件使用。同理,进程的调用也采用同样原理,每个进程对应一个坑位,当坑位被回收时该进程标记为未使用,从而可以分配给其他插件使用。由此,多个插件通过进程和坑位的共享基于宿主程序的实现正常运行。
综上所述,本发明所述方法通过占坑方法向系统注册加载应用程序的各组件,由钩子函数组成的监控模块作为中间代理,启动应用程序的各组件,以及应用程序运行过程中的其他资源调度,实现应用程序借助宿主程序的免安装运行。本发明所述方法使得具有多功能模块的应用程序的各模块之间松耦合关联,简化了应用程序的升级,便于各功能模块的灵活扩展。
以上所述仅是本发明的部分实施方式,应当指出,对于本技术领域的普通技术人员来说,在不脱离本发明原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也应视为本发明的保护范围。