2024年安卓最全腾讯零反射全动态Android插件框架Shadow解析,三年老Android经验面经

最后

有任何问题,欢迎广大网友一起来交流,分享高阶Android学习视频资料和面试资料包~

偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


众所周知,Android 9.0出现限制非公开SDK接口访问之后,可以说当时我们已知的所有插件框架实现都或多或少的出现了适配问题。大家的应对方式基本上都是一种对抗的思想,有的去破解限制,有的通过和Google沟通浅灰名单有效期暂时续命。

遇到了这个问题,我们没有选择和这个策略进行对抗,我们非常理解Google为什么限制使用非公开SDK接口。所以我们重新Review了插件框架的本质原理和设计缺陷,进而设计了全新的插件框架Shadow。Shadow没有使用任何非公开SDK接口,实现了和原本在用的使用了大量非公开SDK接口的实现一样的功能。

在Shadow的Sample中,可以添加如下所示的代码来开启严格检查模式运行,而其他插件框架并不能做到。

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
builder.penaltyDeath();
builder.detectNonSdkApiUsage();
StrictMode.setVmPolicy(builder.build());

比如,我们看到的一款也宣传未使用非公开SDK接口,支持Android 9.0的插件框架,在它的Sample中开启严格模式运行后,出现了如下Crash信息:

W/.xxx.sampl: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
W/System.err: StrictMode VmPolicy violation with POLICY_DEATH; shutting down.

可见,即使它的实现代码中没有出现任何非公开SDK的引用,实际上它依赖的第三方组件内部也使用了非公开SDK接口。

插件框架全动态化

所谓全动态,指的就是除了插件代码之外,插件框架本身的所有逻辑代码也都是动态的。并且,Shadow框架实际上也做到了这一点,即插件框架的代码我们是和插件打包在一起发布的。

全动态化插件框架有多重要呢?其实它比无Hack、零反射实现还要重要!因为有了这个特性之后,就算是我们用了Hack的方案,需要兼容各种手机厂商的系统。我们也不需要等宿主App更新才能解决问题。

实际上,Shadow的这个特性是更早实现的。我们早在2015年就开始大量使用插件技术了。在2016年就实现了这个特性。凭借这个特性不断的动态发布插件框架的代码,去适配各种兼容性问题。在今年更是应用这个特性,在完全不跟宿主版本的前提下,将原本的具有上百个反射Hack调用的旧实现更新为了Shadow无Hack实现。新的Shadow自然也具备这个特性。

在实际使用过程中,我们的宿主对于业务接入在增量上有极其苛刻的要求。Shadow接入时只使用了15.1KB,160个方法。而我们已知的其他插件框架对宿主的增量一般在110KB,900个方法到370KB,2300个方法之间。

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

实现原理

以前的插件框架总是想用一些Hack手段去修改系统行为,找到系统的漏洞达到目的。Shadow的原则是不去跟系统对抗。既然只是限制非公开SDK接口访问,而没有限制动态加载代码。那么肯定有办法在不使用非公开SDK接口的前提下实现原来的目的。因为我们插件技术的目的本质上来说还是动态加载代码。

那么一个重要的原则就是,如果一个组件需要安装才能使用,那么就别在没安装的情况下把它交给系统。我们已知的插件框架中,做的最好的也不符合这个原则,所以尽管它的Hook点少,但就是由于它将没有安装的Activity交给系统了,所以后面就不得不做一些Hack的事修补。

所以套一个壳子的方案就非常好。这种思路其他框架很早就有了,但是它们一直想把一个插件Activity套在一个宿主Activity之中,然后想办法实现一个转调关系。如果插件Activity是一个真的Activity,那这个插件就可以正常编译安装运行,对开发插件或者直接上架插件App非常有利。但是由于它是个系统的Activity子类,它就有很多方法不能直接调用,甚至还可能需要避免它的super方法被调用。如果插件Activity不是一个真的Activity,只是一个跟Activity有差不多方法的普通类,这件事就简单多了,只需要让壳子Activity持有它,转调它就行了。但这种插件的代码正常编译成独立App安装运行会比较麻烦,代码中可能会出现很多插件相关的if-else,也不好。

Shadow做了一个非常简单事,通过运用AOP思想,利用字节码编辑工具,在编译期把插件中的所有Activity的父类都改成一个普通类,然后让壳子持有这个普通类型的父类去转调它就不用Hack任何系统实现了。虽然说是非常简单的事,实际上这样修改后还带来一些额外的问题需要解决,比如getActivity()方法返回的也不是Activity了。不过Shadow的实现中都解决了这些问题。

Shadow框架的原理示意图如下:

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

集成Shadow

环境准备

第一次clone Shadow的代码到本地后,建议先在命令行编译一次。

  • 在编译前,必须设置ANDROID_HOME环境变量。
  • 在编译时,必须使用gradlew脚本,以保证采用了项目配置的Gradle版本。

在命令行测试编译时可以执行如下编译任务:

./gradlew build

如果没有出错,再尝试用Android Studio打开工程。

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,也可以分享给身边好友一起学习。

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

**

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android 项目中使用 QMUI Android,可以按照以下步骤进行: 1. 将 QMUI Android 引入项目中,可以使用 Gradle,将以下代码添加到 `build.gradle` 文件中: ```groovy dependencies { implementation 'com.qmuiteam:qmui:2.1.0' } ``` 2. 在 Application 类中初始化 QMUI,可以在 `onCreate()` 方法中添加以下代码: ```java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); QMUI.init(this); } } ``` 3. 在布局文件中使用 QMUI 的控件,例如: ```xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:qmui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.qmuiteam.qmui.widget.QMUITopBarLayout android:id="@+id/topbar" qmui:layout_constraintTop_toTopOf="parent" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"/> <com.qmuiteam.qmui.widget.QMUIRoundButton android:id="@+id/button" android:text="Button" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> ``` 在这个例子中,我们使用了 `QMUITopBarLayout` 和 `QMUIRoundButton`,它们都是 QMUI 的控件,可以通过 `xmlns:qmui="http://schemas.android.com/apk/res-auto"` 引入 QMUI 的命名空间。 在 Activity 类中,可以通过以下方式来获取控件的实例: ```java public class MyActivity extends AppCompatActivity { private QMUITopBarLayout mTopBarLayout; private QMUIRoundButton mButton; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); mTopBarLayout = findViewById(R.id.topbar); mButton = findViewById(R.id.button); // 设置 TopBar 的标题 mTopBarLayout.setTitle("My Activity"); } } ``` 以上就是使用 QMUI Android 的基本步骤,更多的 QMUI 控件和用法可以参考官方文档。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值