private PluginActivity pluginActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
String pluginActivityName = getIntent().getString(“pluginActivityName”, “”);
pluginActivity = PluginLoader.loadActivity(pluginActivityName, this);
if (pluginActivity == null) {
super.onCreate(savedInstanceState);
return;
}
pluginActivity.onCreate();
}
@Override
protected void onResume() {
if (pluginActivity == null) {
super.onResume();
return;
}
pluginActivity.onResume();
}
@Override
protected void onPause() {
if (pluginActivity == null) {
super.onPause();
return;
}
pluginActivity.onPause();
}
// …
}
public class PluginActivity {
private ContainerActivity containerActivity;
public PluginActivity(ContainerActivity containerActivity) {
this.containerActivity = containerActivity;
}
@Override
public T findViewById(int id) {
return containerActivity.findViewById(id);
}
// …
}
// 插件 Apk
中真正写的组件
public class TestActivity extends PluginActivity {
// …
}
Emm,是不是感觉有点看懂了,虽然真正搞的时候还有很多小坑,但大概原理就是这么简单,启动插件组件需要依赖容器,容器负责加载插件组件并且完成双向转发,转发来自系统的生命周期回调至插件组件,同时转发来自插件组件的系统调用至系统。
最后要说的是资源注入,其实这一点相当重要,Android
应用的开发其实崇尚的是逻辑与资源分离的理念,所有资源(layout
、values
等)都会被打包到 Apk
中,然后生成一个对应的 R
类,其中包含对所有资源的引用 id
。
资源的注入并不容易,好在 Android
系统给我们留了一条后路,最重要的是这两个接口:
-
PackageManager#getPackageArchiveInfo
:根据Apk
路径解析一个未安装的Apk
的PackageInfo
-
PackageManager#getResourcesForApplication
:根据ApplicationInfo
创建一个Resources
实例
我们要做的就是在上面 ContainerActivity#onCreate
中加载插件 Apk
的时候,用这两个方法创建出来一份插件资源实例。具体来说就是先用 PackageManager#getPackageArchiveInfo
拿到插件 Apk
的 PackageInfo
,有了 PacakgeInfo
之后我们就可以自己组装一份 ApplicationInfo
,然后通过 PackageManager#getResourcesForApplication
来创建资源实例,大概代码像这样:
PackageManager packageManager = getPackageManager();
PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(
pluginApkPath,
PackageManager.GET_ACTIVITIES
| PackageManager.GET_META_DATA
| PackageManager.GET_SERVICES
| PackageManager.GET_PROVIDERS
| PackageManager.GET_SIGNATURES
);
packageArchiveInfo.applicationInfo.sourceDir = pluginApkPath;
packageArchiveInfo.applicationInfo.publicSourceDir = pluginApkPath;
Resources injectResources = null;
try {
injectResources = packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
// …
}
拿到资源实例后,我们需要将宿主的资源和插件资源 Merge
一下,编写一个新的 Resources
类,用这样的方式完成自动代理:
public class PluginResources extends Resources {
private Resources hostResources;
private Resources injectResources;
public PluginResources(Resources hostResources, Resources injectResources) {
super(injectResources.getAssets(), injectResources.getDisplayMetrics(), injectResources.getConfiguration());
this.hostResources = hostResources;
this.injectResources = injectResources;
}
@Override
public String getString(int id, Object… formatArgs) throws NotFoundException {
try {
return injectResources.getString(id, formatArgs);
} catch (NotFoundException e) {
return hostResources.getString(id, formatArgs);
}
}
// …
}
然后我们在 ContainerActivity
完成插件组件加载后,创建一份 Merge
资源,再复写 ContainerActivity#getResources
,将获取到的资源替换掉:
public class ContainerActivity extends Activity {
private Resources pluginResources;
@Override
protected void onCreate(Bundle savedInstanceState) {
// …
pluginResources = new PluginResources(super.getResources(), PluginLoader.getResources(pluginApkPath));
// …
}
@Override
public Resources getResources() {
if (pluginActivity == null) {
return super.getResources();
}
return pluginResources;
}
}
这样就完成了资源的注入。
=================================================================================
上面其实说到了,我们被迫改变了插件组件的编写方式:
class TestActivity extends Activity {}
->
class TestActivity extends PluginActivity {}
有没有什么办法能让插件组件的编写与原来没有任何差别呢?
Shadow
的做法是字节码替换插件,我认为这是一个非常棒的想法,简单来说,Android
提供了一些 Gradle
插件开发套件,其中有一项功能叫 Transform Api
,它可以介入项目的构建过程,在字节码生成后、dex
文件生成钱,对代码进行某些变换,具体怎么做的不说了,可以自己看文档。
实现的功能嘛,就是用户配置 Gradle
插件后,正常开发,依然编写:
class TestActivity extends Activity {}
然后完成编译后,最后的字节码中,显示的却是:
学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
58fT-1714712909959)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!