当前位置:   article > 正文

我的Android重构之旅:插件化篇,android程序设计基础_android 插件化

android 插件化

Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
if (baseInstrumentation.getClass().getName().contains(“lbe”)) {
// reject executing in paralell space, for example, lbe.
System.exit(0);
}
// 用于处理替换 Activity 的名称
final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
Object activityThread = ReflectUtil.getActivityThread(this.mContext);
// Hook Instrumentation 替换 Activity 名称
ReflectUtil.setInstrumentation(activityThread, instrumentation);
// Hook handleLaunchActivity
ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
this.mInstrumentation = instrumentation;
} catch (Exception e) {
e.printStackTrace();
}
}

上面我们已经成功的 Hook 了 Instrumentation ,接下来就是需要我们的李鬼上场了

public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
// 只有是插件中的Activity 才进行替换
if (intent.getComponent() != null) {
Log.i(TAG, String.format(“execStartActivity[%s : %s]”, intent.getComponent().getPackageName(),
intent.getComponent().getClassName()));
// 使用"李鬼"进行替换
this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
}
ActivityResult result = realExecStartActivity(who, contextThread, token, target,
intent, requestCode, options);
return result;
}

我们来看一看 markIntentIfNeeded(intent); 到底做了什么

public void markIntentIfNeeded(Intent intent) {
if (intent.getComponent() == null) {
return;
}
String targetPackageName = intent.getComponent().getPackageName();
String targetClassName = intent.getComponent().getClassName();
// 保存我们原有数据
if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
intent.putExtra(Constants.KEY_IS_PLUGIN, true);
intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
dispatchStubActivity(intent);
}
}

private void dispatchStubActivity(Intent intent) {
ComponentName component = intent.getComponent();
String targetClassName = intent.getComponent().getClassName();
LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
ActivityInfo info = loadedPlugin.getActivityInfo(component);
// 判断是否是插件中的Activity
if (info == null) {
throw new RuntimeException("can not find " + component);
}
int launchMode = info.launchMode;
// 并入主题
Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
themeObj.applyStyle(info.theme, true);
// 将插件中的 Activity 替换为占坑的 Activity
String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
Log.i(TAG, String.format(“dispatchStubActivity,[%s -> %s]”, targetClassName, stubActivity));
intent.setClassName(mContext, stubActivity);
}

可以看到上面将我们原本的信息保存至 Intent 中,然后调用了 getStubActivity(targetClassName, launchMode, themeObj); 进行了替换

public static final String STUB_ACTIVITY_STANDARD = “%s.A p u b l i c s t a t i c f i n a l S t r i n g S T U B A C T I V I T Y S I N G L E T O P = " %d"; public static final String STUB_ACTIVITY_SINGLETOP = "%s.B publicstaticfinalStringSTUBACTIVITYSINGLETOP="%d”;
public static final String STUB_ACTIVITY_SINGLETASK = “%s.C p u b l i c s t a t i c f i n a l S t r i n g S T U B A C T I V I T Y S I N G L E I N S T A N C E = " %d"; public static final String STUB_ACTIVITY_SINGLEINSTANCE = "%s.D publicstaticfinalStringSTUBACTIVITYSINGLEINSTANCE="%d”;

public String getStubActivity(String className, int launchMode, Theme theme) {
String stubActivity= mCachedStubActivity.get(className);
if (stubActivity != null) {
return stubActivity;
}

TypedArray array = theme.obtainStyledAttributes(new int[]{
android.R.attr.windowIsTranslucent,
android.R.attr.windowBackground
});
boolean windowIsTranslucent = array.getBoolean(0, false);
array.recycle();
if (Constants.DEBUG) {
Log.d(“StubActivityInfo”, "getStubActivity, is transparent theme ? " + windowIsTranslucent);
}
stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, usedStandardStubActivity);
switch (launchMode) {
case ActivityInfo.LAUNCH_MULTIPLE: {
stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, usedStandardStubActivity);
if (windowIsTranslucent) {
stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, 2);
}
break;
}
case ActivityInfo.LAUNCH_SINGLE_TOP: {
usedSingleTopStubActivity = usedSingleTopStubActivity % MAX_COUNT_SINGLETOP + 1;
stubActivity = String.format(STUB_ACTIVITY_SINGLETOP, corePackage, usedSingleTopStubActivity);
break;
}
case ActivityInfo.LAUNCH_SINGLE_TASK: {
usedSingleTaskStubActivity = usedSingleTaskStubActivity % MAX_COUNT_SINGLETASK + 1;
stubActivity = String.format(STUB_ACTIVITY_SINGLETASK, corePackage, usedSingleTaskStubActivity);
break;
}
case ActivityInfo.LAUNCH_SINGLE_INSTANCE: {
usedSingleInstanceStubActivity = usedSingleInstanceStubActivity % MAX_COUNT_SINGLEINSTANCE + 1;
stubActivity = String.format(STUB_ACTIVITY_SINGLEINSTANCE, corePackage, usedSingleInstanceStubActivity);
break;
}

default:break;
}

mCachedStubActivity.put(className, stubActivity);
return stubActivity;
}

其余略····

StubActivityInfo 根据同的 launchMode 启动相应的“李鬼” Activity 至此,我们已经成功的 欺骗了 AMS ,启动了我们占坑的 Activity 但是只成功了一半,为什么这么说呢?因为欺骗过了 AMS,AMS 执行完成后,最终要启动的并非是占坑的 Activity ,所以我们还要能正确的启动目标Activity。

我们在 Hook Instrumentation 的同时一并 Hook 了 handleLaunchActivity,所以我们之间到 Instrumentation 的 newActivity 方法查看启动 Activity 的流程。

@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
// 是否能直接加载,如果能就是宿主中的 Activity
cl.loadClass(className);
} catch (ClassNotFoundException e) {
// 取得正确的 Activity
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
String targetClassName = PluginUtil.getTargetActivity(intent);
Log.i(TAG, String.format(“newActivity[%s : %s]”, className, targetClassName));
// 判断是否是 VirtualApk 启动的插件 Activity
if (targetClassName != null) {
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
// 启动插件 Activity
activity.setIntent(intent);
try {
// for 4.1+
ReflectUtil.setField(ContextThemeWrapper.class, activity, “mResources”, plugin.getResources());

总结

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的 点击我的GitHub免费获取。
还有免费的高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

跨平台开发:Flutter.png

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

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

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

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

…(img-sTcgrAql-1711016302578)]

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/861112
推荐阅读
相关标签
  

闽ICP备14008679号