Android PluginManager 源码解析4--ActivityOverider


ActivityOverider概述

ActivityOverider负责与动态生成的PluginActivity交互。

ActivityOverider主要干了3件事:

  • 修正StartActivity的Intent
  • 动态生成PluginActivity的Dex文件
  • 处理PluginActivity生命周期的回调,根据需要做一些特殊处理

overrideStartActivityForResult方法解析

由于插件内的Activity没有在AndroidManifest.xml中注册,只注册了androidx.pluginmgr.PluginActivity,当通过Context.startActivtiyForResult()调起目标Activity时会找不到,所以需要对系统的Context.startActivtiyForResult()进行劫持,把要启动的Activity指向androidx.pluginmgr.PluginActivity,当加载PluginActivity时,通过FrameworkClassLoader.loadClass()方法再把类的加载路径修正回目标Activity,对系统进行了一个瞒天过海,骗过单纯的系统,哈哈。

startActivity可能启动的是插件内部的Activity,也可能是宿主的Activity,也可能是其它插件的Activity,这一切都交由overrideStartActivityForResult()方法处理。

  • 启动明确指定类名的Activity

...
		
// 取出ComponentName
ComponentName compname = intent.getComponent();
// 取包名
String pkg = compname.getPackageName();
// 取Activity类名
String toActName = compname.getClassName();
// 根据插件id取缓存PluginInfo
PlugInfo thisPlugin = mgr.getPluginById(pluginId);
ActivityInfo actInThisApk = null;
PlugInfo plug = thisPlugin;
if (pkg != null) {
	// 启动同一插件内的Activity
	if (pkg.equals(thisPlugin.getPackageName())) {
		actInThisApk = thisPlugin
				.findActivityByClassName(toActName);
	// 启动另一插件内的Activity
	}else{
		PlugInfo otherPlug = mgr.getPluginByPackageName(pkg);
		if (otherPlug != null) {
			plug = otherPlug;
			actInThisApk = otherPlug
					.findActivityByClassName(toActName);
		}
	}
} else {
	// 容错处理,启动同一插件内的Activity
	actInThisApk = thisPlugin.findActivityByClassName(toActName);
}

...

setPluginIntent(intent, plug, actInThisApk.name);

...
  • 启动有具体Action的Activity


String action = intent.getAction();
PlugInfo thisPlugin = mgr.getPluginById(pluginId);
ActivityInfo actInThisApk = thisPlugin.findActivityByAction(action);
			
...

setPluginIntent(intent, thisPlugin, actInThisApk.name);

...
  • 偷天换日,修改Intent


private static void setPluginIntent(Intent intent, PlugInfo plugin,
			String actName) {
	PluginManager mgr = PluginManager.getInstance();
	String pluginId = plugin.getId();
	// 根据类名动态生成新的PluginActivity
	createProxyDex(plugin, actName);
	// 修改Intent中的ComponentName,将其指向androidx.pluginmgr.PluginActivity
	String act = mgr.getFrameworkClassLoader().newActivityClassName(
				pluginId, actName);
	ComponentName compname = new ComponentName(mgr.getContext(), act);
	intent.setComponent(compname);
}

动态生成PluginActivity

通过createProxyDex()为要加载的Activity生成代理类

try {
	String pkgName = plugin.getPackageName();
	ActivityClassGenerator.createActivityDex(activity, targetClassName,
			saveDir, plugin.getId(), pkgName);
} catch (Throwable e) {
	Log.e(tag, Log.getStackTraceString(e));
}

具体生成机制委托给ActivityClassGenerator实现,后面再分析。

Activity生命周期回调

PluginActivity的一些生命周期方法会回调到ActivityOverider里。

比较重要的几个有overrideAttachBaseContext()callback_onCreate()

先看overrideAttachBaseContext()

overrideAttachBaseContext()


PlugInfo plugin = PluginManager.getInstance().getPluginById(pluginId);
if (plugin.getApplication() == null) {
	try {
		PluginManager.getInstance().initPluginApplication(plugin,
				null);
	} catch (Exception e) {
		Log.e(tag, Log.getStackTraceString(e));
	}
}
PluginActivityWrapper actWrapper = new PluginActivityWrapper(base, plugin.appWrapper, plugin);
return new Object[] { actWrapper, plugin.getAssetManager() };

overrideAttachBaseContext()方法是为了更改Activity中的Context,把默认宿主的Context替换为修改过后的插件Context。

由于Activity是继承自ContextThemeWrapper,而Application是继承ContextWrapper,所以这里不能直接使用PluginContextWrapper,而是把在Application初始化时生成的PluginContextWrapper注入继承自ContextThemeWrapperPluginActivityWrapper

这样插件Application的Context与插件Activity的Context便在行为上一致了。

接着来看callback_onCreate()

callback_onCreate()

  • callback_onCreate()先替换了Activity的Application。

因为PluginActivity实际还是在宿主中调起的,所以默认的Context、Application都是宿主的。既然Context已经替换了,那Application也不能漏掉。

...
		
try {
	Field applicationField = Activity.class
			.getDeclaredField("mApplication");
	applicationField.setAccessible(true);
	applicationField.set(fromAct, plugin.getApplication());
}  catch (Exception e) {
	e.printStackTrace();
}

...
  • 处理activity的主题
...
		
// PluginActivity是动态生成并继承与原来的Activity,所以此处需要取supperClass的name
String actName = fromAct.getClass().getSuperclass().getName();
// 从通过PluginManifestUtil解析的Activity list中查找当前Activity的ActivityInfo
ActivityInfo actInfo = plugin.findActivityByClassName(actName);
int resTheme = actInfo.getThemeResource();
// 如果使用了主题
if (resTheme != 0) {
	boolean hasNotSetTheme = true;
	try {
		Field mTheme = ContextThemeWrapper.class
				.getDeclaredField("mTheme");
		mTheme.setAccessible(true);
		// 判断PluginActivity的mTheme字段是否为空,为空表示没有设置过主题
		hasNotSetTheme = mTheme.get(fromAct) == null;
	} catch (Exception e) {
		e.printStackTrace();
	}
	if (hasNotSetTheme) {
		// 替换PluginActivity的mActivityInfo
		changeActivityInfo(fromAct);
		fromAct.setTheme(resTheme);
	}
}

...

这里为何在设置了主题后才替换mActivityInfo,暂时还没有看懂。

  • 替换PluginActivity的mActivityInfo
...

Field field_mActivityInfo=null;
try {
	field_mActivityInfo = Activity.class.getDeclaredField("mActivityInfo");
	field_mActivityInfo.setAccessible(true);
}  catch (Exception e) {
	Log.e(tag, Log.getStackTraceString(e));
	return;
}
PluginManager con = PluginManager.getInstance();
PlugInfo plugin = con.getPluginByPackageName(activity.getPackageName());

ActivityInfo actInfo = plugin.findActivityByClassName(actName);
actInfo.applicationInfo = plugin.getPackageInfo().applicationInfo;
try {
	field_mActivityInfo.set(activity, actInfo);
} catch (Exception e) {
	Log.e(tag, Log.getStackTraceString(e));
}

...
  • 处理回调

PluginActivityLifeCycleCallback callback = PluginManager.getInstance()
		.getPluginActivityLifeCycleCallback();
if (callback != null) {
	callback.onCreate(pluginId, fromAct);
}

如果注册了PluginActivityLifeCycleCallback,会在相应的生命周期内触发回调。

剩下一些其他的生命周期没有做额外的处理,只是处理了PluginActivityLifeCycleCallback回调,就略过了。


文章作者: Caden
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Caden !
  目录