征服安卓面试官路漫漫:从源码深扒一下四大组件和Context( 四 )


这不正是 装饰者模式 的体现?想给广播的 Context 对象加点限制 , 那就再来一个装饰类 ReceiverRestrictedContext, 它继承了 ContextWrapper , 重写部分方法以限制应用场景 。 通过增加和组合装饰类 , 而不是增加子类 , 来实现功能扩展 。
Application 和 Context四大组件说完了 , 别忘了 Application 也是 Context 的间接子类 。
Application 的创建时机得从应用进程的创建开始说起 。 Zygote 进程在接收到客户端请求创建应用进程的 socket 请求之后 , 会 fork 出子进程 , 并反射调用 ActivityThread 的静态 main() 方法 。 接着是 AMS 和客户端的一系列 Binder 调用以及 Handler 通信 , 最终主线程在收到 BIND_APPLICATION 消息之后回调 handleBindApplication() 方法 , 到这里就是我们需要的逻辑了:
private void handleBindApplication(AppBindData data){......// 获取 ContextImplfinal ContextImpl appContext = ContextImpl.createAppContext(this, data.info);......// 创建 Application 对象app = data.info.makeApplication(data.restrictedBackupMode, null);......// 调用 Application 的 onCreate() 方法mInstrumentation.callApplicationOnCreate(app);}你可能会疑惑怎么没有回调 attBaseContext() 方法 , 别急 , 看看 LoadedApk.makeApplication() 方法是如何创建 Application 的 。
public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {......// 创建 ContextImplContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);// 反射创建 Applicationapp = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);appContext.setOuterContext(app);}通过 Instrumentation.newApplication() 方法创建 Application。
public Application newApplication(ClassLoader cl, String className, Context context)throws InstantiationException, IllegalAccessException,ClassNotFoundException {// 反射创建Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);// 重点app.attach(context);return app;}重点就在 Application.attach() 方法 。
final void attach(Context context) {attachBaseContext(context);mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;}在这里调用了 attachBaseContext() 方法进行赋值 , 也验证了 attachBaseContext() 的确比 onCreate() 先调用 。
为什么 Application 的 Context 不可以创建 Dialog ?使用 Application 的 Context 创建 Dialog 并显示 , 会报如下错误:
Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?at android.view.ViewRootImpl.setView(ViewRootImpl.java:951)at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:387)at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:96)at android.app.Dialog.show(Dialog.java:344)注意错误信息 token null is not valid, 还记得文章前面说到 Activity 和 Context 的时候 , 有这么一段代码:
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags 注意其中的 mToken 参数不为 null, 是不是就说明了 Application 的 token 参数为空呢?
本来准备接着说说这个问题 , 但可能造成文章篇幅过长 , 所以 之后会单独来唠唠这个问题 。
如何进阶Android?有些东西你不仅要懂 , 而且要能够很好地表达出来 , 能够让面试官认可你的理解 , 例如Handler机制 , 这个是面试必问之题 。 有些晦涩的点 , 或许它只活在面试当中 , 实际工作当中你压根不会用到它 , 但是你要知道它是什么东西 。
对于程序员来说 , 要学习的知识内容、技术有太多太多 , 要想不被环境淘汰就只有不断提升自己 , 从来都是我们去适应环境 , 而不是环境来适应我们!
最后我在这里分享一下这段时间从朋友 , 大佬那里收集到的一些2019-2020BAT 面试真题解析 , 里面内容很多也很系统 , 包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题等等 , 可以很好地帮助我们深刻理解Android相关知识点的原理以及面试相关知识 。
1、确定好方向 , 梳理成长路线图
不用多说 , 相信大家都有一个共识:无论什么行业 , 最牛逼的人肯定是站在金字塔端的人 。 所以 , 想做一个牛逼的程序员 , 那么就要让自己站的更高 , 成为技术大牛并不是一朝一夕的事情 , 需要时间的沉淀和技术的积累 。
关于这一点 , 在我当时确立好Android方向时 , 就已经开始梳理自己的成长路线了 , 包括技术要怎么系统地去学习 , 都列得非常详细 。
征服安卓面试官路漫漫:从源码深扒一下四大组件和Context文章插图