autojs打包apk源码提取_腾讯插件框架Shadow源码分析
前⾔:Android的插件化开发经过这么多年的发展,已经⽐较成熟,也诞⽣了很多优秀的插件框架,⽐如VirtualApk、RePlugin、
前⾔:
Shadow等。其实所有⽀持四⼤组件的插件框架,都在解决⼀个问题,就是绕过Manifest注册表校验。传统的做法就是通过Hook技术,欺骗Android系统,注册表插桩,让系统认为启动的是插桩的四⼤组件,实际loadclass插件的类。
Shadow可以说算是另辟蹊径,开源之初,对外宣传的是零Hook,可关键其实源码⾥是有⼀处Hook点的,对此官⽅也解释了,并⾮必须。那么Shadow是如何“骗”系统的呢?这其实也就是Shadow设计的巧妙之处,后⽂会进⾏分析。
关于Shadow在分析前,有⼏个点我觉得有必要搞清楚:
1、Shadow是跨进程的,插件运⾏在插件进程,通过Binder机制通信,所以不了解Binder的,建议提前熟悉⼀下,否则看着会⽐较绕。
2、Shadow的宿主和业务插件之间还有⼀层中间层,中间层也是以插件的形式加载,同时可以升级,有较强的灵活性。
3、插件⾥写⼀个页⾯,⽐如继承⾃Activity,我们可以正常写,但是在编译期会修改继承关系,将其⽗类改为
ShadowActivity,ShadowActivity实际上不是⼀个Activity,他持有HostActivity的代理对象,依赖此完成⽣命周期的回调。
这个操作是靠修改字节码实现的,⾃定义gradle脚本,通过javassist或者asm都可以实现,不再赘述。
4、我纯属因为感兴趣所有阅读了源码,实际上并没有应⽤在⽣产环境,毕竟推进公司框架层⾯的修改不是那么容易,⽽且也不⼀定合适。各位在选择的时候,也要考虑全⾯,适合⾃⼰公司业务的解决⽅案才是最好的。
思考:
虽然Shadow与传统插件框架的实现⽅式不同,但有⼀些基本流程还是⼀致的。⽐如插件的下载、安装、更新、卸载。解析插件apk,插件四⼤组件的信息解析及缓存,插件的ClassLoader、Resource的处理等。
Shadow不⼀样的点前⾔已经介绍,所以可以想到,第⼀次加载业务插件,⾸先会加载中间层插件。要想代理Activity(ProxyActivity)和插件Activity(PluginActivity)关联,同时ProxyActivity的⽣命周期⽅法能调⽤到PluginActivity,那么ProxyActivity必然会持有PluginActivity实例话对象的引⽤。PluginActivity⾥有具体的业务实现,同时需要回调ProxyActivity的⽣命周期⽅法,那么PluginActivity也会有⼀个有此能⼒的代理对象。
再次强调,PluginActivity并不是⼀个真实的Activity,不要被Demo⾥的源码所欺骗,之所以要在编译期修改继承关系,好处就是在开发阶段,我们可以按照⼀个真实的Activity的写法去开发,剩下的事⼉由框架处理;另外插件也是可以独⽴运⾏的。
可能放两张图可以表达的更清楚:
下⾯是我反编译插件apk得到的代码
源码分析:
Shadow源码较多,我们只分析⼀下插件Activity是如何启动及运⾏的。
可以先看⼀下打包出来的apk的结构
我的理解pluginmanager.apk loader.apk runtime.apk是中间层
config.json 是发版信息,主要⽤于检查更新,其中的uuid即为当前版本的唯⼀标⽰
HostApplication的onCreate⽅法会有⼀些初始化的⼯作,主要是把asset⽬录下的插件复制到指定⽬录,还有runtime插件的状态恢复,⾮核⼼流程,不再详述。
我们直接看启动插件的逻辑,很容易就到加载插件的缺省页PluginLoadActivity,只有⼀个startPlugin⽅法:
public void startPlugin() {
@Override
public void run() {
// ⽅法名虽然叫loadPluginManager,实际上并没有真正安装manager插件,
// 只是将插件路径包装成FixedPathPmUpdater,作为构造函数的参数,创建⼀个DynamicPluginManager保存在Application中
Bundle bundle = new Bundle();
//插件的安装路径
bundle.putString(Constant.KEY_PLUGIN_ZIP_PATH, Instance().AbsolutePath());
//当前值是:sample-plugin-app
bundle.putString(Constant.KEY_PLUGIN_PART_KEY, getIntent().getStringExtra(Constant.KEY_PLUGIN_PART_KEY));
//要启动的插件中的Activity路径 t.shadow.sample.plugin.app.lib.gallery.splash.SplashActivity
bundle.putString(Constant.KEY_ACTIVITY_CLASSNAME, getIntent().getStringExtra(Constant.KEY_ACTIVITY_CLASSNAME));
//EnterCallback主要是⽤于处理插件加载过程中的过度状态
.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);
mui框架常用组件有哪些}
});
}
@Override
public void onCloseLoadingView() {
finish();
}
@Override
public void onEnterComplete() {
}
});
}
});
}
懒的长篇⼤论,相关逻辑已经写在注释⾥,会执⾏到DynamicPluginManager的enter⽅法:
public void enter(Context context, long fromId, Bundle bundle, EnterCallback callback) {
if (mLogger.isInfoEnabled()) {
mLogger.info("enter fromId:" + fromId + " callback:" + callback);
}
//动态管理插件的更新逻辑
updateManagerImpl(context);
//mManagerImpl的类型是SamplePluginManager
<(context, fromId, bundle, callback);
mUpdater.update();
}
mManagerImpl是⼀个接⼝,上⾯的代码其真实实例是SamplePluginManager,updateManagerImpl⽅法会安装pluginmanager.apk 插件,同时通过反射创建⼀个SamplePluginManager实例,也就是上⾯的mManagerImpl,同时⽀持pluginmanager.apk插件的更新逻辑。
所以进⼊SamplePluginManager的enter->onStartActivity,代码逻辑⽐较简单,没什么可说的,需要注意⼀点是会启动⼀个线程,去加载zip包下的⼏个插件(runtime、loader、业务插件),⽽后会调⽤到其⽗类FastPluginManager的startPluginActivity⽅法:
public void startPluginActivity(Context context, InstalledPlugin installedPlugin, String partKey, Intent pluginIntent) throws RemoteException, TimeoutExcept        Intent intent = convertActivityIntent(installedPlugin, partKey, pluginIntent);
if (!(context instanceof Activity)) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
//最终启动的是t.shadow.sample.plugin.runtime.PluginDefaultProxyActivity
//PluginDefaultProxyActivity 在宿主manifest中有注册
context.startActivity(intent);
}
核⼼流程就在convertActivityIntent
convertActivityIntent⾥,从命名就可以看出来,最终会把我们要启动的插件Activity,映射成⼀个在Ma
nifest⾥注册的
真实Activity,也就是注释中标注的PluginDefaultProxyActivity。
可以回看⼀下上⽂“思考”中的内容,即为Shadow第⼀次使⽤插件的主要流程,convertActivityIntent的代码如下:
public Intent convertActivityIntent(InstalledPlugin installedPlugin, String partKey, Intent pluginIntent) throws RemoteException, TimeoutException, FailedEx        //这个partKey的真实值是"sample-plugin-app"
loadPlugin(installedPlugin.UUID, partKey);
Map map = LoadedPlugin();
Boolean isCall = (Boolean) (partKey);
if (isCall == null || !isCall) {
//其持有的是PluginLoaderBinder的引⽤
/
/这⾥⼜是⼀次跨进程通信
mPluginLoader.callApplicationOnCreate(partKey);
}
vertActivityIntent(pluginIntent);
}
loadPlugin:先安装中间层插件再安装业务插件,当然如果已安装,直接跳过
mPluginLoader:是⼀个⽐较关键的变量,具体他是什么初始化的,下⾯会具体分析
后续的代码执⾏逻辑可⾃⾏看源码,⾸先会执⾏loadPluginLoaderAndRuntime⽅法,这个⽅法⾥会初始化插件进程的服务,同时将插件进程的binder对象赋值给mPpsController:
private void loadPluginLoaderAndRuntime(String uuid, String partKey) throws RemoteException, TimeoutException, FailedException {
if (mPpsController == null) {
//partKey是启动插件的时候在PluginLoadActivity中赋值
//getPluginProcessServiceName 获取插件进程服务的名字
//bindPluginProcessService启动插件进程服务由此可见,shadow宿主和插件的信息传递是进程间通信的过程
bindPluginProcessService(getPluginProcessServiceName(partKey));
//等待链接超时时间
waitServiceConnected(10, TimeUnit.SECONDS);
}
loadRunTime(uuid);
loadPluginLoader(uuid);
}
......
/**
* 启动PluginProcessService
*
* @param serviceName 注册在宿主中的插件进程管理service完整名字
*/
public final void bindPluginProcessService(final String serviceName) {
if (()) {
if (mLogger.isInfoEnabled()) {
mLogger.info("pps service connecting");
}
return;
return;
}
if (mLogger.isInfoEnabled()) {
mLogger.info("bindPluginProcessService " + serviceName);
}
mConnectCountDownLatch.set(new CountDownLatch(1));
mServiceConnecting.set(true);
//CountDownLatch是⼀个同步⼯具,协调多个线程之间的同步
//可以看下这篇⽂章 wwwblogs/Lee_xy_z/p/10470181.html
final CountDownLatch startBindingLatch = new CountDownLatch(1);
final boolean[] asyncResult = new boolean[1];
/
/从onStartActivity⽅法可知,当前线程并不是UI线程
mUiHandler.post(new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
//serviceName的值是t.shadow.sample.host.PluginProcessPPS
intent.setComponent(new ComponentName(mHostContext, serviceName));
boolean binding = mHostContext.bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {//service对应的是PluginProcessService中的mPpsControllerBinder                        if (mLogger.isInfoEnabled()) {
mLogger.info("onServiceConnected connectCountDownLatch:" + mConnectCountDownLatch);
}
mServiceConnecting.set(false);
mPpsController = PluginProcessService.wrapBinder(service);
try {
//跨进程执⾏PluginProcessService的setUuidManager⽅法
//UuidManagerBinder内部封装了三个⽅法,可以让插件进程拿到loader、runtime及指定其他业务插件的相关信息
mPpsController.setUuidManager(new UuidManagerBinder(PluginManagerThatUseDynamicLoader.this));
} catch (DeadObjectException e) {
if (mLogger.isErrorEnabled()) {
<("onServiceConnected RemoteException:" + e);
}
} catch (RemoteException e) {
if (e.getClass().getSimpleName().equals("TransactionTooLargeException")) {
if (mLogger.isErrorEnabled()) {
<("onServiceConnected TransactionTooLargeException:" + e);
}
} else {
throw new RuntimeException(e);
}
}
try {
/
/第⼀次拿到的是⼀个null
IBinder iBinder = PluginLoader();
if (iBinder != null) {
mPluginLoader = new BinderPluginLoader(iBinder);
}
} catch (RemoteException ignored) {
if (mLogger.isErrorEnabled()) {
<("onServiceConnected mPpsController getPluginLoader:", ignored);
}
}
<().countDown();
if (mLogger.isInfoEnabled()) {
mLogger.info("onServiceConnected countDown:" + mConnectCountDownLatch);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (mLogger.isInfoEnabled()) {
mLogger.info("onServiceDisconnected");

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。