Shadow 插件化框架分析(1),资深Android面试题

│   │   ├── coding // lint
│   │   ├── core
│   │   │   ├── common
│   │   │   ├── gradle-plugin // gradle 插件
│   │   │   ├── load-parameters
│   │   │   ├── loader // 负责加载插件
│   │   │   ├── manager // 装载插件,管理插件
│   │   │   ├── runtime // 插件运行时需要,包括占位 Activity,占位 Provider 等等
│   │   │   ├── transform // Transform 实现,用于替换插件 Activity 父类等等
│   │   │   └── transform-kit
│   │   └── dynamic // 插件自身动态化实现,包括一些接口的抽象

框架主要类说明

PluginContainerActivity
代理 Activity
ShadowActivity
插件 Activity 统一父类,在打包时通过 Transform 统一替换
ComponentManager
管理插件和宿主代理的对应关系
PluginManager
装载插件
PluginLoader
管理插件 Activity 生命周期等等

sample 示例代码 AndroidManifest.xml 分析
注册 sample MainActivity

负责启动插件

注册代理 Activity

注册了三个代理 Activity,分别是 PluginDefaultProxyActivity,PluginSingleInstance1ProxyActivity,PluginSingleTask1ProxyActivity。
可以看到,这三个 Activity 都是继承自 PluginContainerActivity,只是设置了不同的 launchMode,这里就明显的看出来,PluginContainerActivity 就是代理 Activity。

注册代理 Provider

PluginContainerContentProvider 也是代理 Provider。

Activity 实现

关于插件 Activity 的实现,我们主要看两个地方: 0. 替换插件 Activity 的父类

  1. 宿主中如何启动插件 Activity
  2. 插件中如何启动插件 Activity
替换插件 Activity 的父类

Shadow 中有一个比较巧妙的地方,就是插件开发的时候,插件的 Activity 还是正常继承 Activity,在打包的时候,会通过 Transform 替换其父类为 ShadowActivity。如果对 Transform 不太了解,可以看看之前的 Gradle 学习系列文章。 projects/sdk/core/transform 和 projects/sdk/core/transform-kit 两个项目就是 Transform,入口是 ShadowTransform。这里对 Transform 做了一些封装,提供了友好的开发方式,这里就不多做分析了,我们主要看下 TransformManager。

class TransformManager(ctClassInputMap: Map<CtClass, InputClass>,
classPool: ClassPool,
useHostContext: () -> Array
) : AbstractTransformManager(ctClassInputMap, classPool) {

override val mTransformList: List = listOf(
ApplicationTransform(),
ActivityTransform(),
ServiceTransform(),
InstrumentationTransform(),
RemoteViewTransform(),
FragmentTransform(ctClassInputMap),
DialogTransform(),
WebViewTransform(),
ContentProviderTransform(),
PackageManagerTransform(),
KeepHostContextTransform(useHostContext())
)
}

这里的 mTransformList 就是要依次执行的 Transform 内容,也就是需要替换的类映射。我们以 ApplicationTransform 和 ActivityTransform 为例。

class ApplicationTransform : SimpleRenameTransform(
mapOf(
“android.app.Application”
to “com.tencent.shadow.core.runtime.ShadowApplication”
,
“android.app.Application$ActivityLifecycleCallbacks”
to “com.tencent.shadow.core.runtime.ShadowActivityLifecycleCallbacks”
)
)

class ActivityTransform : SimpleRenameTransform(
mapOf(
“android.app.Activity”
to “com.tencent.shadow.core.runtime.ShadowActivity”
)
)

可以看到,打包过程中,插件的 Application 会被替换成 ShadowApplication,Activity 会被替换成 ShadowActivity,这里主要看一下 ShadowActivity 的继承关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为何插件 Activity 可以不用继承 Activity 呢?因为在代理 Activity 的方式中,插件 Activity 是被当作一个普通类来使用的,只要负责执行对应的生命周期即可。

宿主中如何启动插件 Activity

宿主中启动插件 Activity 原理如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们就从 sample 里的 MainActivity 开始看起。 sample-host/src/main/java/com/tencent/shadow/sample/host/MainActivity 是 demo 的主入口。
启动插件的方式是:

startPluginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// …
Intent intent = new Intent(MainActivity.this, PluginLoadActivity.class);
intent.putExtra(Constant.KEY_PLUGIN_PART_KEY, partKey);
intent.putExtra(Constant.KEY_ACTIVITY_CLASSNAME, “com.tencent.shadow.sample.plugin.app.lib.gallery.splash.SplashActivity”);
// …
startActivity(intent);
}
});

可以看到,这里是通过 PluginLoadActivity 来启动的,传入了要启动的插件 Activity:SplashActivity,接着就到 PluginLoadActivity 里看一下具体的启动。

class PluginLoadActivity extends Activity {
public void startPlugin() {
PluginHelper.getInstance().singlePool.execute(new Runnable() {
@Override
public void run() {
HostApplication.getApp().loadPluginManager(PluginHelper.getInstance().pluginManagerFile);
// …
bundle.putString(Constant.KEY_ACTIVITY_CLASSNAME, getIntent().getStringExtra(Constant.KEY_ACTIVITY_CLASSNAME));
HostApplication.getApp().getPluginManager()
.enter(PluginLoadActivity.this, Constant.FROM_ID_START_ACTIVITY, bundle, new EnterCallback() {
@Override
public void onShowLoadingView(final View view) {
// 设置加载的样式
mHandler.post(new Runnable() {
@Override
public void run() {
mViewGroup.addView(view);
}
});
}
// …
});
}
});
}
}

这里可以看到,是通过 HostApplication 获取到 PluginManager,然后调用其 enter 方法,进入插件。这里先看看返回的 PluginManager 是什么。

class HostApplication extends Application {
public void loadPluginManager(File apk) {
if (mPluginManager == null) {
// 创建 PluginManager
mPluginManager = Shadow.getPluginManager(apk);
}
}

public PluginManager getPluginManager() {
return mPluginManager;
}
}

public class Shadow {
public static PluginManager getPluginManager(File apk){
final FixedPathPmUpdater fixedPathPmUpdater = new FixedPathPmUpdater(apk);
File tempPm = fixedPathPmUpdater.getLatest();
if (tempPm != null) {
// 创建 DynamicPluginManager
return new DynamicPluginManager(fixedPathPmUpdater);
}
return null;
}
}

可以看到,HostApplication 里返回的其实是一个 DynamicPluginManager 实例,那么接下来就要看 DynamicPluginManager 的 enter 方法。

class DynamicPluginManager implements PluginManager {
@Override
public void enter(Context context, long fromId, Bundle bundle, EnterCallback callback) {
// 加载 mManagerImpl 实现,这里涉及到了框架的自身动态化,在后面会讲到,这里只要知道,mManagerImpl 最终是 SamplePluginManager 实例即可
updateManagerImpl(context);
// mManagerImpl 是 SamplePluginManager 实例,调用其实现
mManagerImpl.enter(context, fromId, bundle, callback);
mUpdater.update();
}
}

通过上面的代码我们知道了,调用 DynamicPluginManager.enter 会转发到 SamplePluginManager.enter 中去,接着就看看这个实现。

class SamplePluginManager extends FastPluginManager {
public void enter(final Context context, long fromId, Bundle bundle, final EnterCallback callback) {
// …
// 启动 Activity
onStartActivity(context, bundle, callback);
// …
}

private void onStartActivity(final Context context, Bundle bundle, final EnterCallback callback) {
// …
final String className = bundle.getString(Constant.KEY_ACTIVITY_CLASSNAME);
// …
final Bundle extras = bundle.getBundle(Constant.KEY_EXTRAS);
if (callback != null) {
// 创建 loading view
final View view = LayoutInflater.from(mCurrentContext).inflate(R.layout.activity_load_plugin, null);
callback.onShowLoadingView(view);
}
executorService.execute(new Runnable() {
@Override
public void run() {
// …
// 加载插件
InstalledPlugin installedPlugin = installPlugin(pluginZipPath, null, true);
// 创建插件 Intent
Intent pluginIntent = new Intent();
pluginIntent.setClassName(
context.getPackageName(),
className
);
if (extras != null) {
pluginIntent.replaceExtras(extras);
}
// 启动插件 Activity
startPluginActivity(context, installedPlugin, partKey, pluginIntent);
// …
}
});
}
}

在 SamplePluginManager.enter 中,调用 onStartActivity 启动插件 Activity,其中开线程去加载插件,然后调用 startPluginActivity。
startPluginActivity 实现在其父类 FastPluginManager 里。

class FastPluginManager {
public void startPluginActivity(Context context, InstalledPlugin installedPlugin, String partKey, Intent pluginIntent) throws RemoteException, TimeoutException, FailedException {
Intent intent = convertActivityIntent(installedPlugin, partKey, pluginIntent);
if (!(context instanceof Activity)) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
}

其中的重点是 convertActivityIntent,将插件 intent 转化成宿主的 intent,然后调用 系统的 context.startActivity 启动插件。这里的 context 是 PluginLoadActivity.this,从其 enter 方法中一直传进来的。
下面重点看看 convertActivityIntent 的实现。

class FastPluginManager {
public Intent convertActivityIntent(InstalledPlugin installedPlugin, String partKey, Intent pluginIntent) throws RemoteException, TimeoutException, FailedException {
// 创建 mPluginLoader
loadPlugin(installedPlugin.UUID, partKey);
// 先调用 Application onCreate 方法
mPluginLoader.callApplicationOnCreate(partKey);
// 转化插件 intent 为 代理 Activity intent
return mPluginLoader.convertActivityIntent(pluginIntent);
}
}

到了这里其实有一些复杂了,因为 mPluginLoader 是通过 Binder 去调用相关方法的。由于这里涉及到了 Binder 的使用,需要读者了解 Binder 相关的知识,代码比较繁琐,这里就不具体分析代码实现了,用一张图理顺一下对应的关系:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过上面的 Binder 对应图,我们可以简单的理解为,调用 mPluginLoader 中的方法,就是调用 DynamicPluginLoader 中的方法,调用 mPpsController 的方法,就是调用 PluginProcessService 中的方法。
所以这里的 mPluginLoader.convertActivityIntent 相当于调用了 DynamicPluginLoader.convertActivityIntent。

internal class DynamicPluginLoader(hostContext: Context, uuid: String) {
fun convertActivityIntent(pluginActivityIntent: Intent): Intent? {
return mPluginLoader.mComponentManager.convertPluginActivityIntent(pluginActivityIntent)
}
}

调用到了 ComponentManager.convertPluginActivityIntent 方法。

abstract class ComponentManager : PluginComponentLauncher {
override fun convertPluginActivityIntent(pluginIntent: Intent): Intent {
return if (pluginIntent.isPluginComponent()) {
pluginIntent.toActivityContainerIntent()
} else {
pluginIntent
}
}

private fun Intent.toActivityContainerIntent(): Intent {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

要如何成为Android架构师?

搭建自己的知识框架,全面提升自己的技术体系,并且往底层源码方向深入钻研。
大多数技术人喜欢用思维脑图来构建自己的知识体系,一目了然。这里给大家分享一份大厂主流的Android架构师技术体系,可以用来搭建自己的知识框架,或者查漏补缺;

对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,在这里点击GitHub免费分享,希望能帮你突破瓶颈,跳槽进大厂;

最后我必须强调几点:

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。

你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境

同学,在这里点击GitHub免费分享,希望能帮你突破瓶颈,跳槽进大厂;

最后我必须强调几点:

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。

你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值