Android 插件化-Service 篇,flutter 通知推送
": " + e.toString(), e);}}
try {if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);//初始化 Service,并调用其 onCreate 方法 service.attach(context, this, data.info.name, data.token, app,ActivityManager.getService());service.onCreate();//保存当前创建的 Service 对应,用于后续执行 onStart、onBind、unBind 以及 onDestroy 等方法 mServices.put(data.token, service);try {ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to create service " + data.info.name
": " + e.toString(), e);}}}
2、其他生命周期方法
Service 的其它相关生命期方法与ActivityThread.java源码中方法实现分别对应关系如下:
onStartCommand:handleServiceArgs
onBind:handleBindService
onUnbind:handleUnbindService
onDestroy:handleStopService
看完源码实现之后你会发现,上述的几个生命周期方法所操作的 Service 实例对象都是在
方法 handleCreateService 中所创建的对象。因此对于 Service 的插件化实现我们只需要拦截方法 handleCreateService 就足以。
三、Service 插件化的实现
1、实现步骤
有了上述源码的理解分析以及上一篇所实现Activity插件化的加持,对于 Service 插件化的实现那简直不要太简单。大致可以分为如下几个步骤:
第一步复用 Activity 插件化中所实现的 AMNHook 以及 ActivityThread 中 Handler 的 hook
第二步就是在 hook 的 AMN 中对操作 Service 的相关方法 startService、startService 以及 bindService 三个方法进行拦截,并将要启动的 Service 替换成在宿主中所预埋的 Service;
第三步就是在所 hook 的 Handler 中对方法 handleCreateService 进行拦截了。
2、代码实现
因为 AMN 以及对应 Handler 的 hook 在Activity篇中有做注解,所以这里就不做过多的赘述了, 我们直接跳到第二步的代码实现。
/***
@param realIntent 上层应用所构造 Intent 对象
@param name 上层应用所调用的方法,没啥实际用处,只是为了打印*/private void hookServiceOperate(Intent realIntent, String name) {if (null != realIntent) {ComponentName pluginComponentName = realIntent.getComponent();if (null != pluginComponentName) {String pluginServiceName = pluginComponentName.getClassName();//获取要启动的插件 Service 所对应的宿主 Service 名字哦 String hostServiceName = HostToPluginMapping.getHostService(pluginServiceName);if (!TextUtils.isEmpty(hostServiceName)) {Log.i(TAG, "current is hooking " + name);//将实际要启动的 Service 替换成宿主中 Service 以欺骗 AMSComponentName componentName = new ComponentName(pluginComponentName.getPackageName(), hostServiceName);realIntent.setComponent(componentName);}}}}
看着是不是 so easy,简单来说就是吧上层应用需要启动的插件 Service 替换成宿主中预埋的 Service 以欺骗 AMS;
最后就是在所 hook 的 Handler 中拦截方法 handleCreateService 了,对应的实现如下:
public void handleCreateService(Object object) {try {//首先获取从 AMS 传递过来的 CreateServiceData 对象中的 ServiceInfo 属性 ServiceInfo serviceInfo = (ServiceInfo) RefInvoke.getFieldValue(RefInvoke.getField(object.getClass(), "info"), object);String hostServiceName = serviceInfo.name;String pluginServiceName = HostToPluginMapping.getPluginService(hostServiceName);if (TextUtils.isEmpty(pluginServiceName)) {Log.i(TAG, "not found host service,so no need replace");return;}String path = DePluginSP.getInstance(DePluginApplication.getContext()).getString(Constants.COPY_FILE_PATH, "");//接着就是复用 Activity 插件化实现中的生成 LoadedApk 对象,并将其中的 ClassLoader 替换成加载对应插件的 ClassLoader 了 replaceClassloader(path);
//接着就是将 ServiceInfo 中的 ServiceName 替换成对应插件的 ServiceName 了,用于后续加载插件对应的 ServiceserviceInfo.name = pluginServiceName;serviceInfo.applicationInfo.packageName = mPathToPluginNameMap.get(path);Log.i(TAG, "replaced to plugin service success");} catch (Exception e) {Log.i(TAG, "handle create service failed");}......//最后就是继续调用到 ActivityThread 中的 handleCreateService 方法生成对应的 Service 对象,并做初始化等动作}
3、mergeDex
当然对于插件 Service 类的加载其实不用生成 ClassLoader 这么麻烦,我们可以将插件对应的 dex 文件通过 mergeDex 的方式添加到 BaseDexClassLoader 的 pathList 中,这样就能够直接通过当前应用默认的 ClassLoader 进行加载了,这也是 Android 中热修复实现的方式之一。对应的实现如下:
评论