try {
//看到这里是不是更加熟悉了,没错加载Activity类的ClassLoader也是从这个LoadedApk中获取的
//这里当然也是一样的,所以在后续Service插件化的实现中我们可以直接复用实现Activity插件中的相关代码
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//这里的最终实现就是痛过对应的ClassLoader加载名字为data.info.name对应的Service了
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
- ": " + 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以欺骗AMS
ComponentName 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了,用于后续加载插件对应的Service
serviceInfo.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中热修复实现的方式之一。对应的实现如下:
public static void mergeDex(ClassLoader classLoader, File apkFile, File optDexfile) {
try {
Object pathListObj = RefInvoke.getFieldValue(RefInvoke.getField(BaseDexClassLoader.class, “pathList”), classLoader);
if (null == pathListObj) {
Log.i(TAG, “get path list failed”);
return;
}
Field dexElementsField = RefInvoke.getField(pathListObj.getClass(), “dexElements”);
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
的学习之门**
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!