• 面试:插件化相关---service


    插件service启动分析

    同样的,先来看看service的常规启动流程

    • 调用contextimpl.startService/bindService/stopService -> AMS,AMS对应创建ServiceRecord和token后,通知ActivityThread
    • ActivityThread收到startService后,会创建service并保存到mService的ArrayMap,key为token,接着调用oncreate
    • ActivityThread接着收到handleServiceArgs, 根据token拿到service,接着调用onStartCommond并传入intent
    • ActivityThread收到bindservice后,从根据token拿到service,接着调用onBind拿到native binder,接着调用publishService将native binder传到AMS

    Service跟Activity还是存在很大的区别的,service非常独立,也就是说,系统创建service后,除了调用规定的那些回调,传递intent外,剩下就是service自己玩自己的,跟系统一毛钱关系都没有了。

    Service无法拥有多实例

    Service组件与Activity组件另外一个不同点在于,对同一个Service调用多次startService并不会启动多个Service实例,而非特定Flag的Activity是可以允许这种情况存在的,因此如果用StubService的方式,为了实现Service的这种特性,必须建立一个StubService到插件Service的一个Map,Map的这种一一对应关系使得我们使用一个StubService对应多个插件Service的计划成为天方夜谭。

    至此,结论已经非常清晰——对于Service组件的插件化,我们不能简单地套用Activity的方案。

    对于DroidPlugin来说,插件service的hook,则会简单很多,只需要用一个stub service做为代理,在stubservice内部根据传入的intent去管理插件service对象即可

    在startservice和bindservice时,只需要把目标sevice缓存stubservice,并将真实的intent作为extra传递到stub service就可以了

    通过反射调用ActivityThrea的方法创建service,从而达到调用oncreate的目地

    如何实现Service的插件化?

    上文指出,我们不能套用Activity的方案实现Service组件的插件化,可以通过手动控制Service组件的生命周期实现;我们先来看一下Service的生命周期:

    从图中可以看出,Service的生命周期相当简单:整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。对于非绑定服务,就是从startService调用到stopService或者stopSelf调用。对于绑定服务,就是bindService调用到unbindService调用;

    如果要手动控制Service组件的生命周期,我们只需要模拟出这个过程即可;而实现这一点并不复杂:

    1. 如果以startService方式启动插件Service,直接回调要启动的Service对象的onStartCommand方法即可;如果用stopService或者stopSelf的方式停止Service,只需要回调对应的Service组件的onDestroy方法。
    2. 如果用bindService方式绑定插件Service,可以调用对应Service对应的onBind方法,获取onBind方法返回的Binder对象,然后通过ServiceConnection对象进行回调统计;unBindService的实现同理。

    完全手动控制

    现在我们已经有了实现思路,那么具体如何实现呢?

    我们必须在startService,stopService等方法被调用的时候拿到控制权,才能手动去控制Service的生命周期;要达到这一目的非常简单——Hook ActivityManagerNative即可。在Activity的插件化方案中我们就通过这种方式接管了startActivity调用,相信读者并不陌生。

    我们Hook掉ActivityManagerNative之后,可以拦截对于startService以及stopService等方法的调用;拦截之后,我们可以直接对插件Service进行操作:

    1. 拦截到startService之后,如果Service还没有创建就直接创建Service对象(可能需要加载插件),然后调用这个Service的onCreate,onStartCommond方法;如果Service已经创建,获取到原来创建的Service对象并执行其onStartCommand方法。
    2. 拦截到stopService之后,获取到对应的Service对象,直接调用这个Service的onDestroy方法。

    这种方案简直简单得让人不敢相信!很可惜,这么干是不行的。

    首先,Service存在的意义在于它作为一个后台任务,拥有相对较高运行时优先级;除非在内存及其不足威胁到前台Activity的时候,这个组件才会被系统杀死。上述这种实现完全把Service当作一个普通的Java对象使用了,因此并没有完全实现Service所具备的能力。

    其次,Activity以及Service等组件是可以指定进程的,而让Service运行在某个特定进程的情况非常常见——所谓的远程Service;用上述这种办法压根儿没有办法让某个Service对象运行在一个别的进程。Android系统给开发者控制进程的机会太少了,要么在AndroidManifest.xml中通过process属性指定,要么借助Java的Runtime类或者native的fork;这几种方式都无法让我们以一种简单的方式配合上述方案达到目的。

    代理分发技术

    既然我们希望插件的Service具有一定的运行时优先级,那么一个货真价实的Service组件是必不可少的——只有这种被系统认可的真正的Service组件才具有所谓的运行时优先级。

    因此,我们可以注册一个真正的Service组件ProxyService,让这个Service承载一个真正的Service组件所具备的能力(进程优先级等);当启动插件的服务比如PluginService的时候,我们统一启动这个ProxyService,当这个ProxyService运行起来之后,再在它的onStartCommand等方法里面进行分发,执行PluginService的onStartCommond等对应的方法;我们把这种方案形象地称为「代理分发技术」

    代理分发技术也可以完美解决插件Service可以运行在不同的进程的问题——我们可以在AndroidManifest.xml中注册多个ProxyService,指定它们的process属性,让它们运行在不同的进程;当启动的插件Service希望运行在一个新的进程时,我们可以选择某一个合适的ProxyService进行分发。也许有童鞋会说,那得注册多少个ProxyService才能满足需求啊?理论上确实存在这问题,但事实上,一个App使用超过10个进程的几乎没有;因此这种方案是可行的。

    Android 插件化原理解析——Service的插件化 - 腾讯云开发者社区-腾讯云

    插件化-插件Service的运行管理 - 简书

    如何评价360的Android插件化框架RePlugin? - 知乎

  • 相关阅读:
    JVM学习----垃圾回收
    Spring默认缓存的使用-Simple
    Java运算符及流程控制
    Go语言入门篇
    Java集合(一)
    矩阵存储和特殊矩阵的压缩存储
    来了,永久免费的图床服务
    LVGL---进度条(lv_bar)
    深入了解Redis的TYPE命令
    4核16G服务器价格腾讯云PK阿里云
  • 原文地址:https://blog.csdn.net/cpcpcp123/article/details/128109219